├── Example └── MasterScriptTemplate.xlsx ├── LICENSE ├── README.md ├── build ├── MasterScript.zip └── archive │ ├── MasterScript_1_0_0.zip │ ├── MasterScript_1_0_1.zip │ ├── MasterScript_1_0_2.zip │ ├── MasterScript_1_1_0.zip │ ├── MasterScript_1_1_1.zip │ └── MasterScript_1_1_2.zip └── src ├── MasterScript.css ├── MasterScript.js ├── MasterScript.qext ├── definition.js ├── dialog-template.ng.html ├── template.ng.html └── wbfolder.wbl /Example/MasterScriptTemplate.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/Example/MasterScriptTemplate.xlsx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Steven Pressland 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MasterScript 2 | 3 | MasterScript is a utility to easily import Qlik Sense master items from an external table via the Qlik application data model. For those situation where a bulk load of master items is necessary to load a business data dictionary to your application, then MasterScript is the tool for you! 4 | 5 | ## How to get started? 6 | 1. Install the extension via QMC 7 | 1. Load your metadata in the format outlined below to your application data model 8 | 1. Add the MasterScript extension to your application sheet 9 | 1. Open MasterScript 10 | 1. Click "Import Master Items" 11 | 1. Check the status 12 | 1. Delete the MasterScript extension from your application sheet 13 | 1. Build your application 14 | 15 | ## Loading metadata 16 | The metadata required by the extension must be in a pre-defined format, the easiest way to load this is with the sample spreadsheet which has all the required column headers in a single table. It is also possible to use the data transformation capabilities in Qlik Sense to convert to this format once loaded in a different structure. 17 | 18 | The format is designed for ease of manual completion in a spreadsheet, hence the flat nature of the data structure. 19 | 20 | The fields have a "_" prefix so this can be combined with the ___set HidePrefix = '\_'___ option 21 | 22 | ### The data format 23 | * _MasterItemID 24 | * A unique ID for the master item, this will be used to apply updates, so the name of a master item can be changed 25 | * _MasterItemType 26 | * A value of either "Measure" or "Dimension" 27 | * _MasterItemName 28 | * The name for the master item, this will be displayed in the master items panel 29 | * _MasterItemDescription 30 | * The contents for the Description field seen when creating a master item 31 | * _MasterItemColor 32 | * The HEX code to define the master items color. Must be in the format of "#000000" 33 | * _MasterItemTags 34 | * A list of tags to be applied to the master item. tags should be listed using the ";" as a delimiter 35 | * _MasterItemLabel 36 | * The label expression to be displayed on chart axis when using the master item (Qlik Sense September 2017 Onwards) 37 | * _MasterItemExpression1 38 | * The Field name or expression for the measure or dimension 39 | * _MasterItemExpression(2...n) 40 | * Additional fields, when required for a Drill-down Dimension should be added as extra columns using a sequential number in the field title 41 | 42 | ## Once Imported 43 | When you have imported, the extension can be deleted, you can also remove the data table from your script too. If you need to update the master items, just load the extension again (if you have deleted it from the sheet) and update from the source data. 44 | 45 | The master items will be updated based upon the _MasterItemID column. 46 | 47 | There are no dependencies in your app for the extension, or when you share the application for the recipient server to have MasterScript installed. This is just used to importing master items. 48 | -------------------------------------------------------------------------------- /build/MasterScript.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/MasterScript.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_0_0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_0_0.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_0_1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_0_1.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_0_2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_0_2.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_1_0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_1_0.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_1_1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_1_1.zip -------------------------------------------------------------------------------- /build/archive/MasterScript_1_1_2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnalyticsEarth/MasterScript/2eec061dc159d5b1eee885b98ad1f9dca1331a9f/build/archive/MasterScript_1_1_2.zip -------------------------------------------------------------------------------- /src/MasterScript.css: -------------------------------------------------------------------------------- 1 | /* CSS */ 2 | 3 | .qv-object-aeRWizard > .qv-object-nav{ 4 | display: none; 5 | } 6 | 7 | .extensionFrame { 8 | height:100%; 9 | width:100%; 10 | display:table; 11 | min-width:207px; 12 | min-height:118px; 13 | } 14 | 15 | .inputPlaceholderSheet { 16 | text-align: center; 17 | font-weight: bold; 18 | font-size: 1.1em; 19 | padding: 5px; 20 | height:100%; 21 | display:table-cell; 22 | vertical-align:middle; 23 | text-align:center; 24 | } 25 | 26 | .placeholderIconSheet { 27 | font-size: 20px; 28 | padding-bottom: 5px; 29 | } 30 | 31 | .placeholderButtonSheet { 32 | margin-top: 5px; 33 | } 34 | 35 | .wizarddialog { 36 | min-width: 900px; 37 | max-width: 900px; 38 | min-height: 90%; 39 | max-height: 90%; 40 | } 41 | 42 | .warningLeft { 43 | float: left; 44 | clear: none; 45 | font-weight: bold; 46 | font-size: 1.1em; 47 | padding: 5px; 48 | } 49 | 50 | 51 | .column1 { 52 | width:25%; 53 | float:left; 54 | clear:none; 55 | } 56 | 57 | .column2 { 58 | width:10%; 59 | float:left; 60 | clear:none; 61 | } 62 | 63 | .column3 { 64 | width:17%; 65 | float:left; 66 | clear:none; 67 | } 68 | 69 | .column4 { 70 | width:10%; 71 | float:left; 72 | clear:none; 73 | text-align: center; 74 | } 75 | 76 | .column5 { 77 | width:10%; 78 | float:left; 79 | clear:none; 80 | font-weight: bold; 81 | text-align: center; 82 | } 83 | 84 | .topButton { 85 | float:right; 86 | margin-left:10px; 87 | } 88 | 89 | .topTitle { 90 | float: left; 91 | } 92 | 93 | .colorDisc { 94 | width:16px; 95 | height: 16px; 96 | border-radius: 8px; 97 | margin:0 auto; 98 | border:1px solid #CCCCCC; 99 | } 100 | 101 | .fieldoval { 102 | background-color: #E6E6E6; 103 | border-radius: 10px; 104 | padding-left:9px; 105 | padding-right: 9px; 106 | padding-top: 1px; 107 | padding-bottom: 1px; 108 | border: 1px solid #CCCCCC; 109 | float:left; 110 | clear:both; 111 | margin: 1px 0; 112 | } 113 | 114 | .rowHeight { 115 | padding-top: 10px; 116 | padding-bottom: 10px; 117 | } 118 | 119 | .hidden { 120 | display: none; 121 | } 122 | 123 | .errorList { 124 | clear: both; 125 | width: 100%; 126 | margin-top: 40px; 127 | box-sizing: border-box; 128 | } 129 | 130 | .errorList li { 131 | font-weight: bold; 132 | list-style-type: none; 133 | border-radius: 5px; 134 | margin-bottom: 5px; 135 | background-color: #f05555; 136 | padding-left:10px; 137 | padding-right: 10px; 138 | color: #ffffff; 139 | } 140 | -------------------------------------------------------------------------------- /src/MasterScript.js: -------------------------------------------------------------------------------- 1 | define( [ "qlik", 2 | 'text!./template.ng.html', 3 | './definition', 4 | 'text!./dialog-template.ng.html', 5 | 'css!./MasterScript.css', 6 | 'util' 7 | ], 8 | function ( qlik, template, definition, dialogTemplate, cssStyle, Util) { 9 | 'use strict'; 10 | return { 11 | support : { 12 | snapshot: false, 13 | export: false, 14 | exportData : false 15 | }, 16 | template: template, 17 | definition: definition, 18 | paint: function ($element,layout){ 19 | 20 | layout.navmode = qlik.navigation.getMode(); 21 | //console.log($element); 22 | 23 | if(layout.navmode == 'analysis'){ 24 | $("#launchButton").removeClass("hidden").addClass("hidden"); 25 | }else{ 26 | $("#launchButton").removeClass("hidden"); 27 | } 28 | 29 | }, 30 | controller: ['$scope','luiDialog', function ( $scope, luiDialog) { 31 | 32 | 33 | //console.log($scope); 34 | 35 | $scope.processButton = function($event){ 36 | 37 | $scope.openWizard(); 38 | }; 39 | 40 | 41 | /* This function opens the dialog window when the openWizard() function 42 | is called */ 43 | $scope.openWizard = function(){ 44 | luiDialog.show({ 45 | template: dialogTemplate, 46 | input: { 47 | selectedKey: '', 48 | wizardName: '', 49 | showKey: false, 50 | wizardList: $scope.wizardList, 51 | appModel: $scope.component.model.app, 52 | layout: $scope.layout, 53 | isLoading: false, 54 | enableVizBuild: true, 55 | previewEnabled: false, 56 | buttonState: 0, 57 | buttonTitle: 'Preview Master Items', 58 | buttonIcon: 'view', 59 | warningMessage: '', 60 | masterScriptListInternal: [], 61 | tableReady: false 62 | }, 63 | controller: ['$scope', function( $scope ) { 64 | //console.log($scope); 65 | 66 | /* Get current Qlik App and field list */ 67 | var app = qlik.currApp(this); 68 | 69 | //app.getList("FieldList", function(reply){ 70 | app.createGenericObject({ 71 | qFieldListDef:{ 72 | qShowHidden:true 73 | }}, function(reply){ 74 | //console.log("Field List"); 75 | //console.log(reply); 76 | $scope.input.fieldList = reply.qFieldList.qItems.filter(a => a.qName.substring(0,11) == '_MasterItem').map(a => a.qName); 77 | //console.log($scope.input.fieldList); 78 | 79 | if(typeof $scope.input.fieldList != 'undefined'){ 80 | var rowCount = Math.floor(10000 / ($scope.input.fieldList.length + 1)); 81 | //console.log(rowCount); 82 | $scope.input.masterScriptList = app.createTable( 83 | $scope.input.fieldList, 84 | [{qDef:{qLabel:"_KeyCount",qDef:"count(TOTAL <_MasterItemID> _MasterItemID)"}}], 85 | {rows:rowCount} 86 | ); 87 | 88 | var listener = function() { 89 | 90 | $scope.processItems(); 91 | }; 92 | $scope.input.masterScriptList.OnData.bind( listener ); //bind the listener 93 | $scope.input.tableReady = true; 94 | } 95 | 96 | }); 97 | 98 | 99 | /* Set the default tab and create the function which will allow for 100 | the tab to be changed in code */ 101 | $scope.tabs = 'tab1'; 102 | $scope.make_tab_active = function(tabid) { 103 | $scope.tabs = 'tab'+tabid; 104 | //console.log($scope.tabs); 105 | } 106 | 107 | //$scope.make_tab_active(1); 108 | 109 | 110 | $scope.updateDimList = function(){ 111 | var list = { 112 | qInfo: { 113 | qId: "", 114 | qType: "DimensionList" 115 | }, 116 | qDimensionListDef: { 117 | qType: 'dimension', 118 | qData: {grouping: '/qDim/qGrouping'} 119 | } 120 | }; 121 | 122 | $scope.input.appModel.createSessionObject(list).then((data) => { 123 | $scope.input.dimListObj = data; 124 | $scope.refreshDimList(data); 125 | }); 126 | }; 127 | 128 | $scope.refreshDimList = function(dimListObj){ 129 | dimListObj.getLayout().then((dataLayout) => { 130 | //console.log("Refresh Dim List"); 131 | //console.log(dataLayout); 132 | $scope.input.dimList = dataLayout.qDimensionList.qItems; 133 | $scope.processItems(); 134 | 135 | }); 136 | }; 137 | 138 | $scope.updateMesList = function(){ 139 | var list = { 140 | qInfo: { 141 | qId: "", 142 | qType: "MeasureList" 143 | }, 144 | qMeasureListDef: { 145 | qType: 'measure', 146 | qData: {grouping: '/qMeasure/qGrouping'} 147 | } 148 | }; 149 | 150 | $scope.input.appModel.createSessionObject(list).then((data) => { 151 | $scope.input.mesListObj = data; 152 | $scope.refreshMesList(data); 153 | }); 154 | }; 155 | 156 | $scope.refreshMesList = function(mesListObj){ 157 | mesListObj.getLayout().then((dataLayout) => { 158 | //console.log("Refresh Dim List"); 159 | //console.log(dataLayout); 160 | $scope.input.mesList = dataLayout.qMeasureList.qItems; 161 | $scope.processItems(); 162 | }); 163 | }; 164 | 165 | $scope.updateDimList(); 166 | $scope.updateMesList(); 167 | 168 | //console.log("Session Object"); 169 | //console.log($scope.input.DimListObj); 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | $scope.process = function(){ 179 | //console.log("Process Click"); 180 | 181 | $scope.createItems(true); 182 | 183 | }; 184 | 185 | $scope.createItems = function(shouldCreate){ 186 | var p = []; 187 | $scope.input.masterScriptListInternal.forEach(function(row) { 188 | //console.log(row); 189 | 190 | if(row.rowType == "Dimension"){ 191 | var a = $scope.createDimension(row, shouldCreate); 192 | p.push(a); 193 | } 194 | if(row.rowType == "Measure"){ 195 | var a = $scope.createMeasure(row, shouldCreate); 196 | //console.log(a); 197 | p.push(a); 198 | } 199 | }); 200 | 201 | Promise.all(p).then(values => { 202 | $scope.refreshDimList($scope.input.dimListObj); 203 | $scope.refreshMesList($scope.input.mesListObj); 204 | 205 | }); 206 | 207 | }; 208 | 209 | $scope.processItems = function(){ 210 | if($scope.input.tableReady && $scope.input.masterScriptList.colCount > 1){ 211 | var table = $scope.input.masterScriptList; 212 | //console.log(table); 213 | var iDCol = table.getColByName('_MasterItemID'); 214 | var typeCol = table.getColByName('_MasterItemType'); 215 | var nameCol = table.getColByName('_MasterItemName'); 216 | var descCol = table.getColByName('_MasterItemDescription'); 217 | var colorCol = table.getColByName('_MasterItemColor'); 218 | var tagCol = table.getColByName('_MasterItemTags'); 219 | var countCol = table.getColByName('_KeyCount'); 220 | var labelExpCol = table.getColByName('_MasterItemLabel'); 221 | //console.log("Accumulate Column: " + accumulateCol); 222 | 223 | 224 | 225 | $scope.input.masterScriptList.rows.forEach(function(row, rowNum) { 226 | //console.log(row); 227 | var error = false; 228 | var errors = []; 229 | var fieldsList = []; 230 | 231 | if(row.cells[countCol].qNum > 1){ 232 | error = true; 233 | errors.push("Duplicate _MasterItemID Found"); 234 | } 235 | 236 | if(row.cells[countCol].qNum == 0){ 237 | error = true; 238 | errors.push("Missing _MasterItemID Value"); 239 | } 240 | 241 | for(var i = 1; i <= 10; i++) 242 | { 243 | var expCol = table.getColByName('_MasterItemExpression'+i); 244 | var cell = row.cells[expCol]; 245 | if(cell && cell.qElemNumber >= 0){ 246 | fieldsList.push(cell.qText); 247 | } 248 | } 249 | 250 | var idValue; 251 | if(typeof row.cells[iDCol] != 'undefined'){ 252 | idValue = row.cells[iDCol].qText; 253 | } 254 | 255 | if(idValue == '-'){ 256 | //idValue = row.cells[nameCol].qText; 257 | error = true; 258 | errors.push('No _MasterItemID - Add MasterItemID to source data'); 259 | } 260 | 261 | var descValue; 262 | if(typeof row.cells[descCol] != 'undefined'){ 263 | descValue = row.cells[descCol].qText; 264 | } 265 | if(descValue == '-'){ 266 | descValue = ''; 267 | } 268 | 269 | 270 | var tagsList = []; 271 | if(typeof row.cells[tagCol] != 'undefined'){ 272 | tagsList = row.cells[tagCol].qText.split(";"); 273 | tagsList = tagsList.filter(a => a !== '-'); 274 | tagsList.push(idValue); 275 | } 276 | 277 | var rowDisplay; 278 | if(typeof row.cells[typeCol] != 'undefined'){ 279 | rowDisplay = row.cells[typeCol].qText; 280 | } 281 | 282 | var labelExp = ''; 283 | if(typeof row.cells[labelExpCol] != 'undefined'){ 284 | labelExp = row.cells[labelExpCol].qText; 285 | if(labelExp == '-'){ 286 | labelExp = ''; 287 | } 288 | } 289 | 290 | if(!(rowDisplay == 'Dimension' || rowDisplay == 'Measure')){ 291 | error = true; 292 | errors.push("_MasterItemType does not match either 'Dimension' or 'Measure'") 293 | } 294 | 295 | 296 | if(fieldsList.length > 1 && rowDisplay == "Dimension"){ 297 | rowDisplay = "Drill-down Dimension" 298 | } 299 | var prevProcessed = 'Pending'; 300 | if(typeof $scope.input.masterScriptListInternal[rowNum] != 'undefined'){ 301 | prevProcessed = $scope.input.masterScriptListInternal[rowNum].processed; 302 | } 303 | 304 | var itemData = { 305 | rowNumber:rowNum, 306 | rowType: row.cells[typeCol].qText, 307 | rowDisplayType: rowDisplay, 308 | displayName: row.cells[nameCol].qText, 309 | description: descValue, 310 | color: row.cells[colorCol].qText, 311 | labelExpression: labelExp, 312 | fields: fieldsList, 313 | tags: tagsList, 314 | msId: idValue, 315 | status: "Pending", 316 | processed: prevProcessed, 317 | error: error, 318 | errors: errors 319 | }; 320 | 321 | if(itemData.error){ 322 | itemData.status = "Error"; 323 | itemData.processed = ""; 324 | } 325 | 326 | $scope.input.masterScriptListInternal[rowNum] = itemData; 327 | $scope.checkDim(itemData); 328 | $scope.checkMes(itemData); 329 | 330 | }); 331 | } 332 | 333 | 334 | //console.log("Complete"); 335 | //console.log($scope.input.masterScriptListInternal); 336 | 337 | }; 338 | 339 | $scope.checkDim = function(t){ 340 | if(!t.error){ 341 | var check = false; 342 | $scope.input.dimList.forEach(function(dim){ 343 | //console.log(dim); 344 | 345 | if(t.msId == dim.qMeta.masterScriptId){ 346 | //console.log("Dim Already Exists: " + t.msId + " " + dim.qMeta.masterScriptId); 347 | t.qId = dim.qInfo.qId; 348 | check = true; 349 | //console.log(t); 350 | } 351 | }); 352 | if(t.rowType == "Dimension"){ 353 | if(check){ 354 | t.status = "Exists"; 355 | }else{ 356 | t.status = "Not Created"; 357 | } 358 | } 359 | } 360 | return check; 361 | }; 362 | 363 | $scope.checkMes = function(t){ 364 | if(!t.error){ 365 | var check = false; 366 | $scope.input.mesList.forEach(function(mes){ 367 | //console.log(dim); 368 | 369 | if(t.msId == mes.qMeta.masterScriptId){ 370 | //console.log("Measure Already Exists: " + t.msId + " " + mes.qMeta.masterScriptId); 371 | t.qId = mes.qInfo.qId; 372 | check = true; 373 | } 374 | }); 375 | if(t.rowType == "Measure"){ 376 | if(check){ 377 | t.status = "Exists"; 378 | }else{ 379 | t.status = "Not Created"; 380 | } 381 | } 382 | 383 | } 384 | return check; 385 | }; 386 | 387 | 388 | /* Create Dimension */ 389 | $scope.createDimension = function(t, shouldCreate){ 390 | var group = "N"; 391 | if(t.fields.length > 1) group = "H"; 392 | 393 | var colorBlock = {} 394 | 395 | if(t.color != "-"){ 396 | colorBlock = { 397 | baseColor: { 398 | color: t.color, 399 | index: -1 400 | } 401 | }; 402 | } 403 | var dimJSON = 404 | { 405 | qInfo: { 406 | qType: "dimension" 407 | }, 408 | qDim: { 409 | qGrouping: group, 410 | qFieldDefs: t.fields, 411 | qFieldLabels: t.fields, 412 | title:t.displayName, 413 | qLabelExpression:t.labelExpression, 414 | coloring: colorBlock 415 | }, 416 | qMetaDef: { 417 | title:t.displayName, 418 | description:t.description, 419 | tags:t.tags, 420 | masterScriptId:t.msId 421 | } 422 | }; 423 | 424 | if(!t.error){ 425 | if($scope.checkDim(t)){ 426 | if(shouldCreate){ 427 | return $scope.input.appModel.getDimension(t.qId).then((data) => { 428 | data.setProperties(dimJSON); 429 | t.processed = "Updated"; 430 | }); 431 | } 432 | }else{ 433 | if(shouldCreate){ 434 | return $scope.input.appModel.createDimension(dimJSON).then((data) => { 435 | t.processed = "Created"; 436 | }); 437 | } 438 | } 439 | } 440 | }; 441 | 442 | /* Create Measure */ 443 | $scope.createMeasure = function(t, shouldCreate){ 444 | var colorBlock = {} 445 | 446 | if(t.color != "-"){ 447 | colorBlock = { 448 | baseColor: { 449 | color: t.color, 450 | index: -1 451 | } 452 | }; 453 | } 454 | 455 | var mesJSON = 456 | { 457 | qInfo: { 458 | qType: "measure" 459 | }, 460 | qMeasure: { 461 | qLabel:t.displayName, 462 | qGrouping: "N", 463 | qDef: t.fields[0], 464 | qExpressions:[], 465 | qActiveExpression: 0, 466 | qLabelExpression:t.labelExpression, 467 | coloring:colorBlock 468 | }, 469 | qMetaDef: { 470 | title:t.displayName, 471 | description:t.description, 472 | tags:t.tags, 473 | masterScriptId:t.msId 474 | } 475 | }; 476 | 477 | if(!t.error){ 478 | if($scope.checkMes(t)){ 479 | //console.log("Updating: "+ t.qId); 480 | return $scope.input.appModel.getMeasure(t.qId).then((data) => { 481 | //console.log("Updating Measure"); 482 | //console.log(data); 483 | data.setProperties(mesJSON); 484 | t.processed = "Updated"; 485 | }); 486 | }else{ 487 | if(shouldCreate){ 488 | return $scope.input.appModel.createMeasure(mesJSON).then((data) => { 489 | t.processed = "Created"; 490 | }); 491 | } 492 | } 493 | } 494 | }; 495 | }] 496 | }); 497 | } 498 | }] 499 | }; 500 | }); 501 | -------------------------------------------------------------------------------- /src/MasterScript.qext: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MasterScript", 3 | "description": "Create Master Items from a table in your data model", 4 | "type": "visualization", 5 | "version": "1.1.3", 6 | "icon": "library", 7 | "author": "Steven Pressland", 8 | "homepage": "https://github.com/AnalyticsEarth/MasterScript", 9 | "keywords": "qlik-sense, visualization, Master Items", 10 | "license": "", 11 | "repository": "", 12 | "dependencies": { 13 | "qlik-sense": ">=3.0.x" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/definition.js: -------------------------------------------------------------------------------- 1 | define( [], function () { 2 | 'use strict'; 3 | 4 | // Appearance section 5 | var appearanceSection = { 6 | uses: "settings", 7 | items: { 8 | general: { 9 | items:{ 10 | showTitles:{ 11 | defaultValue: false 12 | } 13 | } 14 | } 15 | } 16 | }; 17 | 18 | 19 | 20 | // ***************************************************************************** 21 | // Main properties panel definition 22 | // Only what is defined here is returned from properties.js 23 | // ***************************************************************************** 24 | return { 25 | type: "items", 26 | component: "accordion", 27 | items: { 28 | appearance: appearanceSection 29 | } 30 | }; 31 | }); 32 | -------------------------------------------------------------------------------- /src/dialog-template.ng.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MasterScript 5 | 6 | 7 | 38 | 39 | 40 |
The MasterScript table will always overwrite any manual changes to master items
41 | 42 | 43 |
44 |
45 | -------------------------------------------------------------------------------- /src/template.ng.html: -------------------------------------------------------------------------------- 1 |
2 |

MasterScript
3 | 4 |
5 |
6 | 7 | -------------------------------------------------------------------------------- /src/wbfolder.wbl: -------------------------------------------------------------------------------- 1 | aeRWizard.qext; 2 | aeRWizard.js; 3 | template.ng.html; 4 | dialog-template.ng.html; 5 | definition.js; 6 | wizardList.js; 7 | aeRWizardStyle.css; 8 | clusterscatter.json; 9 | kpi.json --------------------------------------------------------------------------------