Skip to content

Instantly share code, notes, and snippets.

@karantakalkar
Last active March 13, 2020 07:27
Show Gist options
  • Save karantakalkar/c7cee52de34698b4f45abe9f51ae42c6 to your computer and use it in GitHub Desktop.
Save karantakalkar/c7cee52de34698b4f45abe9f51ae42c6 to your computer and use it in GitHub Desktop.
Comparison b/w run reports component of community and web app.
(function (module) {
mifosX.controllers = _.extend(module, {
RunReportsController: function (scope, routeParams, resourceFactory, location, dateFilter, http, API_VERSION, $rootScope, $sce) {
//displays report parameter options div on startup
scope.isCollapsed = false;
//hides the run report output div on startup
scope.hideTable = true;
scope.hidePentahoReport = true;
scope.hideChart = true;
// toggle chart type
scope.piechart = false;
scope.barchart = false;
// Form data.
scope.formData = {};
// reports params with input type select dropdown.
scope.reportParams = new Array();
// reports params with input type date.
scope.reportDateParams = new Array();
// reports params with input type text.
scope.reportTextParams = new Array();
// all requried report parameters.
scope.reqFields = new Array();
// data fetched based on user's form input.
scope.reportData = {};
scope.reportData.columnHeaders = [];
scope.reportData.data = [];
// pentaho trusted resource url used in iframe.
scope.baseURL = "";
// ngcsv data for report type table.
scope.csvData = [];
scope.row = [];
// Report specifications fetched from route params.
scope.reportName = routeParams.name;
scope.reportType = routeParams.type;
scope.reportId = routeParams.reportId;
// Pentaho specific parameter names used in constructing API call url.
scope.pentahoReportParameters = [];
// sets default chart type for chart output.
scope.type = "pie";
// CSS highlights
scope.highlight = function (id) {
var i = document.getElementById(id);
if (i.className == 'selected-row') {
i.className = 'text-pointer';
} else {
i.className = 'selected-row';
}
};
// sets default file type for pentaho output.
if (scope.reportType == 'Pentaho') {
scope.formData.outputType = 'HTML';
};
// Fetches all report parameters (inc. child params) from api.
resourceFactory.runReportsResource.getReport({reportSource: 'FullParameterList', parameterType: true, R_reportListing: "'" + routeParams.name + "'"}, function (data) {
// labels the response objects as report parameter.
// ref : reports service and report parameter model.
for (var i in data.data) {
var temp = {
name: data.data[i].row[0],
variable: data.data[i].row[1],
label: data.data[i].row[2],
displayType: data.data[i].row[3],
formatType: data.data[i].row[4],
defaultVal: data.data[i].row[5],
selectOne: data.data[i].row[6],
selectAll: data.data[i].row[7],
parentParameterName: data.data[i].row[8],// null for non-child params.
inputName: "R_" + data.data[i].row[1] //model name, used for api call url building, post user form submission.
};
// adds labelled param to list of required fields.
scope.reqFields.push(temp);
// classfies non-child report parameters based on input type into 3 different arrays
// utilized in view to render different input fields.
// initializeParams() only for non child params
if (temp.displayType == 'select' && temp.parentParameterName == null) {
intializeParams(temp, {}); // line 124
} else if (temp.displayType == 'date') {
scope.reportDateParams.push(temp);
} else if (temp.displayType == 'text') {
scope.reportTextParams.push(temp);
}
}
});
// fetches pentaho specific paramter names.
// to be utilized in building url for fetching resources from pentaho servers.
// ref : createRunReportForm() run-report.component.ts
// ref : mapPentahoParams() run-report.component.ts
if (scope.reportType == 'Pentaho') {
resourceFactory.reportsResource.get({id: scope.reportId, fields: 'reportParameters'}, function (data) {
scope.pentahoReportParameters = data.reportParameters || [];
});
}
// returns success function which labels select option response objects with name and id.
// ref : fetchSelectOptions() run-report.component.ts
function getSuccuessFunction(paramData) {
var tempDataObj = new Object();
var successFunction = function (data) {
var selectData = [];
var isExistedRecord = false;
for (var i in data.data) {
selectData.push({id: data.data[i].row[0], name: data.data[i].row[1]});
}
for (var j in scope.reportParams) {
if (scope.reportParams[j].name == paramData.name) {
scope.reportParams[j].selectOptions = selectData;
isExistedRecord = true;
}
}
// Add all select option //
if (!isExistedRecord) {
if(paramData.selectAll == 'Y'){
selectData.push({id: "-1", name: "All"});
}
paramData.selectOptions = selectData;
scope.reportParams.push(paramData);
}
};
return successFunction;
}
// fetches selectoptions data and labels it with success function
// ref : createRunReportForm() run-report.component.ts
function intializeParams(paramData, params) {
scope.errorStatus = undefined;
scope.errorDetails = [];
params.reportSource = paramData.name;
params.parameterType = true;
var successFunction = getSuccuessFunction(paramData);
resourceFactory.runReportsResource.getReport(params, successFunction);
}
// called in the view, whenever value of parent parameter changes.
// fetches child param with select options based on parent param's value.
// ref : createRunReportForm() run_report.component.ts
// ref : setChildControls() run_report.component.ts
scope.getDependencies = function (paramData) {
for (var i = 0; i < scope.reqFields.length; i++) {
var temp = scope.reqFields[i];
if (temp.parentParameterName == paramData.name) {
if (temp.displayType == 'select') {
var parentParamValue = this.formData[paramData.inputName];
if (parentParamValue != undefined) {
eval("var params={};params." + paramData.inputName + "='" + parentParamValue + "';");
intializeParams(temp, params);
}
} else if (temp.displayType == 'date') {
scope.reportDateParams.push(temp);
}
}
}
};
// checks collapse status of html elements.
// ref : Not needed
scope.checkStatus = function () {
var collapsed = false;
if (scope.isCollapsed) {
collapsed = true;
}
return collapsed;
};
// date format validation function.
// ref : Not needed in angular 7
function invalidDate(checkDate) {
// validates for yyyy-mm-dd returns true if invalid, false is valid
var dateformat = /^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$/;
if (!(dateformat.test(checkDate))) {
return true;
} else {
var dyear = checkDate.substring(0, 4);
var dmonth = checkDate.substring(5, 7) - 1;
var dday = checkDate.substring(8);
var newDate = new Date(dyear, dmonth, dday);
return !((dday == newDate.getDate()) && (dmonth == newDate.getMonth()) && (dyear == newDate.getFullYear()));
}
}
// removes validation and other errors once presented.
// ref : Not needed in angular 7
function removeErrors() {
var $inputs = $(':input');
$inputs.each(function () {
$(this).removeClass("validationerror");
});
}
// to be called in run-report function to check for form validation errors.
// ref : Not needed, already fullfilled by form validation
function parameterValidationErrors() {
var tmpStartDate = "";
var tmpEndDate = "";
scope.errorDetails = [];
for (var i in scope.reqFields) {
var paramDetails = scope.reqFields[i];
switch (paramDetails.displayType) {
case "select":
var selectedVal = scope.formData[paramDetails.inputName];
if (selectedVal == undefined || selectedVal == 0) {
var fieldId = '#' + paramDetails.inputName;
$(fieldId).addClass("validationerror");
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.parameter.required';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
}
break;
case "date":
var tmpDate = scope.formData[paramDetails.inputName];
if (tmpDate == undefined || !(tmpDate > "")) {
var fieldId = '#' + paramDetails.inputName;
$(fieldId).addClass("validationerror");
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.parameter.required';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
}
if (tmpDate && invalidDate(tmpDate) == true) {
var fieldId = '#' + paramDetails.inputName;
$(fieldId).addClass("validationerror");
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.invalid.value.for.parameter';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
}
if (paramDetails.variable == "startDate") tmpStartDate = tmpDate;
if (paramDetails.variable == "endDate") tmpEndDate = tmpDate;
break;
case "text":
var selectedVal = scope.formData[paramDetails.inputName];
if (selectedVal == undefined || selectedVal == 0) {
var fieldId = '#' + paramDetails.inputName;
$(fieldId).addClass("validationerror");
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.parameter.required';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
}
break;
default:
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.parameter.invalid';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
break;
}
}
// Errors when user sets start date after end date.
// ref : setMinDate() setMaxDate()
if (tmpStartDate > "" && tmpEndDate > "") {
if (tmpStartDate > tmpEndDate) {
var errorObj = new Object();
errorObj.field = paramDetails.inputName;
errorObj.code = 'error.message.report.incorrect.values.for.date.fields';
errorObj.args = {params: []};
errorObj.args.params.push({value: paramDetails.label});
scope.errorDetails.push(errorObj);
}
}
}
// modifies parameter name to pentaho specific name to build resource url.
// ref : report.service.ts
// ref : mapPentahoParams() run_report.component.ts
function buildReportParms() {
var paramCount = 1;
var reportParams = "";
for (var i = 0; i < scope.reqFields.length; i++) {
var reqField = scope.reqFields[i];
for (var j = 0; j < scope.pentahoReportParameters.length; j++) {
var tempParam = scope.pentahoReportParameters[j];
if (reqField.name == tempParam.parameterName) {
var paramName = "R_" + tempParam.reportParameterName;
if (paramCount > 1) reportParams += "&"
reportParams += encodeURIComponent(paramName) + "=" + encodeURIComponent(scope.formData[scope.reqFields[i].inputName]);
paramCount = paramCount + 1;
}
}
}
return reportParams;
}
// nvd3 chart x function
// ref : setBarChart(), setPieChart() chart.component.ts.
scope.xFunction = function () {
return function (d) {
return d.key;
};
};
// nvd3 chart y function
scope.yFunction = function () {
return function (d) {
return d.values;
};
};
// used in the view to switch to pie chart.
scope.setTypePie = function () {
if (scope.type == 'bar') {
scope.type = 'pie';
}
};
// used in the view to switch to bar chart.
scope.setTypeBar = function () {
if (scope.type == 'pie') {
scope.type = 'bar';
}
};
//pie chart
scope.colorFunctionPie = function () {
return function (d, i) {
return colorArrayPie[i];
};
};
// checks the column for decimal type field, used in run report output for Tbale and SMS type
// if true view will pipe all values for users decimal choice.
// ref : isDecimal() table-and-sms.component.ts
scope.isDecimal = function(index){
if(scope.reportData.columnHeaders && scope.reportData.columnHeaders.length > 0){
for(var i=0; i<scope.reportData.columnHeaders.length; i++){
if(scope.reportData.columnHeaders[index].columnType == 'DECIMAL'){
return true;
}
}
}
return false;
};
// core run report functionality.
scope.runReport = function () {
//clear the previous errors
scope.errorDetails = [];
removeErrors();
//update date fields with proper dateformat for utilization in api calls.
// currently date validation is not being done anywhere in app, logger service gives error
for (var i in scope.reportDateParams) {
if (scope.formData[scope.reportDateParams[i].inputName]) {
scope.formData[scope.reportDateParams[i].inputName] = dateFilter(scope.formData[scope.reportDateParams[i].inputName], 'yyyy-MM-dd');
}
}
// custom validation for report parameters, checks validity of run report form.
// ref : Not needed, covered by form validation.
parameterValidationErrors();
// ref : runrReport() run_report.component.ts
if (scope.errorDetails.length == 0) {
// collapses the run report form
scope.isCollapsed = true;
switch (scope.reportType) {
case "Table":
case "SMS":
scope.hideTable = false;
scope.hidePentahoReport = true;
scope.hideChart = true;
scope.formData.reportSource = scope.reportName;
//fetch data based on user response.
resourceFactory.runReportsResource.getReport(scope.formData, function (data) {
//clear the csvData array for each request
scope.csvData = [];
// table headers and data
scope.reportData.columnHeaders = data.columnHeaders;
scope.reportData.data = data.data;
for (var i in data.columnHeaders) {
scope.row.push(data.columnHeaders[i].columnName);
}
// push data into csv array to be utilized by ngcsv.
scope.csvData.push(scope.row);
for (var k in data.data) {
scope.csvData.push(data.data[k].row);
}
});
break;
case "Pentaho":
scope.hideTable = true;
scope.hidePentahoReport = false;
scope.hideChart = true;
// build url prefix
// ref : report.service.ts
var reportURL = $rootScope.hostUrl + API_VERSION + "/runreports/" + encodeURIComponent(scope.reportName);
reportURL += "?output-type=" + encodeURIComponent(scope.formData.outputType) + "&tenantIdentifier=" + $rootScope.tenantIdentifier + "&locale=" + scope.optlang.code + "&dateFormat=" + scope.df;
var inQueryParameters = buildReportParms(); // append the form values to url.
if (inQueryParameters > "") reportURL += "&" + inQueryParameters;
// ref: domsanitizer pentaho.component.ts
// Allow untrusted urls for the ajax request.
// http://docs.angularjs.org/error/$sce/insecurl
reportURL = $sce.trustAsResourceUrl(reportURL);
reportURL = $sce.valueOf(reportURL);
// fetch resource from pentaho servers.
http.get(reportURL, {responseType: 'arraybuffer'}).
success(function(data, status, headers, config) {
// extract content type from response headers, set the blob type.
var contentType = headers('Content-Type');
var file = new Blob([data], {type: contentType});
var fileContent = URL.createObjectURL(file);
// Pass the form data to the iframe as a trusted data url.
scope.baseURL = $sce.trustAsResourceUrl(fileContent);
});
break;
case "Chart":
scope.hideTable = true;
scope.hidePentahoReport = true;
scope.hideChart = false;
scope.formData.reportSource = scope.reportName;
//fetch data based on user response.
resourceFactory.runReportsResource.getReport(scope.formData, function (data) {
scope.reportData.columnHeaders = data.columnHeaders;
scope.reportData.data = data.data;
scope.chartData = [];
scope.barData = [];
// configure nvd3 charts data
var l = data.data.length;
for (var i = 0; i < l; i++) {
scope.row = {};
scope.row.key = data.data[i].row[0];
scope.row.values = data.data[i].row[1];
scope.chartData.push(scope.row);
}
var x = {};
x.key = "summary";
x.values = [];
for (var m = 0; m < l; m++) {
var inner = [data.data[m].row[0], data.data[m].row[1]];
x.values.push(inner);
}
scope.barData.push(x);
});
break;
// ref: Not needed
default:
var errorObj = new Object();
errorObj.field = scope.reportType;
errorObj.code = 'error.message.report.type.is.invalid';
errorObj.args = {params: []};
errorObj.args.params.push({value: scope.reportType});
scope.errorDetails.push(errorObj);
break;
}
}
};
}
});
mifosX.ng.application.controller('RunReportsController', ['$scope', '$routeParams', 'ResourceFactory', '$location', 'dateFilter', '$http', 'API_VERSION', '$rootScope', '$sce', mifosX.controllers.RunReportsController]).run(function ($log) {
$log.info("RunReportsController initialized");
});
}(mifosX.controllers || {}));
<div class="content-container">
<ul class="breadcrumb">
<li><a href="#/reports/all">{{'label.anchor.reports' | translate}}</a></li>
<li class="active">{{'label.anchor.runreport' | translate}}</li>
</ul>
<div class="card well" ng-controller="RunReportsController">
<api-validate></api-validate>
<div class="span gray-head" style="margin-left:0%;height:30px;">
<span style="margin-left: 10px;font-size:20px;">
<strong>{{''+reportName+'' | translate}}</strong>
<!-- chart toggle buttons -->
<button type="button" class="btn btn-primary pull-right" ng-show="checkStatus()"
ng-click="isCollapsed=!isCollapsed"><i class="fa fa-chevron-down "></i>{{
'label.button.parameters' | translate }}
</button>
<button type="button" data-ng-hide="hideChart" class="btn btn-primary pull-right" ng-click="setTypePie()">
{{ 'label.input.piechart' | translate }}
</button>
<button type="button" data-ng-hide="hideChart" class="btn btn-primary pull-right" ng-click="setTypeBar()">
<i class="fa fa-bar-chart-o"></i>&nbsp;&nbsp;{{ 'label.input.barchart' | translate }}
</button>
</span>
</div>
<div ng-hide="isCollapsed" class="alert-block form-horizontal">
<br>
<!-- form -->
************************************************************************************************************************
<div class="form-group info" ng-repeat="reportParam in reportParams">
<label class="control-label col-sm-2" for="{{reportParam.variable}}">{{ reportParam.label | translate}}</label>
<div class="col-sm-3">
<select chosen="reportParam.selectOptions" id="{{reportParam.inputName}}" ng-model="formData[reportParam.inputName]"
ng-options="selectOption.id as selectOption.name for selectOption in reportParam.selectOptions"
value="{{selectOption.id}}"
class="form-control input-xlarge" ng-change="getDependencies(reportParam)" required>
<option value="">--{{"label.menu.selectone" | translate}}--</option>
</select>
</div>
</div>
<div class="form-group info" ng-repeat="reportTextParam in reportTextParams">
<label class="control-label col-sm-2" for="{{reportTextParam.variable}}">{{ reportTextParam.label |
translate}}</label>
<div class="col-sm-3">
<input style="width: 257px" id="{{reportTextParam.inputName}}" type="text" ng-model="formData[reportTextParam.inputName]" class="form-control" required late-Validate/>
</div>
</div>
<div class="form-group info" ng-repeat="reportDateParam in reportDateParams">
<label class="control-label col-sm-2" for="{{reportDateParam.variable}}">{{ reportDateParam.label |
translate}}</label>
<div class="col-sm-3">
<input style="width: 257px" id="{{reportDateParam.inputName}}" type="text" datepicker-pop="yyyy-MM-dd"
ng-model="formData[reportDateParam.inputName]" is-open="'opened'+$index" min="minDate"
max="'2020-06-22'" date-disabled="disabled(date, mode)" class="form-control" required late-Validate/>
</div>
</div>
**********************************************************************************************************************************
<!-- Gets shown only when pentaho is selctecd OUTPUT FORMAT-->
<div class="form-group info" ng-show="reportType == 'Pentaho'">
<label class="control-label col-sm-2" for="outputType">{{ 'label.input.outputtype' | translate }}</label>
<div class="col-sm-3">
<select class="form-control input-xlarge" ng-model="formData.outputType">
<option value="HTML">{{ 'label.input.showreport' | translate }}</option>
<option value="XLS">{{ 'label.input.exportexcel' | translate }}</option>
<option value="XLSX">{{ 'label.input.exportexcel2' | translate }}</option>
<option value="CSV">{{ 'label.input.exportcsv' | translate }}</option>
<option value="PDF">{{ 'label.input.pdfformat' | translate }}</option>
</select>
</div>
</div>
<!-- decimals chice input -->
<div class="form-group info">
<label class="control-label col-sm-2" for="decimalsChoice">{{ 'label.input.decimalplace' | translate }}</label>
<div class="col-sm-3">
<select id="decimalsChoice" class="form-control input-xlarge" ng-model="decimalsChoice">
<option value="">{{ 'label.input.no.decimalplaces' | translate }}</option>
<option value="4">4</option>
<option value="3">3</option>
<option value="2">2</option>
<option value="1">1</option>
<option value="0">0</option>
</select>
</div>
</div>
<!-- run report button -->
<span class="col-md-offset-3 paddedleft120"><a id="run" ng-click="runReport()" class="btn btn-primary">
<i class="fa fa-play"></i>&nbsp;&nbsp;{{ 'label.button.runreport' | translate }}</a></span>
</div>
<!-- *************Related to Run Report Functionality************* -->
<!-- Renders bar andpie charts -->
<div style="visibility: hidden">
<table>
<tr>
<td width="30%"></td>
<td>
<input type="radio" ng-model="type" value="pie">{{'label.input.pie' |
translate}}<br/>
<td>
<td>
<input type="radio" ng-model="type" value="bar">{{'label.input.bar' |
translate}}<br/>
</td>
</tr>
</table>
</div>
<!-- BAR and PIE chart HTML -->
<div style="margin-left:-45px;overflow:auto">
<div data-ng-hide="hideChart">
<div data-ng-show="type=='pie'">
<nvd3-pie-chart
data="chartData"
id="pie"
width="1100"
height="900"
x="xFunction()"
y="yFunction()"
tooltips="true"
showLegend="true"
showLabels="true"
labelType="percent">
<svg height="1300"></svg>
</nvd3-pie-chart>
</div>
<div style="margin-left: 100px" data-ng-show="type=='bar'">
<nvd3-multi-bar-chart
data="barData"
id="barchart"
width="1000"
height="600"
showValues="true"
tooltips="true"
rotateLabels="90">
<svg></svg>
</nvd3-multi-bar-chart>
</div>
</div>
</div>
<div data-ng-hide="hideTable">
<div class="pull-right" ng-show="reportType == 'Table'">
<button type="button" class="btn btn-primary" ng-csv="csvData"><i class="fa fa-file "></i>&nbsp;&nbsp;{{'label.button.exportcsv'
| translate }}
</button>
</div>
<div scroll>
<table style="width: 1200px;white-space:nowrap" class="table table-bordered" data-anchor>
<thead>
<tr class="graybg">
<th ng-repeat="columnHeader in reportData.columnHeaders">{{columnHeader.columnName}}</th>
</tr>
</thead>
<tbody>
<!-- http://www.anujgakhar.com/2013/06/15/duplicates-in-a-repeater-are-not-allowed-in-angularjs/ -->
<tr id="{{$index}}" data-ng-click="highlight($index)" ng-repeat="row in reportData.data track by $index">
<td ng-repeat="col in row.row track by $index">
<!-- decimals filtering -->
<span ng-show="isDecimal($index)">{{col | FormatNumber:decimalsChoice}}</span>
<span ng-hide="isDecimal($index)">{{col}}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- pentaho output -->
<div uib-collapse="hidePentahoReport" class="row alert-block span tab-content">
<br>
<iframe id="rptLoadingFrame" ng-src="{{baseURL}}" frameborder="0" width="100%" height="600px"></iframe>
</div>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment