Commit 08a5ed40 authored by Boris Kocherov's avatar Boris Kocherov

add CUBEMEMBER, CUBEVALUE

use lazy calculation
parent 6ee6d7fe
/* jshint -W040 */
/*
* (c) Copyright Ascensio System SIA 2010-2017
*
......@@ -38,15 +39,298 @@
*/
function (window, undefined) {
var cBaseFunction = AscCommonExcel.cBaseFunction;
var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup;
var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup,
cElementType = AscCommonExcel.cElementType,
cNumber = AscCommonExcel.cNumber,
cString = AscCommonExcel.cString,
cBool = AscCommonExcel.cBool,
cError = AscCommonExcel.cError,
cErrorType = AscCommonExcel.cErrorType,
cArea = AscCommonExcel.cArea,
cArea3D = AscCommonExcel.cArea3D,
cRef = AscCommonExcel.cRef,
cRef3D = AscCommonExcel.cRef3D,
cEmpty = AscCommonExcel.cEmpty,
cArray = AscCommonExcel.cArray,
cubeScheme = {};
cFormulaFunctionGroup['Cube'] = cFormulaFunctionGroup['Cube'] || [];
cFormulaFunctionGroup['Cube'].push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER, cCUBESET,
cFormulaFunctionGroup.Cube = cFormulaFunctionGroup.Cube || [];
cFormulaFunctionGroup.Cube.push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER, cCUBESET,
cCUBESETCOUNT, cCUBEVALUE);
cFormulaFunctionGroup['NotRealised'] = cFormulaFunctionGroup['NotRealised'] || [];
cFormulaFunctionGroup['NotRealised'].push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER,
cCUBESET, cCUBESETCOUNT, cCUBEVALUE);
cFormulaFunctionGroup.NotRealised = cFormulaFunctionGroup.NotRealised || [];
cFormulaFunctionGroup.NotRealised.push(cCUBEKPIMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER,
cCUBESET, cCUBESETCOUNT);
var xmla = new Xmla({
// listeners: {
// events: Xmla.EVENT_ERROR,
// handler: function (eventName, eventData, xmla) {
// console.log(eventData.exception);
// // alert(
// // "Snap, an error occurred: " + eventData.exception.message + " (" + eventData.exception.code + ")" +
// // (eventData.exception.code === Xmla.Exception.HTTP_ERROR_CDE
// // ? "\nstatus: " + eventData.exception.data.status + "; statusText: " + eventData.exception.data.statusText
// // : "")
// // );
// }
// },
async: true
});
function xmla_request(func, prop) {
var xmla = new Xmla({async: true});
// return function () {
return new RSVP.Queue()
.push(function () {
return new RSVP.Promise(function (resolve, reject) {
prop.success = function (xmla, options, response) {
resolve(response);
};
prop.error = function (xmla, options, response) {
reject(response);
};
xmla[func](prop);
});
});
}
function xmla_request_retry(func, prop) {
return xmla_request(func, prop)
.push(undefined, function (response) {
// fix mondrian Internal and Sql errors
if (response) {
switch (response.code) {
case "SOAP-ENV:Server.00HSBE02":
case "SOAP-ENV:00UE001.Internal Error":
return xmla_request(func, prop);
}
}
throw response;
});
}
function getProperties(connection) {
var connections = {
xmla: {
prop: {
url: "https://d1.erp5.ru/saiku/xmla",
properties: {
DataSourceInfo: "FoodMart",
Catalog: "FoodMart"
}
},
cube: "Sales"
}
};
connection = connections[connection];
connection = JSON.parse(JSON.stringify(connection));
return connection;
}
function getScheme(connection) {
var scheme = cubeScheme[connection];
if (scheme) {
return scheme;
} else {
scheme = {
members: {},
hierarchies: {}
};
cubeScheme[connection] = scheme;
return scheme;
}
}
function getCell(arg0) {
if (arg0 instanceof cArray) {
arg0 = arg0.getElement(0);
// } else if (arg0 instanceof cArea || arg0 instanceof cArea3D) {
// arg0 = arg0.cross(arguments[1].bbox);
} else if (arg0 instanceof cRef || arg0 instanceof cRef3D) {
arg0 = arg0.getValue();
}
return arg0;
}
function parseArgs(mdx_array) {
return function () {
var members = [];
function stringForge(value) {
var array;
if (value.cube_value) {
array = value.cube_value;
} else {
array = value.value.split(',');
}
if (array.length > 0) {
// filter members already existed
members = members.filter(function (i) {
return array.indexOf(i) === -1;
});
members = members.concat(array);
}
}
function cellForge(cell) {
if (cell) {
if (cell.oValue.type === cElementType.error) {
// debugger;
throw "refenced cell contain error";
}
if (cell.formulaParsed && cell.formulaParsed.value) {
stringForge(cell.formulaParsed.value);
} else {
stringForge({value: cell.getValue()});
}
}
}
mdx_array.forEach(function (element) {
switch (element.type) {
case cElementType.cellsRange:
case cElementType.cellsRange3D:
case cElementType.array:
element.foreach(cellForge);
break;
default:
if (element instanceof cRef || element instanceof cRef3D) {
element.getRange().getCells().forEach(cellForge);
} else {
stringForge(element);
}
}
});
return members;
};
}
var AddCubeValueCalculate = (function () {
var deferred = RSVP.defer(),
cells = [];
return function (cell_id) {
if (cells.indexOf(cell_id) === -1) {
cells.push(cell_id);
}
// console.log('+ ' + cells);
return function () {
var i = cells.indexOf(cell_id);
if (i !== -1) {
cells.splice(i, 1);
}
// console.log('-' + cells);
if (cells.length === 0) {
deferred.resolve();
deferred = RSVP.defer();
return {};
}
return deferred.promise;
};
};
})();
function execute(connection) {
var scheme = getScheme(connection);
if (!scheme.execute) {
scheme.execute = RSVP.defer();
new RSVP.Queue()
.push(function () {
var settings = getProperties(connection),
prop = settings.prop,
hierarchies = scheme.hierarchies,
hierarchy,
mdx = [];
for (hierarchy in hierarchies) {
mdx.push("{" + hierarchies[hierarchy].join(",") + "}");
}
prop.statement = "SELECT " + mdx.join("*") +
" ON 0 FROM [" + settings.cube + "]";
return xmla_request("execute", prop);
})
.push(function (dataset) {
var cellset = dataset.getCellset(),
axis_count = dataset.axisCount(),
cell_id = 0,
axis_id,
axis,
cube = {
axes: {length: axis_count},
members: {},
hierarchies: {length: 0},
cells: []
};
function collectAxes(axisIndex, parent_members) {
var member;
if (typeof(axisIndex) === "undefined") {
axisIndex = axis_count - 1;
parent_members = [];
}
axis = dataset.getAxis(axisIndex);
axis.eachTuple(function (tuple) {
var coordinate_tuple = [];
this.eachHierarchy(function (hierarchy) {
member = this.member();
coordinate_tuple.push(member.UName);
});
if (axisIndex) {
collectAxes(axisIndex - 1, parent_members.concat(coordinate_tuple));
} else {
console.log(parent_members.concat(coordinate_tuple) + ' - ' + cube.cells[cell_id]);
cell_id++;
}
});
axis.reset();
}
for (axis_id = 0; axis_id < axis_count; axis_id++) {
axis = dataset.getAxis(axis_id);
cube.axes[axis_id] = {
tuples: {},
length: 0
};
axis.eachTuple(function (tuple) {
var coordinate_tuple = [];
axis.eachHierarchy(function () {
var member = this.member();
if (!cube.members.hasOwnProperty(member.UName)) {
cube.members[member.UName] = member;
}
coordinate_tuple.push(member.UName);
});
cube.axes[axis_id].tuples[coordinate_tuple.join(',')] = tuple.index;
cube.axes[axis_id].length++;
});
axis.eachHierarchy(function (hierarchy) {
cube.hierarchies[hierarchy.name] = {
axis_id: axis_id, tuple_id: hierarchy.index, name: hierarchy.name
};
cube.hierarchies[cube.hierarchies.length] = cube.hierarchies[hierarchy.name];
cube.hierarchies['' + axis_id + ',' + hierarchy.index] = cube.hierarchies[hierarchy.name];
cube.hierarchies.length++;
});
}
do {
cube.cells[cellset.cellOrdinal()] = cellset.cellValue();
} while (cellset.nextCell() > 0);
collectAxes();
scheme.cube = cube;
scheme.execute.resolve(cube);
scheme.execute = null;
scheme.hierarchies = [];
})
.push(undefined, function () {
scheme.execute = null;
scheme.hierarchies = [];
});
}
return scheme.execute.promise;
}
/**
* @constructor
......@@ -73,6 +357,78 @@
cCUBEMEMBER.prototype = Object.create(cBaseFunction.prototype);
cCUBEMEMBER.prototype.constructor = cCUBEMEMBER;
cCUBEMEMBER.prototype.argumentsMin = 2;
cCUBEMEMBER.prototype.argumentsMax = 3;
cCUBEMEMBER.prototype.CalculateLazy = function (queue) {
var connection,
caption;
return queue
.push(function (arg) {
connection = getCell(arg[0]);
caption = getCell(arg[2]);
if (caption) {
caption = caption.getValue();
}
return parseArgs([arg[1]])();
})
.push(function (members) {
var promises = [],
i;
function discoverMember(connection, member_name) {
var settings = getProperties(connection),
prop = settings.prop;
prop.restrictions = {
// 'CATALOG_NAME': 'FoodMart',
'MEMBER_UNIQUE_NAME': member_name,
'CUBE_NAME': settings.cube
};
return xmla_request_retry("discoverMDMembers", prop)
.push(function (response) {
if (response.numRows > 0) {
return response;
} else {
throw "member not found";
}
});
}
for (i = 0; i < members.length; i++) {
if (members[i]) {
promises.push(discoverMember(connection, members[i]));
}
}
return RSVP.all(promises);
})
.push(function (responses) {
var last_id = responses.length - 1,
ret,
scheme = getScheme(connection);
if (!caption) {
caption = responses[last_id].getMemberCaption();
}
ret = new cString(caption);
ret.ca = true;
ret.cube_value = responses.map(function (r) {
var uname = r.getMemberUniqueName(),
member = scheme.members[uname];
if (!member) {
scheme.members[uname] = {h: r.getHierarchyUniqueName()};
}
return uname;
});
return ret;
})
.push(undefined, function () {
return new cError(cErrorType.not_available);
});
};
cCUBEMEMBER.prototype.getInfo = function () {
return {
name: this.name, args: "( x )"
};
};
/**
* @constructor
......@@ -138,4 +494,97 @@
cCUBEVALUE.prototype = Object.create(cBaseFunction.prototype);
cCUBEVALUE.prototype.constructor = cCUBEVALUE;
})(window);
cCUBEVALUE.prototype.argumentsMin = 2;
cCUBEVALUE.prototype.argumentsMax = 5;
cCUBEVALUE.prototype.CalculateLazy = function (queue, range) {
var scheme,
connection,
members,
current_cell_id = range.getCells()[0].getId(),
waiter = AddCubeValueCalculate(current_cell_id);
return queue
.push(function (arg) {
connection = getCell(arg[0]);
scheme = getScheme(connection);
return parseArgs(arg.slice(1))();
})
.push(function (m) {
members = m;
members.forEach(function (member) {
var h;
h = scheme.hierarchies[scheme.members[member].h];
if (!h) {
h = [];
scheme.hierarchies[scheme.members[member].h] = h;
}
if (h.indexOf(member) === -1) {
h.push(member);
}
});
return waiter();
})
.push(function () {
return execute(connection);
})
.push(function (cube) {
var cell_id = 0,
p_d = 1,
h,
member_path,
coordinate = [],
i,
ret;
for (i = 0; i < cube.hierarchies.length; i++) {
h = cube.hierarchies[i];
coordinate[h.axis_id] = [];
coordinate[h.axis_id][h.tuple_id] = null;
}
for (i = 0; i < members.length; i++) {
member_path = members[i];
h = cube.members[member_path];
if (h === undefined) {
throw "query result not contain data for member:" +
member_path;
}
h = h.hierarchy;
h = cube.hierarchies[h];
coordinate[h.axis_id][h.tuple_id] = member_path;
}
coordinate = coordinate.map(function (axis, axis_id) {
axis.forEach(function (h, h_id) {
if (h === null) {
throw "Axis:" + axis_id + " hierarchy:" +
cube.hierarchies[axis_id + ',' + h_id].name +
" not determinated";
}
});
return axis.join(',');
});
coordinate.forEach(function (tuple, axis_id) {
var axis = cube.axes[axis_id];
cell_id = p_d * axis.tuples[tuple] + cell_id;
p_d = p_d * axis.length;
});
ret = new cNumber(cube.cells[cell_id]);
ret.ca = true;
return ret;
})
.push(undefined, function (error) {
console.log(error, current_cell_id);
return waiter()
.then(function () {
var ret = new cError(cErrorType.not_available);
ret.ca = true;
return ret;
});
});
};
// cCUBEVALUE.prototype.Calculate = cCUBEVALUE.prototype.CalculateLazy
cCUBEVALUE.prototype.getInfo = function () {
return {
name: this.name, args: "( connection, member1, member2, .. )"
};
};
})
(window);
......@@ -670,6 +670,112 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseType.prototype.toLocaleString = function () {
return this.toString();
};
cBaseType.prototype.CalculatePromise = function (arg, opt_bbox, isDefName, ws) {
// depromise args
var t = this,
lazy_formulas = [],
lazy_found;
function check_args_promise() {
var promise_flag = false,
element,
length,
i;
function cellForge(cell) {
if (cell && cell.formulaParsed &&
(cell.formulaParsed.lazy_value || cell.formulaParsed.queue)) {
promise_flag = true;
lazy_formulas.push(cell.formulaParsed);
}
}
for (i = 0, length = arg.length; i < length; ++i) {
element = arg[i];
if (typeof element === "function") {
promise_flag = true;
}
if (element instanceof cArea || element instanceof cArea3D) {
element.foreach(cellForge);
}
if (element instanceof cRef || element instanceof cRef3D) {
element.getRange().getCells().forEach(cellForge);
}
}
return promise_flag;
}
lazy_found = check_args_promise();
if (t.CalculateLazy) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy run it
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
});
return t.CalculateLazy(queue, opt_bbox, isDefName, ws);
};
} else {
if (lazy_found) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy add it dependence list
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
return queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
})
.push(function (arg) {
return t.Calculate(arg, opt_bbox, isDefName, ws);
});
};
} else {
return t.Calculate(arg, opt_bbox, isDefName, ws);
}
}
};
/*Basic types of an elements used into formulas*/
/**
......@@ -2347,6 +2453,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseOperator.prototype.Calculate = function () {
return null;
};
cBaseOperator.prototype.CalculatePromise = cBaseType.prototype.CalculatePromise;
cBaseOperator.prototype.Assemble = function (arg) {
var str = "";
if (this.argumentsCurrent === 2) {
......@@ -2394,6 +2501,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
this.value = new cError(cErrorType.wrong_name);
return this.value;
};
cBaseFunction.prototype.CalculatePromise = cBaseOperator.prototype.CalculatePromise
cBaseFunction.prototype.DecrementArguments = function () {
--this.argumentsCurrent;
};
......@@ -4225,7 +4333,7 @@ function parserFormula( formula, parent, _ws ) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
}
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
if (this.parent && this.parent.onFormulaEvent) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
......@@ -5102,6 +5210,8 @@ parserFormula.prototype.parse = function(local, digitDelim) {
};
parserFormula.prototype.calculate = function (opt_defName, opt_bbox, opt_offset) {
var value,
formula = this;
if (this.isCalculate && (!this.calculateDefName || this.calculateDefName[opt_bbox ? opt_bbox.getName() :
opt_bbox])) {
//cycle
......@@ -5145,7 +5255,7 @@ parserFormula.prototype.parse = function(local, digitDelim) {
for (var ind = 0; ind < currentElement.getArguments(); ind++) {
arg.unshift(elemArr.pop());
}
_tmp = currentElement.Calculate(arg, opt_bbox, opt_defName, this.ws);
_tmp = currentElement.CalculatePromise(arg, opt_bbox, opt_defName, this.ws);
if (cNumFormatNull !== _tmp.numFormat) {
numFormat = _tmp.numFormat;
} else if (0 > numFormat || cNumFormatNone === currentElement.numFormat) {
......@@ -5163,8 +5273,28 @@ parserFormula.prototype.parse = function(local, digitDelim) {
elemArr.push(currentElement);
}
}
this.value = elemArr.pop();
this.value.numFormat = numFormat;
value = elemArr.pop();
if (typeof value === "function") {
this.value = new cError(cErrorType.getting_data);
this.queue = true;
this.lazy_value = function () {
formula.lazy_value = null;
formula.queue = value()
.push(function (ret) {
formula.value = ret;
formula.value.numFormat = numFormat;
formula._endCalculate();
opt_bbox.updateOnScreen();
formula.queue = false;
// formula.lazy_value = null;
return formula.value;
});
return formula.queue
}
} else {
this.value = value;
this.value.numFormat = numFormat;
}
this._endCalculate();
return this.value;
......
......@@ -908,6 +908,11 @@
this.buildDefName = {};
},
calcTree: function() {
var dependency_graph = this,
formula,
i,
tasks = [],
lazy_value;
if (this.lockCounter > 0) {
return;
}
......@@ -924,26 +929,44 @@
this._broadcastCells(notifyData, calcTrack);
}
this._broadcastCellsEnd();
for (var i = 0; i < noCalcTrack.length; ++i) {
var formula = noCalcTrack[i];
for (i = 0; i < noCalcTrack.length; ++i) {
formula = noCalcTrack[i];
//defName recalc when calc formula containing it. no need calc it
formula.setIsDirty(false);
}
for (var i = 0; i < calcTrack.length; ++i) {
var formula = calcTrack[i];
for (i = 0; i < calcTrack.length; ++i) {
formula = calcTrack[i];
if (formula.getIsDirty()) {
formula.calculate();
}
}
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = this.cleanCellCache;
this.cleanCellCache = {};
for (var i in tmpCellCache) {
this.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
for (i = calcTrack.length-1; i >= 0; --i) {
lazy_value = calcTrack[i].lazy_value;
if (lazy_value) {
tasks.push(lazy_value());
}
}
function end () {
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = dependency_graph.cleanCellCache;
dependency_graph.cleanCellCache = {};
for (var i in dependency_graph.cleanCellCache) {
dependency_graph.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
}
if (tasks.length > 0) {
new RSVP.Queue()
.push(function () {
return RSVP.all(tasks);
})
.push(end);
} else {
end();
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
},
initOpen: function() {
this._foreachDefName(function(defName) {
......@@ -5387,6 +5410,11 @@
this.nCol = -1;
this.formulaParsed = null;
}
Cell.prototype.getId = function () {
return [this.ws.getId(), this.nRow, this.nCol].join(",");
};
Cell.prototype.getStyle=function(){
return this.xfs;
};
......@@ -5945,7 +5973,7 @@
this.setValue("");
};
Cell.prototype._checkDirty = function(){
if(this.formulaParsed && this.formulaParsed.getIsDirty()) {
if(this.formulaParsed && this.formulaParsed.getIsDirty() && !this.formulaParsed.queue) {
this.formulaParsed.calculate();
}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment