diff --git a/process/ImporterCustomMappingFunctions_lib/ImporterCustomMappingFunctions_lib.aod b/process/ImporterCustomMappingFunctions_lib/ImporterCustomMappingFunctions_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..6544d46839aa52bd3517add653f0eb0a8fb7e881 --- /dev/null +++ b/process/ImporterCustomMappingFunctions_lib/ImporterCustomMappingFunctions_lib.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>ImporterCustomMappingFunctions_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/ImporterCustomMappingFunctions_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/ImporterCustomMappingFunctions_lib/process.js b/process/ImporterCustomMappingFunctions_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..bbfd65d5d7dccccbb1ea6066c73a7df03aea68d8 --- /dev/null +++ b/process/ImporterCustomMappingFunctions_lib/process.js @@ -0,0 +1,4 @@ +/////////////////////////////////////////////////////////////////// +/// custom toolkit methods for the import handler /// +/// edit this, since this is serperate vor every project /// +/////////////////////////////////////////////////////////////////// diff --git a/process/ImporterMappingFunctions_lib/ImporterMappingFunctions_lib.aod b/process/ImporterMappingFunctions_lib/ImporterMappingFunctions_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..0e8acf1918c716ed3d774608965f64b1dba0d136 --- /dev/null +++ b/process/ImporterMappingFunctions_lib/ImporterMappingFunctions_lib.aod @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>ImporterMappingFunctions_lib</name> + <comment></comment> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/ImporterMappingFunctions_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/ImporterMappingFunctions_lib/process.js b/process/ImporterMappingFunctions_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..05c4aa930bda24e17fd357797301b67bd8fce6d3 --- /dev/null +++ b/process/ImporterMappingFunctions_lib/process.js @@ -0,0 +1,1028 @@ +import("system.fileIO"); +import("system.SQLTYPES"); +import("system.text"); +import("system.db"); +import("system.vars"); +import("system.eMath"); +import("system.util"); +import("system.datetime"); +import("system.logging"); +import("Attribute_lib"); +import("Sql_lib"); +import("Importer_lib"); + +///////////////////////////////////////////////////////////////////// +/// toolkit methods for the import handler /// +/// DO NOT TOUCH - use lib_importerCustomMappingFunctions /// +/////////////////////////////////////////////////////////////////// + +/* +* Values of the mapping line: +* Keyword req -- the column index with the new keyword value +* Container req -- the keyword container for the keyword lookup +* +* @name iKeyword +* @param {Object} pObject req the mapping line +* @return {Boolean} true +**/ +function iKeyword(pObject) { + if (!this.doIfCheck(pObject)) return true; + + var keyword = this.InputRecord[pObject.Keyword]; + if(keyword == undefined) keyword = this.resolveSymbol(pObject, pObject.Keyword); + var container = this.InputRecord[pObject.Container]; + if(container == undefined) container = this.resolveSymbol(pObject, pObject.Container); + + if(!keyword || !container) return true; + + var sql = "select " + this.getColumnCase("keyid") + " from " + this.getTableCase("ab_keyword_entry") + " where " + + this.getColumnCase("container") + " = ? and " + this.getColumnCase("title") + " = ?"; + var id = db.cell([sql, [[container, SQLTYPES.VARCHAR], [keyword, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + + if(id == "" || id == null) { + id = util.getNewUUID(); + var columns = [this.getColumnCase("ab_keyword_entryid"), this.getColumnCase("keyid"), this.getColumnCase("container"), + this.getColumnCase("title"), this.getColumnCase("sorting"), this.getColumnCase("isactive"), this.getColumnCase("isessential")]; + sql = "select max(coalesce(sorting, 0))+1 from ab_keyword_entry where container = ?"; + var sort = db.cell([sql, [[container, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if(sort == "") sort = "0"; + values = [id, util.getNewUUID(), container, keyword, sort, "1", "0"]; + this.insertData(this.getTableCase("ab_keyword_entry"), columns, null, values, this.Config.AliasTo); + } + this.setOutput(pObject, id); + return true; +} + +/* + * Values of the mapping line: + * Attribute req -- the new attribute name + * AType req -- the type of the attribute + * OType opt -- the type of the object (AB_ATTRIBUTEUSAGE) + * OID opt -- the row id for the object instance (AB_ATTRIBUTERELATION) + * Value opt -- the value for the object instance (AB_ATTRIBUTERELATION) + * + * @name iAttribute + * @param {Object} pObject req the mapping line + * @return {Boolean} true + * */ +function iAttribute(pObject) { + if (!this.doIfCheck(pObject)) return true; + + var attribute = this.InputRecord[pObject.Attribute]; + if(attribute == undefined) attribute = this.resolveSymbol(pObject, pObject.Attribute); + var atype = this.InputRecord[pObject.AType]; + if(atype == undefined) atype = this.resolveSymbol(pObject, pObject.AType); + var otype = this.InputRecord[pObject.OType]; + if(otype == undefined) otype = this.resolveSymbol(pObject, pObject.OType); + var oid = this.InputRecord[pObject.OID]; + if(oid == undefined) oid = this.resolveSymbol(pObject, pObject.OID); + var value = this.InputRecord[pObject.Value]; + if(value == undefined) value = this.resolveSymbol(pObject, pObject.Value); + + if (!attribute || !atype) return true; + atype = atype.toUpperCase(); + + var valueColumn = ""; + var attributes = attribute.split("."); + var columns = [this.getColumnCase("ab_attributeid"), this.getColumnCase("attribute_parent_id"), this.getColumnCase("attribute_name"), + this.getColumnCase("attribute_type"), this.getColumnCase("attribute_level"), this.getColumnCase("attribute_active")]; + var type = $AttributeTypes.GROUP.toString(); + switch (atype) { + case $AttributeTypes.TEXT.toString(): + valueColumn = this.getColumnCase("char_value"); + break; + case $AttributeTypes.DATE.toString(): + valueColumn = this.getColumnCase("date_value"); + break; + case $AttributeTypes.NUMBER.toString(): + valueColumn = this.getColumnCase("number_value"); + break; + case $AttributeTypes.BOOLEAN.toString(): + valueColumn = this.getColumnCase("bool_value"); + break; + case $AttributeTypes.COMBO.toString(): + valueColumn = this.getColumnCase("id_value"); + type = $AttributeTypes.COMBO.toString(); + break; + default: + return true; + } + + if (this.FuncBuffer.iAttribute == undefined) this.FuncBuffer.iAttribute = {childs: {}}; + var pathToFollow = this.FuncBuffer.iAttribute; + for (var i = 0; i < attributes.length; i++) { + if (pathToFollow["childs"][attributes[i]] != undefined) { + var id = pathToFollow["childs"][attributes[i]]["id"]; + } else { + pathToFollow["childs"][attributes[i]] = {id: id, childs: {}}; + if (i == 0) { + var parent = "NULL"; + var sql = "select " + this.getColumnCase("ab_attributeid") + " from " + this.getTableCase("ab_attribute") + " where " + + this.getColumnCase("attribute_name") + " = ? and " + this.getColumnCase("attribute_level") + " = 0"; + id = db.cell([sql, [[attributes[i], SQLTYPES.VARCHAR]]], this.Config.AliasTo); + } else { + parent = pathToFollow["id"]; + sql = "select " + this.getColumnCase("ab_attributeid") + " from " + this.getTableCase("ab_attribute") + " where " + + this.getColumnCase("attribute_name") + " = ? and " + this.getColumnCase("attribute_parent_id") + " = ?"; + id = db.cell([sql, [[attributes[i], SQLTYPES.VARCHAR], [parent, SQLTYPES.CHAR]]], this.Config.AliasTo); + } + if (id == "" || id == null) { + id = util.getNewUUID(); + if (attributes.length == i+1) type = atype; + //TODO: add insertNoWait to instantly add AB_ATTRIBUTE records; this ensures that nothing is in the funcBuffer that does not exist in the database + //TODO: check: are COMOB-values added automatically? + this.insertData(this.getTableCase("ab_attribute"), columns, null, [id, parent, attributes[i], type, i.toString(), "1"], this.Config.AliasTo); + } + pathToFollow["childs"][attributes[i]]["id"] = id; + } + pathToFollow = pathToFollow["childs"][attributes[i]]; + } + + if (otype) { + var aid = id; + sql = "select " + this.getColumnCase("ab_attributeusageid") + " from " + this.getTableCase("ab_attributeusage") + " where " + + this.getColumnCase("ab_attribute_id") + " = ? and " + this.getColumnCase("object_type") + " = ?"; + id = db.cell([sql, [[aid, SQLTYPES.CHAR], [otype, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if (id == "" || id == null) { + columns = [this.getColumnCase("ab_attributeusageid"), this.getColumnCase("ab_attribute_id"), this.getColumnCase("object_type")]; + this.insertData(this.getTableCase("ab_attributeusage"), columns, null, [util.getNewUUID(), aid, otype], this.Config.AliasTo); + } + + if (value && oid) { + sql = "select " + this.getColumnCase("ab_attributerelationid") + " from " + this.getTableCase("ab_attributerelation") + " where " + + this.getColumnCase("ab_attribute_id") + " = ? and " + this.getColumnCase("object_rowid") + " = ? and " + + this.getColumnCase("object_type") + " = ?"; + id = db.cell([sql, [[aid, SQLTYPES.CHAR], [oid, SQLTYPES.CHAR], [otype, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if (id == "" || id == null) { + columns = [this.getColumnCase("ab_attributerelationid"), this.getColumnCase("ab_attribute_id"), this.getColumnCase("object_type"), + this.getColumnCase("object_rowid"), valueColumn]; + this.insertData(this.getTableCase("ab_attributerelation"), columns, null, [util.getNewUUID(), aid, otype, oid, value], this.Config.AliasTo); + } else if (this.Config.ImportCommand.indexOf("update") != -1) { + cond = this.getColumnCase("ab_attributerelationid") + " = '" + id + "'"; + this.updateData(this.getTableCase("ab_attributerelation"), [valueColumn], null, [value], cond, this.Config.AliasTo); + } + } + } + return true; +} + +/* + * Values of the mapping line: + * Attribute req -- the column index with the new attribute value + * AType req -- the type of the attribute + * Container req -- the container name of the keyword + * Keyword opt -- a new keyword name or an existing KeyId (AB_KEYWORD_ATTRIBUTERELATION) + * Value opt - the value of the relation (AB_KEYWORD_ATTRIBUTERELATION) + * + * @name iKeywordAttribute + * @param {Object} pObject req the mapping line + * @return {Boolean} true + * */ +function iKeywordAttribute(pObject) { + if (!this.doIfCheck(pObject)) return true; + + var attribute = this.InputRecord[pObject.Attribute]; + if(attribute == undefined) attribute = this.resolveSymbol(pObject, pObject.Attribute); + var atype = this.InputRecord[pObject.AType]; + if(atype == undefined) atype = this.resolveSymbol(pObject, pObject.AType); + var container = this.InputRecord[pObject.Container]; + if(container == undefined) container = this.resolveSymbol(pObject, pObject.Container); + var keyword = this.InputRecord[pObject.Keyword]; + if(keyword == undefined) keyword = this.resolveSymbol(pObject, pObject.Keyword); + var value = this.InputRecord[pObject.Value]; + if(value == undefined) value = this.resolveSymbol(pObject, pObject.Value); + + if (!attribute || !container || !atype) return true; + atype = atype.toUpperCase(); + + var valueColumn = ""; + switch (atype) { + case $AttributeTypes.TEXT.toString(): + valueColumn = this.getColumnCase("char_value"); + break; + case $AttributeTypes.NUMBER.toString(): + valueColumn = this.getColumnCase("number_value"); + break; + case $AttributeTypes.BOOLEAN.toString(): + valueColumn = this.getColumnCase("bool_value"); + break; + default: + return true; + } + + var sql = "select " + this.getColumnCase("ab_keyword_attributeid") + " from " + this.getTableCase("ab_keyword_attribute") + + " where " + this.getColumnCase("name") + " = ? and " + this.getColumnCase("container") + " = ?"; + var aid = db.cell([sql, [[attribute, SQLTYPES.VARCHAR], [container, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if (aid == "" || aid == null) { + aid = util.getNewUUID(); + var columns = [this.getColumnCase("ab_keyword_attributeid"), this.getColumnCase("name"), this.getColumnCase("container"), this.getColumnCase("type")]; + this.insertData(this.getTableCase("ab_keyword_attribute"), columns, null, [aid, attribute, container, atype], this.Config.AliasTo); + } + + if (keyword && value) { + sql = "select " + this.getColumnCase("keyid") + " from " + this.getTableCase("ab_keyword_entry") + " where " + + this.getColumnCase("keyid") + " = ?"; + var kid = db.cell([sql, [[keyword, SQLTYPES.CHAR]]], this.Config.AliasTo); + + if (kid == "" || kid == null) { + sql = "select " + this.getColumnCase("keyid") + " from " + this.getTableCase("ab_keyword_entry") + " where " + + this.getColumnCase("container") + " = ? and " + this.getColumnCase("title") + " = ?"; + kid = db.cell([sql, [[container, SQLTYPES.VARCHAR], [keyword, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if (kid == "" || kid == null) { + columns = [this.getColumnCase("ab_keyword_entryid"), this.getColumnCase("keyid"), this.getColumnCase("container"), + this.getColumnCase("title"), this.getColumnCase("sorting"), this.getColumnCase("isactive"), this.getColumnCase("isessential")]; + sql = "select max(coalesce(sorting, 0))+1 from ab_keyword_entry where container = ?"; + var sort = db.cell([sql, [[container, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if(sort == "") sort = "0"; + kid = util.getNewUUID(); + this.insertData(this.getTableCase("ab_keyword_entry"), columns, null, + [kid, util.getNewUUID(), container, keyword, sort, "1", "0"], this.Config.AliasTo); + } + } + + sql = "select " + this.getColumnCase("ab_keyword_attributerelationid") + " from " + this.getTableCase("ab_keyword_attributerelation") + + " where " + this.getColumnCase("ab_keyword_entry_id") + " = ? and " + this.getColumnCase("ab_keyword_attribute_id") + " = ?"; + id = db.cell([sql, [[kid, SQLTYPES.CHAR], [aid, SQLTYPES.CHAR]]], this.Config.AliasTo); + if (id == "" || id == null) { + columns = [this.getColumnCase("ab_keyword_attributerelationid"), this.getColumnCase("ab_keyword_entry_id"), + this.getColumnCase("ab_keyword_attribute_id"), valueColumn]; + id = util.getNewUUID(); + this.insertData(this.getTableCase("ab_keyword_attributerelation"), columns, null, [id, kid, aid, value], this.Config.AliasTo); + } else { + if (this.Config.ImportCommand.indexOf("update") != -1) { + cond = this.getColumnCase("ab_keyword_attributerelationid") + " = '" + id + "'"; + this.updateData(this.getTableCase("ab_keyword_attributerelation"), [valueColumn], null, [value], cond, this.Config.AliasTo); + } + } + this.setOutput(pObject, id); + } else { + this.setOutput(pObject, aid); + } + return true; +} + +/* + * Values of the mapping line: + * Address req -- the address for the communication entry + * Medium req -- the medium id + * ContactID req -- the id of the entry in the contact table + * Standard opt -- the standard value (boolean) + * + * @name iComm + * @param {Object} pObject req the mapping line + * @return {Boolean} true + * */ +function iComm(pObject) { + if (! this.doIfCheck(pObject)) return true; + + var address = this.InputRecord[pObject.Address]; + if(address == undefined) address = this.resolveSymbol(pObject, pObject.Address); + var medium = this.InputRecord[pObject.Medium]; + if(medium == undefined) medium = this.resolveSymbol(pObject, pObject.Medium); + var contact = this.InputRecord[pObject.ContactID]; + if(contact == undefined) contact = this.resolveSymbol(pObject, pObject.ContactID); + var standard = "0"; + if(pObject.Standard) standard = "1"; + + if(!address || !medium || !contact) return true; + + var sql = "select " + this.getColumnCase("communicationid") + " from " + this.getTableCase("communication") + +" where " + this.getColumnCase("contact_id") + " = ? and " + this.getColumnCase("medium_id") + " = ? and " + + this.getColumnCase("standard") + " = ? and " + this.getColumnCase("addr") + " = ?" + var id = db.cell([sql, [[contact, SQLTYPES.CHAR], [medium, SQLTYPES.INTEGER], + [standard, SQLTYPES.SMALLINT], [address, SQLTYPES.VARCHAR]]], this.Config.AliasTo); + if (id == "" || id == null) { + var columns = [this.getColumnCase("communicationid"), this.getColumnCase("addr"), + this.getColumnCase("medium_id"), this.getColumnCase("contact_id"), this.getColumnCase("standard")]; + this.insertData(this.getTableCase("communication"), columns, null, [util.getNewUUID(), address, medium, contact, standard], this.Config.AliasTo); + } + return true; +} + +/* + * Values of the mapping line: + * Reason opt -- the reason + * Medium req -- the medium id + * ContactID req -- the id of the entry in the contact table + * Type req -- yes or no to communication + * + * @name iCommRestriction + * @param {Object} pObject req the mapping line + * @return {Boolean} true + * */ +function iCommRestriction(pObject) { + if (!this.doIfCheck(pObject)) return true; + + var reason = this.InputRecord[pObject.Reason]; + if(reason == undefined) reason = this.resolveSymbol(pObject, pObject.Reason); + var medium = this.InputRecord[pObject.Medium]; + if(medium == undefined) medium = this.resolveSymbol(pObject, pObject.Medium); + var contact = this.InputRecord[pObject.ContactID]; + if(contact == undefined) contact = this.resolveSymbol(pObject, pObject.ContactID); + var type = this.InputRecord[pObject.Type]; + if(type == undefined) type = this.resolveSymbol(pObject, pObject.Type); + + if (!medium || !contact || !type) return true; + + var sql = "select top 1 " + this.getColumnCase("type") + " from " + this.getTableCase("commrestriction") + +" where " + this.getColumnCase("contact_id") + " = ? and " + this.getColumnCase("medium") + " = ? order by " + + this.getColumnCase("date_edit") + " desc, " + this.getColumnCase("date_new") + " desc"; + var id = db.cell([sql, [[contact, SQLTYPES.CHAR], [medium, SQLTYPES.CHAR]]], this.Config.AliasTo); + if (id == "" || id == null || id != type) { + if(reason == undefined || reason == null) reason = "NULL"; + var columns = [this.getColumnCase("commrestrictionid"), this.getColumnCase("medium"), this.getColumnCase("contact_id"), this.getColumnCase("type"), this.getColumnCase("reason")]; + this.insertData(this.getTableCase("commrestriction"), columns, null, [util.getNewUUID(), medium, contact, type, reason], this.Config.AliasTo); + } + return true; +} + +/* + * Values of the mapping line: + * ActivityID req -- the column specifier for the activity table + * OID req -- the id for a default object for object_rowid + * OType req -- the context name + * + * @name iActivityLink + * @param {Object} pObject req the mapping line + * @return {Boolean} true + * */ +function iActivityLink(pObject) { + if (!this.doIfCheck(pObject)) return true; + + var aid = this.InputRecord[pObject.ActivityID]; + if(aid == undefined) aid = this.resolveSymbol(pObject, pObject.ActivityID); + var otype = this.InputRecord[pObject.OType]; + if(otype == undefined) otype = this.resolveSymbol(pObject, pObject.OType); + var oid = this.InputRecord[pObject.OID]; + if(oid == undefined) oid = this.resolveSymbol(pObject, pObject.OID); + + if (!aid || !oid || !otype) return true; + + var sql = "select " + this.getColumnCase("activitylinkid") + " from " + this.getTableCase("activitylink") + " where " + + this.getColumnCase("activity_id") + " = ? and " + this.getColumnCase("object_type") + " = ? and " + this.getColumnCase("object_rowid") + " = ?"; + var id = db.cell([sql, [[aid, SQLTYPES.VARCHAR], [otype, SQLTYPES.VARCHAR], [oid, SQLTYPES.CHAR]]], this.Config.AliasTo); + if (id == "" || id == null) { + var columns = [this.getColumnCase("activitylinkid"), this.getColumnCase("activity_id"), this.getColumnCase("object_type"), this.getColumnCase("object_rowid")]; + this.insertData(this.getTableCase("activitylink"), columns, null, [util.getNewUUID(), aid, otype, oid], this.Config.AliasTo); + } + return true; +} + +/* +* imports an document from a given path +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true, if import of the data was successful, otherwise false +*/ +function iDocumentByPath(pObject){ + var resultDocument = true; + if (! this.doIfCheck(pObject)) return true; + + try { + if(pObject.Rowid != "" && pObject.Filename != "") { + if(pObject.Value && pObject.Rowid) { + var wert = this.resolveSymbol(pObject, pObject.Value); + var row = this.resolveSymbol(pObject, pObject.Rowid); + var dateNew = this.resolveSymbol(pObject, pObject.DateNew); + var filename = this.InputRecord[pObject.Filename]; + var data = fileIO.getData(wert, util.DATA_BINARY); + var length = fileIO.getLength(wert); + logging.log(filename + " " + dateNew) + } + var sql = "select count(" + this.getColumnCase("row_id") + ") from " + this.getTableCase("asys_binaries") + + " where " + this.getColumnCase("row_id") + " = ? and " + this.getColumnCase("filename") + " = ?"; + var count = db.cell([sql, [[row, SQLTYPES.CHAR], [filename, SQLTYPES.VARCHAR]]], this.Config. AliasSys); + if(count == 0) { + var cols = [this.getColumnCase("Id"), this.getColumnCase("Tablename"), this.getColumnCase("Datasize"), + this.getColumnCase("date_new"), this.getColumnCase("date_edit"), this.getColumnCase("user_new"), + this.getColumnCase("bindata"), this.getColumnCase("containername"), this.getColumnCase("filename"), + this.getColumnCase("row_id"), this.getColumnCase("mimetype")]; + var vals = [util.getNewUUID(), "$!GENERIC!$", length, dateNew, dateNew, vars.getString("$sys.user"), data, + "DOCUMENT", filename, row, util.getMimeType(filename)]; + db.insertData(this.getTableCase("asys_binaries"), cols, null, vals, this.Config. AliasSys); + } + } + } catch(ex) { + logging.log("Datei nicht gefunden!"); + resultDocument = false; + } + return resultDocument; +} + +/* +* imports an document +* draft: Container: "string", Row: "TBL.COLID", Source: index, Filename: index, Tablename: "string", +* Description: "string", Keywords: "string" +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true, if import of the data was successful, otherwise false +*/ +function iDocument(pObject) +{ + var resultDocument = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + // iDocument is insert-only + this.setDefaultAction(pObject); + if(pObject.Action != "I") return resultDocument; + + try + { + var desc = ""; + if(pObject.Description != undefined) desc = this.InputRecord[pObject.Description]; + var keyw = ""; + if(pObject.Keywords != undefined) desc = this.InputRecord[pObject.Keywords]; + + if(pObject.Rowid != "" && pObject.Filename != "") + db.insertBinary( + pObject.Tablename, + pObject.Container, + this.getOutput(pObject, "Rowid"), + null, + this.InputRecord[pObject.Source], + this.InputRecord[pObject.Filename], + desc, + keyw, + this.Config.AliasTo); + } + catch(ex) + { + logging.log(ex); + resultDocument = false; + } + + return resultDocument; +} + +/* +* move import data to target +* +* @param {Object} pObject req the mapping line +* +* @example: [iMove, { Source: 3, Target: "RELATION.ADDRESS" } ] +* +* @return {Boolean} false, if the import of the row is not possible. otherwise true +*/ +function iMove(pObject) +{ + var resultMove = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + if(pObject.Blobfile != undefined && pObject.Blobfile == true) // blobfile move + { + var pn = pObject.Pathname; + var fn = this.InputRecord[pObject.Source]; + + // s will be NULL is something went wrong (no file, access error, etc) + var s = this.getFileContent(pn.toString() + fn.toString(), util.DATA_TEXT); + + // if blob file could be read, assign to output buffer, + // otherweise signal "no import for this row" by returning false as the function value + if(s != null && s != undefined) + this.setOutput(pObject, s); + else + resultMove = false; + } + else // no blob file handling, just plan old move + { + var expr = ""; + if(pObject.Source != undefined) expr = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) expr = this.resolveSymbol(pObject, pObject.Value, pObject.Eval); + if(pObject.Map != undefined && pObject.Index) expr = pObject.Map[this.resolveSymbol(pObject, pObject.Index, pObject.Eval)]; + + //if expr is undefined, then do no replace + if(expr != undefined) + { + // check for trimming option + if(pObject.Trim != undefined && typeof(pObject.Trim) == "string") + { + switch(pObject.Trim.toLowerCase()) + { + case "left": + expr = expr.replace(/^\s+/, ""); + break; + case "right": + expr = expr.replace(/\s+$/, ""); + break; + case "both": + expr = expr.replace(/^\s+|\s+$/g, ""); + break; + } + } + + // chek for replacing option + if(pObject.Replace != undefined && typeof(pObject.Replace) == "string" && pObject.ReplaceTo != undefined) + expr = expr.replace(pObject.Replace, pObject.ReplaceTo); + + // check for format conversion + if(pObject.HTML2Text) + expr = text.html2text(expr); + else if (pObject.RTF2Text) + expr = text.rtf2text(expr); + } + else + expr = ""; + + this.setOutput(pObject, expr); + } + + return resultMove; +} + + +/* +* Return word number "Index" from source column. +* Values of the mapping line: +* String Source the source column index +* String Regex the regular expression for the split +* Number Index the word number starting with 0 +* String Substring "right" or "left" +* String Separator concatenation string, default is blank +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true +*/ +function iWord(pObject) +{ + var resultWord = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var mode = pObject.Substring; + var sep = pObject.Separator; + if(sep == undefined) sep = " "; // default concat with blank + + // split the input string with the regex and get the word number, + // negative values will count from the end of the string (e.g. -1 for the last word in a string) + if(pObject.Source != undefined) s = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) s = this.resolveSymbol(pObject, pObject.Value); + + s = s.split( pObject.Regex ); + var len = s.length; + var num = Number(pObject.Index); + if(num < 0) num = len - eMath.absInt(num); + + // just to be sure we are in a valid range + if((num >= 0) && (num < len)) + { + if(mode != undefined) + { + var part = ""; + // concatenate up the word + mode = mode.toString().toLowerCase(); + if(mode == "left") + { + num++; + part = s.slice(0,num).join(sep); + } + else if(mode == "right") + { + part = s.slice(pObject.Index).join(sep); + } + this.setOutput(pObject, part); + } + else + { + // use the single word + this.setOutput(pObject, s[num]); + } + } + + if(resultWord == undefined) resultWord = ""; + return resultWord; +} + + +/* +* return a new ID for a key field +* Value of the mapping line: +* String pColumn req the key column +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true +*/ +function iNewID(pObject) +{ + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + if (pObject.Action == undefined) + { + pObject.Action = "I"; + } + + this.setOutput(pObject, util.getNewUUID()); + return true; +} + + +/* +* join the list of columns into the specified target column +* Values of the mapping line: +* Array pList req array containing result set indexes with joinable columns +* String pDelimiter req the delimiter string +* String pColumn req target column name +* +* @param {Object} pObject req the mapping line +* +* @example1: [iJoin, {Source: [3, 5], Delimiter: "\n", Target: "RELATION.ADDRESS"}] +* @example2: [iJoin, {Value: ["{3}", "{5}"], Delimiter: "\n", Target: "RELATION.ADDRESS"}] +* +* @return {Boolean} true +*/ +function iJoin(pObject) +{ + var s = ""; + var len; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + if(pObject.Source != undefined) + len = pObject.Source.length; + if(pObject.Value != undefined) + len = pObject.Value.length; + + for(var i=0; i < len; i++) + { + if (pObject.Source != undefined) + if(this.InputRecord[pObject.Source[i]] != "") + { + if(i > 0 ) s += pObject.Delimiter; + s += this.InputRecord[pObject.Source[i]]; + } + + if(pObject.Value != undefined) + if(this.resolveSymbol(pObject, pObject.Value[i]) != "") + { + if(i > 0 ) s += pObject.Delimiter; + s += this.resolveSymbol(pObject, pObject.Value[i]); + } + } + + this.setOutput(pObject, s); + + + return true; +} + +/* +* executes an sql statement with the data from input result set column in pIndex +* Values of the mapping line: +* Number pIndex req the index into the input result set +* String Command req the sql command (use {0}..{n} to specify source indexes) +* String Alias req the alias name +* String Target req the target column +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true +*/ +function iSql(pObject) +{ + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var sql = this.resolveSymbol(pObject, pObject.Command); + + if (pObject.Target != undefined) + this.setOutput(pObject, db.cell(sql, pObject.Alias)); + else + db.cell(sql, pObject.Alias); + + + return true; +} + + +/* +* inserts or updates an relation entry +* +* @param {Object} pObject req the mapping line +* +* @example: [iInsertUpdate, { Table: "RELATION", Alias: "AO_DATEN", +* Columns: ( {Name: "RELATIONID", Source: 4, Required: true }, +* {Name: "AOTYPE", Value: "2" }, +* {Name: "PERS_ID", Column: "PERS.PERSID" }) } ] +* +* @return {Boolean} true, if insert and update are successful, otherwise false +*/ +function iInsertUpdate(pObject) +{ + var resultUpdate = true; + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + this.setDefaultAction(pObject); + + try + { + var spalten = []; + var typen = []; + var werte = []; + var coldef; + var data_ok = true; // be optimistic ... + var alias = this.Config.AliasTo; + var tableName = this.getTableCase(pObject.Table); + var condition = this.resolveSymbol(pObject, pObject.Condition); + + if(pObject.Action == undefined) // set reasonable defaults for Action, if not specified + { + if(this.Config.ImportCommand == "insert") + pObject.Action = "I"; + else if(this.Config.ImportCommand == "update") + pObject.Action = "U"; + else if(this.Config.ImportCommand == "insert+update") + pObject.Action = "I+U"; + } + + if(this.Config.ImportCommand == "insert+update") + { + if(pObject.Action == "I") + action = "insert"; + else if (pObject.Action == "U") + action = "update"; + else if(pObject.Action == "I+U") + { + //try to find an existing entry + var entryid = db.cell("select count(*) from " + tableName + " where " + condition, alias); + + if(Number(entryid) > 0) + { + //exist, do update + action = "update"; + } + else + { + //no entry, do insert + action = "insert"; + } + } + } + else if (this.Config.ImportCommand == "update") + { + action = "update"; + } + else + { + action = "insert"; + } + + // loop thru the column definitions + for(var i=0; i < pObject.Columns.length; i++) + { + var value = undefined; + coldef = pObject.Columns[i]; + + //be sure, that no keycolumn is pushed in the array, when action like insert + if(coldef.Key != true || (coldef.Key == true && action == "insert")) spalten.push(this.getColumnCase(coldef.Name)); + if(coldef.Key != true || (coldef.Key == true && action == "insert")) typen.push( this.DataType[tableName][this.getColumnCase(coldef.Name)] ); + + if(value == undefined && coldef.Source != undefined) value = this.InputRecord[coldef.Source]; + if(value == undefined && coldef.Value != undefined) value = this.resolveSymbol(coldef, coldef.Value, coldef.Eval); + if(value == undefined && coldef.Key == true && action == "insert") value = util.getNewUUID(); + + //value undefined should not be pushed + //only add value if column was pushed + if(value != undefined && (coldef.Key != true || (coldef.Key == true && action == "insert"))) werte.push(value); + + // do not update data if any required field is empty + if(coldef.Required == true && (value == undefined || value == "")) data_ok = false; + } + + if(data_ok == true) + { + switch(action) + { + case "insert": + this.insertData(tableName, spalten, typen, werte, alias); + break; + case "update": + this.updateData(tableName, spalten, typen, werte, condition, alias); + break; + } + } + } + catch(ex) + { + logging.log(ex); + resultUpdate = false; + } + + return resultUpdate; +} + +/* +* import a timestamp string in a specified format into a date field +* Values of the mapping line: +* String Source req the column index for the current record +* String Target req target column name +* String Format opt The timestamp format, default is YYYY-MM-DD HH:MI:SS +* String Timezone opt The timezone string, default is UTC +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true, if the import of the timestamp was successfull, otherwise false +*/ +function iTimestamp(pObject) +{ + var resultTimestamp = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var fmt = pObject.Format; + var tz = pObject.Timezone; + if(fmt == undefined || fmt == "") fmt = "yyyy-MM-dd HH:mm:ss"; + if(tz == undefined || tz == "") tz = "UTC"; + + var value = ""; + if(pObject.Source != undefined) value = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) value = this.resolveSymbol(pObject, pObject.Value); + try + { + this.setOutput(pObject, datetime.toLong(value, fmt, tz)); + } + catch(ex) + { + logging.log(ex); + resultTimestamp = false; + } + + return resultTimestamp; +} + + +/* +* decode an input entry by searching thru a translation list +* Values of the mapping line: +* String Value -- the input data +* String Target -- the target column +* String List -- the decode list, format: data;replacement;data;replacement..... +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} true, if the the decoding was successfull, otherwise false +*/ +function iDecode(pObject) +{ + var resultDecode = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var wert = ""; + if(pObject.Source != undefined) wert = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) wert = this.resolveSymbol(pObject, pObject.Value); + + var list = pObject.List; + var map = new Object(); + if(list != undefined) + { + // convert decode list into map + list = list.split(";"); + + //is the list complete? + if(list.length % 2 == 0) + { + for(var i=0; i < list.length; i=i+2) + { + map[list[i]] = list[i+1]; + } + + // use map entry to decode + if(wert != "") wert = map[wert]; + + //if not found, set default to empty + if(wert == undefined) wert = ""; + } + else + { + //list is not correct, so wert = "" and log error message + wert = ""; + this.writeLog(this.LogLevels.Error, "[iDecode] List is not correct!"); + } + + // write to output buffer + this.setOutput(pObject, wert); + } + else + { + resultDecode = false; + } + + return resultDecode; +} + + +/* +* save an input in a globalvar +* Values of the mapping line: +* String Value -- the input data +* String Name -- the name for the globalvar +* +* @param {Object} pObject req the mapping line +* +* @example [(iGlobalVar {Value: "{3}", Name: "importLogin"} ) --> $global.importLogin] +* +* @return {void} +*/ +function iGlobalVar(pObject) +{ + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var value = ""; + var name = ""; + if(pObject.Source != undefined) value = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) value = this.resolveSymbol(pObject, pObject.Value); + if(pObject.Name != undefined) name = pObject.Name; + vars.getString("$global." + name, value); +} + + + +/* +* do character set translation. +* basically works like iMove, but allows to specify a conversion map +* that will be used to process the input data. +* conversion map is a map (directionary, associative array, whatever you call it). +* declare a varaible like theMap = new Array(); theMap("a") = "X"; theMap("b") = "z"; etc. ... +* and specify this a sthe value for the Parameter "Map" +* +* Important! Usage of "Method" parameter value "replaceall" requires ADITO online 3.0.3 or above! +* +* Values of the mapping line: +* String Value -- the input data +* String Target -- the target column +* String Map -- the decode map +* String Method -- which Method to use: "js", "replaceall" (default to "js")] +* +* @param {Object} pObject req the mapping line +* +* @return {Boolean} +*/ +function iCharMap(pObject) +{ + var resultMap = true; + + //is any DoIf-condition set? + if (! this.doIfCheck(pObject)) + return true; + + var wert = ""; + if(pObject.Source != undefined) wert = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) wert = this.resolveSymbol(pObject, pObject.Value); + + var map = pObject.Map; + + if(map != undefined) + { + if(pObject.Method == undefined) pObject.Method = "js"; // default to JavaScript + this.writeLog(this.LogLevels.Debug, "[iCharMap] Using mapping method '" + pObject.Method + "' for mapping in iCharMap"); + + switch(pObject.Method) + { + case "js" : + for (var i in map) + { + wert = wert.replace(new RegExp(i, "gi"), map[i]); + } + break; + + case "replaceall" : + wert = text.replaceAll(wert, map); + break; + } + + // write to output buffer + this.setOutput(pObject, wert); + + } + else + { + this.writeLog(this.LogLevels.Warning, "[iCharMap] Map for iCharMap missing or undefined/empty"); + resultMap = false; + } + + return resultMap; +} diff --git a/process/ImporterTest_lib/ImporterTest_lib.aod b/process/ImporterTest_lib/ImporterTest_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..e1204321106696757a47000f28e10cc890d73182 --- /dev/null +++ b/process/ImporterTest_lib/ImporterTest_lib.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>ImporterTest_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/ImporterTest_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/ImporterTest_lib/process.js b/process/ImporterTest_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..342d3997fd4c9de7c26706f028f61cb8f6ac1626 --- /dev/null +++ b/process/ImporterTest_lib/process.js @@ -0,0 +1,74 @@ +import("system.question"); +import("system.text"); +import("system.db"); +import("Importer_lib"); + +function ImporterTest() { + this.startImporter = function (pTestCases) { + var parse = function (pElement, pPath) { + if (pElement.childs == undefined) { + return [{ + id: text.encodeMS(pPath.concat(pElement.name)), + element: pElement }]; + } + var childs = []; + for (let i = 0; i < pElement.childs.length; i++) { + childs.push(parse(pElement.childs[i], pPath.concat(pElement.name))[0]) + } + return [{ + id: text.encodeMS(pPath.concat(pElement.name)), + element: { + name: pElement.name, + description: pElement.description, + childs: childs.map(function (val) { return val.id }) + } + }].concat(childs); + }.bind(this) + + for (let i = 0 ; i < Object.keys(pTestCases).length; i++) { + var res = parse(pTestCases[Object.keys(pTestCases)[i]], []); + if (res.length == 1) { + this.startProcess(res[0].element); + } else { + for (var j = 1; j< res.length; j++) { + var testcase = res[j].element; + this.startProcess(testcase); + } + } + } + } + + this.getBaseImporter = function (pConfig) { + var imp = new Importer(pConfig); + imp.Log = "CONSOLE"; + imp.LogLevel = imp.LogLevels.Debug; + imp.ImportUser = "IMPORTER_TESTER"; + imp.Preview = false; + imp.Debug = true; + imp.LogLevel = imp.LogLevels.Debug; + imp.BatchSize = 50; + imp.skipEmptyValue = false; + if (db.getDatabaseType(pConfig.AliasTo) == db.DBTYPE_MARIADB10) imp.TableCase = imp.Cases.Lower; + return imp; + } + + this.startProcess = function (testCase) { + if (testCase.fn != undefined) { + var cbFn = function (pConfig) { + return this.getBaseImporter(pConfig) + }.bind(this) + + var testResult = testCase.fn.call(testCase, cbFn); + if (testResult instanceof Importer) { + testResult = testResult.process(); + question.showMessage("Successful: " + testCase.name +"\n" + JSON.stringify(testResult, null, " ")) + } else { + question.showMessage("Successful\nResult:\n\n" + testResult) + } + } else { + question.showMessage("No test function defined!", question.WARNING) + } + return true; + } +} + diff --git a/process/Importer_lib/Importer_lib.aod b/process/Importer_lib/Importer_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..0c29a1e97e07715224d80de8a9a3a4f803a5fcf0 --- /dev/null +++ b/process/Importer_lib/Importer_lib.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>Importer_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/Importer_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/Importer_lib/process.js b/process/Importer_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..a693e85cf7ed62c81e6dfed7f74471cd8b759a70 --- /dev/null +++ b/process/Importer_lib/process.js @@ -0,0 +1,1553 @@ +import("system.db"); +import("system.vars"); +import("system.translate"); +import("system.logging"); +import("system.datetime"); +import("system.fileIO"); +import("system.swing"); +import("system.util"); +import("system.text"); +import("ErrorHandling_lib"); +import("ImporterCustomMappingFunctions_lib"); +import("ImporterMappingFunctions_lib"); + +///////////////////////////////////////////////////////////////////// +/// importer module constructor function /// +/// DO NOT TOUCH - use lib_importerCustomMappingFunctions /// +/////////////////////////////////////////////////////////////////// +/* +* the importer class, main object +* @param {String []} pConfig req ( Name, FunktionsArt, Details ) +* +* @version 2.0 +* +* @return {void} +*/ +function Importer(pConfig) +{ + var batchNum = 0; + var batchStart = 0; + var batchList; + var mappingtimer = []; + var exectimer = { + insertData: 0, + updateData: 0, + outbuffer: 0, + logging: 0, + getdata: 0, + updateDecisionTime: 0, + insertUpdateActions: 0, + dbDataModifiedTime: 0, + dbInsertTime: 0, + dbUpdateTime: 0, + dataLoopTime: 0, + mapPerRecordTime: 0, + outputPerRecordTime: 0 + }; + var startTime = datetime.date();//this will be overwriten when starting the importing-process + var stopTime; + var logBuffer = []; + this.insertArray = []; + this.updateArray = []; + + this.LogLevels = { + Minimal: 0, + Error : 1, + Warning : 2, + Info : 3, + Debug : 4, + Preview : 5 + }; + + this.Cases = { + Lower: 0, + Upper: 1, + Unchanged: 2 + }; + + this.Process = ""; + this.Config = pConfig; + this.BatchSize = 50; + this.MaxRows = 0; + this.Preview = false; + this.Debug = false; + this.Log = "CONSOLE"; + this.fileInputCharset = "UTF8"; + this.LogLevel = this.LogLevels.Warning; + this.enableLogBuffer = false; + this.InputRecord = new Array(); + this.OutputRecord = new Object(); + this.ImportUser = "IMPORTER"; + this.FuncBuffer = new Object(); + this.CompleteUpdate = true; + this.ErrorLog = ""; + this.DataType = null; + this.KeyColumn = new Object(); // contains key info as in KeyColumn["tbl.col"] = "I" | "U" | "I+U" + this.UseAOType = true; //default true, so that AOType will be uses (for older systems) + this.UseUUID = true; //default false, so that db.getNewID() instead of util.getNewUUID() would be uses + this.TableCase = this.Cases.Upper; //default is uppercase + this.ColumnCase = this.Cases.Upper; //default is uppercase + this.skipEmptyValue = true; + + this.AttributeCache = undefined; + this.useAttributeCache = false; + this.attributeCacheLoadedSuccessfully = false; + this.loadAttributeCache = function(){ + this.writeLog(this.LogLevels.Info, "[LoadAttributeCache]loading Attibute cache"); + try + { + this.AttributeCache = {}; + let TMP = 0; + var cD = { + attrNameL1: TMP + ,attrNameL2: ++TMP + ,attrNameL3: ++TMP + ,attrIdL1: ++TMP + ,attrIdL2: ++TMP + ,attrIdL3: ++TMP + ,attrObjectL1: ++TMP + ,attrObjectL2: ++TMP + ,attrCompL1: ++TMP + ,attrCompL2: ++TMP + ,attrSqlIdFieldL1: ++TMP + ,attrSqlDisplayFieldL1: ++TMP + ,attrSqlFromFieldL1: ++TMP + ,attrSqlWhereFieldL1: ++TMP + ,attrSqlIdFieldL2: ++TMP + ,attrSqlDisplayFieldL2: ++TMP + ,attrSqlFromFieldL2: ++TMP + ,attrSqlWhereFieldL2: ++TMP + }; + + var sqlStr = "select " + + this.getColumnCase( "A.ATTRNAME, B.ATTRNAME, C.ATTRNAME " + + ",A.ATTRID, B.ATTRID, C.ATTRID " + + ",AO_A.ATTROBJECT, AO_B.ATTROBJECT " + + ",A.ATTRCOMPONENT, B.ATTRCOMPONENT " + + ",A.SQLIDCOLUMN, A.SQLSHOWCOLUMN, A.SQLFROMDEF, A.SQLWHERE " + + ",B.SQLIDCOLUMN, B.SQLSHOWCOLUMN, B.SQLFROMDEF, B.SQLWHERE " + + "") + + " from " + this.getTableCase("ATTR A") + + " left join " + this.getTableCase("ATTR B") + " on " + this.getColumnCase("A.ATTRID") + " = " + this.getColumnCase("B.ATTR_ID") + + " left join " + this.getTableCase("ATTR C") + " on " + this.getColumnCase("B.ATTRID") + " = " + this.getColumnCase("C.ATTR_ID") + + " left join " + this.getTableCase("ATTROBJECT AO_A") + " on " + this.getColumnCase("AO_A.ATTR_ID") + " = " + this.getColumnCase("A.ATTRID") + + " left join " + this.getTableCase("ATTROBJECT AO_B") + " on " + this.getColumnCase("AO_B.ATTR_ID") + " = " + this.getColumnCase("B.ATTRID") + + " where " + this.getColumnCase("A.ATTR_ID") + " is null "//if it has an attr_id it's level 2 an with that ATTR B (or ATTR C) + + ""; + this.writeLog(this.LogLevels.Info, "[LoadAttributeCache]executed select is: " + sqlStr); + var data = db.table(sqlStr, this.Config.AliasTo); + var attributeTree = {}; + for(var i = 0, j = data.length; i < j; i++) + { + let row = data[i]; + if (attributeTree[row[cD.attrNameL1]] == undefined) + attributeTree[row[cD.attrNameL1]] = { + id: row[cD.attrIdL1] + ,component: row[cD.attrCompL1] + ,usedInFrames: {} + ,children: {} + }; + + if (row[cD.attrObjectL1] != "") + attributeTree[row[cD.attrNameL1]].usedInFrames[row[cD.attrObjectL1]] = true; + else + this.writeLog(this.LogLevels.Warning, '[loadAttributeCache]attribute "' + row[cD.attrNameL1] + '" has no ATTROBJECT'); + //selectCombo, init for hybrid-cache (loaded once, when attribute is really needed): + if (row[cD.attrCompL1] == "7" + && attributeTree[row[cD.attrNameL1]].loadedSelectCombo == undefined) + { + attributeTree[row[cD.attrNameL1]].loadedSelectCombo = false; + attributeTree[row[cD.attrNameL1]].selectComboSql = "select " + row[cD.attrSqlIdFieldL1] + + " ," + row[cD.attrSqlDisplayFieldL1] + + " from " + row[cD.attrSqlFromFieldL1] + + (row[cD.attrSqlWhereFieldL1] != "" ? " where " : "") + row[cD.attrSqlWhereFieldL1]; + } + + + if (row[cD.attrNameL2] != "") + { + if (attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]] == undefined) + attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]] = { + id: row[cD.attrIdL2] + ,component: row[cD.attrCompL2] + ,usedInFrames: {} + ,children: {} + }; + + if (row[cD.attrObjectL2] != "") + attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].usedInFrames[row[cD.attrObjectL2]] = true; + else + this.writeLog(this.LogLevels.Warning, '[loadAttributeCache]attribute "' + row[cD.attrNameL1] + "|" + row[cD.attrNameL2] + '" has no ATTROBJECT'); + + //selectCombo, init for hybrid-cache: + if (row[cD.attrCompL2] == "7" + && attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].loadedSelectCombo == undefined) + { + attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].loadedSelectCombo = false; + attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].selectComboSql = "select " + row[cD.attrSqlIdFieldL2] + + " ," + row[cD.attrSqlDisplayFieldL2] + + " from " + row[cD.attrSqlFromFieldL2] + + (row[cD.attrSqlWhereFieldL2] != "" ? " where " : "") + row[cD.attrSqlWhereFieldL2]; + } + + //this nomally happens only if component is combo + if (row[cD.attrNameL3] != "") + { + if (attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].children[row[cD.attrNameL3]] == undefined) + attributeTree[row[cD.attrNameL1]].children[row[cD.attrNameL2]].children[row[cD.attrNameL3]] = { + id: row[cD.attrIdL3] + }; + } + } +// //jetzt muss geprüft werden ob sich die ATTRIDs unterscheiden, wenn ja bedeutet dass, das ein Attributname mehrfach vergeben wurde +// //dann darf keiner der Daten in den Cache geladen werden, dass würde nur zu fehlerhaften daten führen +// var existingAttrid = this.AttributeCache[ attrname ].ATTRID; +// if( existingAttrid != attrid ) +// { +// this.AttributeCache[ attrname ] = null; //cache sicherheitshalber erst freigeben +// this.AttributeCache[ attrname ] = undefined; +// this.writeLog(this.LogLevels.Error, "[LoadAttributeCache] Multiple Attributes were found for attribute: '" + attrname + "'." +// + "\r\nNo Attribute with attrname '" + attrname + "' will be loaded into the cache. "); +// } + } + this.AttributeCache = attributeTree; + this.attributeCacheLoadedSuccessfully = true; + } + catch(ex) + { + this.attributeCacheLoadedSuccessfully = false; + this.writeLog(this.LogLevels.Error, "[LoadAttributeCache]Error while loading Attributes:\r\n" + logging.toLogString(ex) ); + } + if( this.attributeCacheLoadedSuccessfully ) + this.writeLog(this.LogLevels.Info, "[LoadAttributeCache]Loaded Attibute cache successfully"); + else + this.writeLog(this.LogLevels.Info, "[LoadAttributeCache]Failed loading Attibute cache"); + } + + this.recordCounts = {//is initialized with numbers in the this.process-function + total: null + ,skip: null + ,update: null + ,insert: null + ,unchanged: null + }; + Object.defineProperty(this.recordCounts, 'unchanged', { + //"this" is here the passed object + get: function (){ return (this.total - this.insert - this.update - this.skip)} + ,set: function (){throw new Error("importers recordCounts cannot be set; it is read-only")} + }); + + this.process = function() + { + this.mappingLength = this.Config.Mapping.length; + var i; + var t; + var pkid; + var c; + var cond; + var s; + + var pImporter = this; + + this.recordCounts.total = 0; + this.recordCounts.skip = 0; + this.recordCounts.insert = 0; + this.recordCounts.update = 0; + + // Infoobject for Callbackfunction + var recordStack = { + sourceDataSet: [], + insertUpdate: "", + insertData: { + successful: true, + array: [] + }, + updateData: { + successful: true, + array: [] + }, + exception: [], + pId: "" + }; + + var daten; + var skip; + var headercount = 0; + var batchlist = []; + var idcolumn; + var importtype = "S"; // F,S or M for "file", "single" or "multi" batch, default to single batch + batchStart = 0; // current batch start index + var logstring = ""; // contains the log file, if not writing to server log + var requiredTargets = new Object(); // stores the required target columns + // prepare logging stuff + if(this.Log != "" && this.Log != undefined && this.Log.split(".")[0] == "$global") vars.set(this.Log, ""); + if(this.Preview == true) this.LogLevel = this.LogLevels.Preview; + + if (this.enableLogBuffer) + logBuffer = []; + else + logBuffer = ["logBuffer is not enabled; you cannot use getLogMessages(...)"]; + startTime = datetime.date(); + s = datetime.toDate(startTime, translate.text("yyyy-MM-dd HH:mm:ss"), "UTC"); + this.writeLog(this.LogLevels.Minimal, "Started at " + s + " (UTC)"); + logging.log("[IMPORT] Start at " + s + " UTC"); + if(this.Preview) this.writeLog(this.LogLevels.Info, "======== import running in preview mode ========"); + if (this.useAttributeCache) + this.loadAttributeCache(); + this.writeLog(this.LogLevels.Debug, "Building mapping tables."); + // get all column type from all tables of the target alias + this.DataType = this.getDataTypes(this.Config.AliasTo); + // build an object with the data types for each column and + // check for key fields and collect them in parmObject.KeyRecord + for(i=0; i < this.mappingLength; i++) + { + mappingtimer[i] = 0; // init mapping timer + + var target = this.Config.Mapping[i][1]["Target"]; + if(target != undefined && target.substr(0,4).toLowerCase() != "var.") // exclude var storage + { + // collect a required target, if we find one (these columns must not be empty) + requiredTargets[target] = (this.Config.Mapping[i][1]["Required"] == true); + // collect all key columns in an object array + // KeyColumn[table ][column] = action1;action2... + var coldata = target.split("."); + + if(this.Config.Mapping[i][1]["Key"] != undefined) //2009-05-13 TR changed, because on insertcommand "insert" or "update" there can be no Action + { + if(this.KeyColumn[ this.getTableCase(coldata[0]) ] == undefined) this.KeyColumn[ this.getTableCase(coldata[0]) ] = new Object(); + if(this.KeyColumn[ this.getTableCase(coldata[0]) ][ this.getColumnCase(coldata[1]) ] == undefined) this.KeyColumn[ this.getTableCase(coldata[0]) ][ this.getColumnCase(coldata[1]) ] = "key"; + + this.setDefaultAction(this.Config.Mapping[i][1]); + + this.KeyColumn[ this.getTableCase(coldata[0]) ][ this.getColumnCase(coldata[1]) ] = this.KeyColumn[ this.getTableCase(coldata[0]) ][ this.getColumnCase(coldata[1]) ] + ";" + this.Config.Mapping[i][1]["Action"]; + } + } + } + + batchNum = 0; + headercount = this.Config.HeaderLines; + if(headercount == undefined) headercount = 0; + this.writeLog(this.LogLevels.Warning, "Beginning data import."); + // main loop + while(daten = this.getNextBatch(this.Config), daten != null ) + { + var dataLoopTimeStart = datetime.date(); + this.writeLog(this.LogLevels.Info, "Processing batch #" + batchNum); + + // import each row of the current batch + for(var bi=0; bi < daten.length; bi++) + { + recordStack.exception =""; + recordStack.sourceDataSet = daten[bi]; + recordStack.insertData = { + successful: true, + array: [] + } + recordStack.updateData = { + successful: true, + array: [] + } + + // skip empty data rows + if(daten[bi].length == 1 && daten[bi][0] == "") + { + if(this.Debug == true) { + this.writeLog(this.LogLevels.Debug, "Skipping empty row " + bi); + } + continue; + } + + this.recordCounts.total++; + skip = false; + + // skip header rows (usually file import only) + if(bi < headercount) + { + if(this.Preview == true && this.Debug == true) + { + this.writeLog(this.LogLevels.Debug, "Skipping header row " + bi); + } + continue; + } + if (this.Debug) + this.writeLog(this.LogLevels.Debug, "Row " + bi); + + this.InputRecord = daten[bi]; // assign input fields for this record + this.OutputRecord = new Object(); // clear output fields for this record + + var mapRecordMapTimeStart = datetime.date(); + // call mapping functions + for(i=0, ml = this.mappingLength; i < ml; i++) + { + try + { + var fname = this.Config.Mapping[i][0].name;//name is inherited function-property + + if (this.Debug) + this.writeLog(this.LogLevels.Debug, "Calling mapping function " + fname); + // call mapping function and time it + t = datetime.date(); + var mapresult = this.Config.Mapping[i][0].call(this, this.Config.Mapping[i][1]); + t = datetime.date() - t; + mappingtimer[i] += t; + // if the mapping did not succeed, log function and ID value + if(mapresult == false) + { + this.writeLog(this.LogLevels.Error, "Mapping function " + fname + " failed for row " + this.recordCounts.total); + recordStack.exception = "Fehler Zeile: " + this.recordCounts.total + " - Info: Mapping function " + fname + " failed for row " + this.recordCounts.total + " Column: " + this.Config.Mapping[i][1]["Target"]; + skip = true; + } + } + catch(ex) + { + logging.log(ex["rhinoException"] != undefined ? ex["rhinoException"] : ex) + this.writeLog(this.LogLevels.Error, "Exception in mapping function [" + fname + "] for input row " + this.recordCounts.total+ " - " + this.Config.Mapping[i][1]["Target"]); + recordStack.exception = errorHandling.getClearMessage(ex)+ " - Column: " + this.Config.Mapping[i][1]["Target"]; + skip = true; + + } + } + exectimer.mapPerRecordTime += datetime.date() - mapRecordMapTimeStart; + + + if(skip == false) // do we continue with this record? + { + // if a debug callback function has been defined, call it + if(this.DebugCallback != null && typeof(this.DebugCallback) == "function") + { + this.DebugCallback(this.OutputRecord); + } + + + var tt = datetime.date(); + + var outdata = new Object(); + + for(var tbl in this.OutputRecord) // loop thru all output tables + { + for(var col in this.OutputRecord[ tbl ]) // loop thru all output columns + { + if(tbl.toLowerCase() != "var") // exclude var storage + { + for(var action in this.OutputRecord[tbl][col]) + { + if(outdata[tbl] == undefined) outdata[tbl] = ""; + outdata[tbl] = outdata[tbl] + ";" + action; + + // check for required data mappings and set skip again, we check this later + skip = (requiredTargets[tbl + "." + col] == true && (this.OutputRecord[tbl][col][action] == undefined || this.OutputRecord[tbl][col][action] == "")); + } + } + } + } + + tt = datetime.date() - tt; + exectimer["outbuffer"] += tt; + + var outputRecordTimeStart = datetime.date(); + // loop thru all distinct tables, maybe we have more than one record to write + for(t in this.OutputRecord) + { + var spalten = []; + var typen = []; + var werte = []; + + // we do insert-for-new and update-for-existing + if(skip == false && this.Config.ImportCommand == "insert+update") + { + var insertUpdateStart = datetime.date(); + pkid = "0"; // assume we do not find an existing record + + // if no key values have been specified, there is no need to check + // so we do an insert + if(this.KeyColumn[t] != undefined ) + { + // check for existing data row based on key values + cond = this.generateKeyCondition(t); + var sql = "select count(*) from " + t + " where " + cond; + this.writeLog(this.LogLevels.Debug, "Insert/Update decision for table " + t + " based on: " + cond); + pkid = db.cell(sql, this.Config.AliasTo); + } + exectimer["updateDecisionTime"] += (datetime.date() - insertUpdateStart); + + if(t.toLowerCase() != "var") + { + if(this.DataType[t] == undefined) this.writeLog(this.LogLevels.Error, "Table " + t + " not exist in database"); + else + { + for(c in this.OutputRecord[t]) + { + if(this.OutputRecord[t][c]["I+U"] != undefined && (this.OutputRecord[t][c]["I+U"] != "" || !this.skipEmptyValue) && this.OutputRecord[t][c]["I+U"] != null) + { + if(this.DataType[t][c] != undefined) + { + spalten.push(c); + typen.push(this.DataType[t][c]); + werte.push(this.OutputRecord[t][c]["I+U"]); + } + else + this.writeLog(this.LogLevels.Error, "Column " + c + " not exist in table " + t); + } + } + } + } + if(pkid != "0") // exists + { + if(t.toLowerCase() != "var") + { + if(this.DataType[t] == undefined) this.writeLog(this.LogLevels.Error, "Table " + t + " not exist in database"); + else + { + for(c in this.OutputRecord[t]) + { + if(this.OutputRecord[t][c]["U"] != undefined && (this.OutputRecord[t][c]["U"] != "" || !this.skipEmptyValue) && this.OutputRecord[t][c]["U"] != null + && this.OutputRecord[t][c]["I+U"] == undefined) + { + if(this.DataType[t][c] != undefined) + { + spalten.push(c); + typen.push(this.DataType[t][c]); + werte.push(this.OutputRecord[t][c]["U"]); + } + else + this.writeLog(this.LogLevels.Error, "Column " + c + " not exist in table " + t); + } + } + } + } + + if(!this.updateData(t, spalten, typen, werte, cond, this.Config.AliasTo)) skip = true; + } + else + { + if(t.toLowerCase() != "var") + { + if(this.DataType[t] == undefined) this.writeLog(this.LogLevels.Error, "Table " + t + " not exist in database"); + else + { + for(c in this.OutputRecord[t]) + { + if(this.OutputRecord[t][c]["I"] != undefined && this.OutputRecord[t][c]["I"] != "" && this.OutputRecord[t][c]["I"] != null + && this.OutputRecord[t][c]["I+U"] == undefined) + { + if(this.DataType[t][c] != undefined) + { + spalten.push(c); + typen.push(this.DataType[t][c]); + werte.push(this.OutputRecord[t][c]["I"]); + } + else + this.writeLog(this.LogLevels.Error, "Column " + c + " not exist in table " + t); + } + } + } + } + + if(!this.insertData(t, spalten, typen, werte, this.Config.AliasTo)) skip = true; + } + exectimer["insertUpdateActions"] += (datetime.date() - insertUpdateStart); + } + + // we do an insert + if(skip == false && this.Config.ImportCommand == "insert") + { + if(t.toLowerCase() != "var") + { + if(this.DataType[t] == undefined) this.writeLog(this.LogLevels.Error, "Table " + t + " not exist in database"); + else + { + for(c in this.OutputRecord[t]) + { + if(this.DataType[t][c] != undefined) + { + spalten.push(c); + typen.push(this.DataType[t][c]); + werte.push(this.OutputRecord[t][c]["I"]); + } + else + this.writeLog(this.LogLevels.Error, "Column " + c + " not exist in table " + t); + } + } + } + if(!this.insertData(t, spalten, typen, werte, this.Config.AliasTo)) skip = true; + } + + // we do an update? + if(skip == false && this.Config.ImportCommand == "update") + { + if(t.toLowerCase() != "var") + { + if(this.DataType[t] == undefined) this.writeLog(this.LogLevels.Error, "Table " + t + " not exist in database"); + else + { + for(c in this.OutputRecord[t]) + { + if(this.DataType[t][c] != undefined) + { + spalten.push(c); + typen.push(this.DataType[t][c]); + werte.push(this.OutputRecord[t][c]["U"]); + } + else + this.writeLog(this.LogLevels.Error, "Column " + c + " not exist in table " + t); + } + } + } + + // create condition by looking for key fields + cond = this.generateKeyCondition(t); + if(!this.updateData(t, spalten, typen, werte, cond, this.Config.AliasTo)) skip = true; + } + } + exectimer.outputPerRecordTime += datetime.date() - outputRecordTimeStart; + } + + // this is *not* the else-branch for the if above, because we may change the value for "skip" + // even it was false when comparing in the "if" above. so we check this here in a separate + // if to get all occurrences of skipped import rows. + if(skip == true) this.recordCounts.skip++; + //Now insert/update all Columns + if(!skip) + { + var totalDBexecTime = datetime.date(); + let insertSuccessfull = true; + let updateSuccessfull = true; + try + { + if(this.insertArray.length > 0) + { + insertSuccessfull = false; + var startInsert = datetime.date(); + db.inserts(this.insertArray, this.Config.AliasTo); + exectimer.dbInsertTime += datetime.date() - startInsert; + this.recordCounts.insert++; + insertSuccessfull = true; + // insert + recordStack.insertUpdate = "insert"; + recordStack.insertData = { + successful: true, + array: this.insertArray + }; + } + if(this.updateArray.length > 0) + { + updateSuccessfull = false; + var startUpdate = datetime.date(); + db.updates(this.updateArray, this.Config.AliasTo); + exectimer.dbUpdateTime += datetime.date() - startUpdate; + this.recordCounts.update++; + updateSuccessfull = true; + // update + recordStack.insertUpdate = "update"; + recordStack.updateData = { + successful: true, + array: this.updateArray + }; + } + } + catch(ex) + { + this.writeLog(this.LogLevels.Error, "Error at " + (insertSuccessfull ? "" : "Insert") + (updateSuccessfull ? "": "Update") + ":" + logging.toLogString(ex)); + logging.log(ex["rhinoException"] != undefined ? ex["rhinoException"] : ex) + this.recordCounts.skip++; + recordStack.exception = errorHandling.getClearMessage(ex); + + if(this.insertArray.length > 0 && !insertSuccessfull) { + this.writeLog(this.LogLevels.Info, "Insert array: " + JSON.stringify(this.insertArray, null, " ")); + recordStack.insertData = { + successful: false, + array: this.updateArray + }; + } + if(this.updateArray.length > 0 && !updateSuccessfull) { + this.writeLog(this.LogLevels.Info, "Update array: " + JSON.stringify(this.updateArray, null, " ")); + recordStack.updateData = { + successful: false, + array: this.updateArray + }; + } + } + + exectimer.dbDataModifiedTime += datetime.date() - totalDBexecTime; + + //Clear Arrays + this.insertArray = []; + this.updateArray = []; + } + // if a progress callback function has been defined, call it + if(this.ProgressCallback != null && typeof(this.ProgressCallback) == "function") + { + this.ProgressCallback(skip, bi, recordStack, this.Config.AliasFrom, this.Config.Callback.TableFrom, this.Config.Callback.IDColumn, this.Config.Callback.IDColumnIndex); + } + + if(this.MaxRows > 0 && this.recordCounts.total >= this.MaxRows) + { + break; + } + } // end for (row of current batch) + if(this.MaxRows > 0 && this.recordCounts.total >= this.MaxRows) + { + break; + } + exectimer.dataLoopTime += datetime.date() - dataLoopTimeStart; + } + + this.showCounts(); + stopTime = datetime.date(); + this.showTimings(); + s = datetime.toDate(stopTime, translate.text("yyyy-MM-dd HH:mm:ss"), "UTC"); + this.writeLog(this.LogLevels.Minimal, "End at " + s + " UTC"); + logging.log("[IMPORT] End at " + s + " UTC"); + + return { + totalCount: this.recordCounts.total + ,skipCount: this.recordCounts.skip + ,insertCount: this.recordCounts.insert + ,updateCount: this.recordCounts.update + ,unchangedCount: this.recordCounts.unchanged + }; + } + // show timing information + this.showTimings = function(pPrefix) + { + if (pPrefix == undefined) + pPrefix = ""; + + var mappingtotal = 0; + for(var i=0; i < this.Config.Mapping.length; i++) + { + mappingtotal += mappingtimer[i]; + } + if (stopTime == undefined) + this.writeLog(this.LogLevels.Warning, pPrefix + "Total run time till now: " + ((datetime.date() - startTime) / 1000) + " seconds."); + else + this.writeLog(this.LogLevels.Warning, pPrefix + "Total run time: " + ((stopTime - startTime) / 1000) + " seconds."); + + for(var k in exectimer) + { + this.writeLog(this.LogLevels.Info, pPrefix + "Total " + k + " time: " + (exectimer[k] == "0" ? "<1" : exectimer[k]) + " ms "); + } + + this.writeLog(this.LogLevels.Warning, pPrefix + "Total map time: " + (mappingtotal / 1000) + " seconds."); + + for(i=0; i < this.Config.Mapping.length; i++) + { + var fname = this.Config.Mapping[i][0].name;//name is inherited function-property + this.writeLog(this.LogLevels.Info, pPrefix + "Mapping #" + i + " " + fname + ": " + (mappingtimer[i] == "0" ? "<1" : mappingtimer[i]) + " ms "); + } + } + + this.showCounts = function(pPrefix) + { + if (pPrefix == undefined) + pPrefix = ""; + + this.writeLog(this.LogLevels.Minimal, pPrefix + "Total: " + this.recordCounts.total + + ", Skipped: " + this.recordCounts.skip + + ", Inserted: " + this.recordCounts.insert + + ", Updated: " + this.recordCounts.update + + ", Unchanged: " + this.recordCounts.unchanged ); + } + + // this function yields the next batch of data to import. + this.getNextBatch = function(pConfig){ + var tt = datetime.date(); // exec timer + var resultBatch; + var bs; + var daten; + + this.writeLog(this.LogLevels.Debug, "Executing getNextBatch()"); + batchNum++; + + var impmode; + + if(pConfig.DataFunction != undefined) + impmode = "array"; + else if(pConfig.DataFile != undefined) + { + if(pConfig.DataFile.substr(pConfig.DataFile.length-3, pConfig.DataFile.length) == 'xml') //is the file an xml file? then use xml-mode + impmode = "xml"; + else + impmode = "file"; + } + else if(pConfig.XMLObject != undefined) + impmode = "xml"; + else + impmode = "table"; + + + switch(impmode) + { + case "xml": + if(batchNum > 1) return null; // we read files in one sweep + + if(this.Debug == true) this.writeLog("Getting input xml " + pConfig.DataFile, false, 3); + // file import, which is always a single batch operation + importtype = "F"; + var strXML; + + // get xml-string directly or load from file? + if(pConfig.DataFile != "") + strXML = this.getFileContent(pConfig.DataFile, util.DATA_TEXT); //load from file + else + strXML = pConfig.XMLObject; //get xml-string + + if(strXML != "" && strXML != undefined) + { + var xmlData = new XML(strXML); + resultBatch = []; + var xmlArr = []; + + //get every row-element + for each(xmlItem in xmlData.data.row) + { + xmlArr = []; + + for each(xmlItem2 in xmlItem.column) + xmlArr.push(xmlItem2); + + resultBatch.push(xmlArr); + } + } + else + this.writeLog("XML-Data is empty or undefined!", false, 0); + + return resultBatch; + break; + + case "file": + if(batchNum > 1) return null; // we read files in one sweep + if(this.Debug == true) this.writeLog("Reading input file " + pConfig.DataFile, false, 3); + // file import, which is always a single batch operation + importtype = "F"; + + // otherwise, load the file + var filestring = this.getFileContent(pConfig.DataFile, util.DATA_TEXT); + var rs = pConfig.RowSeparator; + if(rs == undefined) rs = "\n"; + var cs = pConfig.ColumnSeparator; + if(cs == undefined) cs = ","; + var sd = pConfig.StringDelimiter; + if(sd == undefined) sd = ""; + + resultBatch = text.parseCSV(filestring, rs, cs, sd); + tt = datetime.date() - tt; + exectimer["getdata"] += tt; + return resultBatch; + break; + + case "array": + bs = this.BatchSize; + if(bs == undefined) bs = 0; + + resultBatch = this.Config.DataFunction.call(this, batchNum, bs); + tt = datetime.date() - tt; + exectimer["getdata"] += tt; + return resultBatch; + break; + + case "table": + // table import + bs = this.BatchSize; + if(bs == undefined) + bs = 0; + + //bs specified: read data in blocks to save memory + if(bs > 0 ) + { + if(this.Config.IdQuery == undefined || this.Config.IdColumn == undefined) + { + //new method for batch-processsing (paging) + daten = db.tablePage(this.Config.DataQuery, this.Config.AliasFrom, batchStart, bs); + batchStart += bs; + + //this happens when all-records % pBlockSize == 0 + //we do not want to call the callback-fn + if (daten.length == 0) + daten = null; + } + else + { + if (this.idList == undefined) + { + this.writeLog(this.LogLevels.Warning, "using \"IdQuery\" and \"IdColumn\" is an obsolte method for getting sql-data in blocks"); + this.idList = this.createIDList(); + } + + //legacy method for batch-processsing + if(batchStart < this.idList.length) // as long as we got something to do + { + batchList = this.idList.slice(batchStart, batchStart + bs); + batchStart += bs; + + // get data of the next batch + var sql = this.Config.DataQuery.replace(/\$\$id/i, idcolumn + " in ('" + batchList.join("','") + "')" ); + daten = db.table(sql, this.Config.AliasFrom); + } + else + { + this.idList = undefined; + daten = null; + } + } + } + else + { + // return complete result set + if(batchNum > 1) + daten = null; + else + daten = db.table(this.Config.DataQuery, this.Config.AliasFrom); + } + tt = datetime.date() - tt; + exectimer["getdata"] += tt; + return daten; + break; + } + } + this.createIDList = function() + { + var tt = datetime.date(); // exectimer + var resIDList; + + if(pConfig.IdQuery != undefined && pConfig.DataQuery != undefined && pConfig.IdColumn != undefined) + { + // set the id column for the batch retrieval + idcolumn = this.Config.IdColumn; + + // set multi-batch import + importtype = "M"; + + // get the list of primary keys + resIDList = db.array(db.COLUMN, pConfig.IdQuery, pConfig.AliasFrom); + tt = datetime.date() - tt; + exectimer["getdata"] += tt; + } + return resIDList; + } + + // @param String pMessage -- die Meldung, die geloggt werden soll + // @param String pLevel + this.writeLog = function(pLevel, pMessage) + { + var tt = datetime.date(); + + // if logging has been "oursourced", just call the callback function and do nothing + if(this.LogCallback != null && typeof(this.LogCallback) == "function") + { + this.LogCallback(pLevel, pMessage); + } + else if(pLevel <= this.LogLevel) // shall we output this message? + { + var logprefix = "[IMPORTER]@" + datetime.toDate(datetime.date(), translate.text("yyyy-MM-dd HH:mm:ss"), "UTC") + " UTC: "; + var logline = logprefix + pMessage; + if (this.enableLogBuffer) + logBuffer.push(logline); + + if(this.Log == "LOGFILE") + { + logging.log(logline); + } + else + { + if(this.Log == "CONSOLE") + { + logging.log(logline); + } + else if(this.Log != "" && this.Log != undefined) + { + // log in globalvar + if(this.Log.split(".")[0].toLowerCase() == "$global") + { + var s = logline + "\r\n"; + vars.set(this.Log, vars.getString(this.Log) + s); + } + } + } + } + + tt = datetime.date() - tt; + exectimer["logging"] += tt; + } + + + // retrieve all log messages currently in log buffer + this.getLogMessages = function(){ + return logBuffer; + } + // return true, if running in client context, false for a server context + this.isClientProcess = function(){ + return vars.getString("$sys.isclient") == "true"; + } + + + this.getFileContent = function(pFilename, pDataType) + { + var resContent; + var cp; + + if(this.isClientProcess() == true) // use doClientIntermediate + { + try + { + resContent = swing.doClientIntermediate(swing.CLIENTCMD_GETDATA, [pFilename, pDataType, this.fileInputCharset]); + } + catch(ex) + { + logging.show(ex) + resContent = ""; + } + } + else // use fileIO + { + try + { + resContent = fileIO.getData(pFilename, util.DATA_TEXT, this.fileInputCharset); + } + catch(ex) + { + logging.log( ex ); + resContent = ""; + } + } + return resContent; + } + + + this.dumpRecord = function(pTable, pColumns, pTypes, pValues, pCondition) + { + var PADDING = "................................"; + var s = ""; + if(pCondition != undefined) s += "update " + pTable + " where " + pCondition; else s += "insert " + pTable; + s += "\n"; + for(var i=0; i < pColumns.length; i++) + { + s += " " + pColumns[i] + PADDING.substr(0, 32- pColumns[i].length) + ": |" + pValues[i] + "|\n"; + } + + s += "\n"; + + return s; + } + + + this.insertData = function(pTable, pColumns, pTypes, pValues, pAlias) + { + var tt = datetime.date(); + var resData = true; + if(this.Preview == false) + { + //better safe than sorry ... + try + { + if(pTable.toLowerCase() != "var") + { + if(this.DataType[pTable] == undefined) this.writeLog(this.LogLevels.Error, "Table " + pTable + " not exist in database"); + else + { + this.writeLog(this.LogLevels.Debug, "INSERT for [" + pAlias + "].[" + pTable + "]"); + this.writeLog(this.LogLevels.Preview, this.dumpRecord(pTable, pColumns, pTypes, pValues)); + //exist already "USER_NEW" and/or "DATE_NEW" in the columns resultset? + var uNew = false; + var dNew = false; + for(var i = 0; i < pColumns.length; i++) + { + if(uNew == false && pColumns[i].toUpperCase() == "USER_NEW") + uNew = true; + + if(dNew == false && pColumns[i].toUpperCase() == "DATE_NEW") + dNew = true; + } + // process audit columns automagically + if(uNew == false && this.DataType[this.getTableCase(pTable)][this.getColumnCase("USER_NEW")] != undefined) + { + pColumns.push(this.getColumnCase("USER_NEW")); + if (pTypes != null) pTypes.push(this.DataType[this.getTableCase(pTable)][this.getColumnCase("USER_NEW")]); + pValues.push(this.ImportUser); + } + if(dNew == false && this.DataType[pTable][this.getColumnCase("DATE_NEW")] != undefined) + { + pColumns.push(this.getColumnCase("DATE_NEW")); + if(pTypes != null) pTypes.push(this.DataType[this.getTableCase(pTable)][this.getColumnCase("DATE_NEW")]); + pValues.push(datetime.date()); + } + + this.insertArray.push([this.getTableCase(pTable), pColumns, pTypes, pValues, pAlias]); + } + } + } + catch(ex) + { + this.writeLog(this.LogLevels.Error, "Exception at insertData for record: " + getRecordString(pColumns, pValues)); + logging.log(ex); + resData = false; + } + } + else + { + this.writeLog(this.LogLevels.Preview, "Insert into table " + pTable); + this.writeLog(this.LogLevels.Preview, this.dumpRecord(pTable, pColumns, pTypes, pValues)); + } + + tt = datetime.date() - tt; + exectimer["insertData"] += tt; + return resData; + } + + this.updateData = function(pTable, pColumns, pTypes, pValues, pCondition, pAlias) + { + var tt = datetime.date(); + var theCols; + var theTypes; + var theValues; + + var resultData = true; + if(this.Preview == false) + { + try + { + if(pTable.toLowerCase() != "var") + { + if(this.DataType[pTable] == undefined) this.writeLog("Table " + pTable + " not exist in database"); + else + { + this.writeLog(this.LogLevels.Debug, "UPDATE for alias [" + pAlias + "].[" + pTable + "]"); + this.writeLog(this.LogLevels.Preview, this.dumpRecord(pTable, pColumns, pTypes, pValues, pCondition)); + + if (this.CompleteUpdate == false) // check for changed database values and use only changed columns for update + { + var uColumns = new Array(); + var uValues = new Array(); + var uTypes = new Array(); + var oldValues = db.array(db.ROW, "select " + pColumns.join(", ") + " from " + pTable + " where " + pCondition, pAlias); + for (var dsi = 0; dsi < oldValues.length; dsi++) + { + if (oldValues[dsi] != pValues[dsi]) + { + //get the values from the validate target + uColumns.push(pColumns[dsi]); + uValues.push(pValues[dsi]); + if (pTypes != null) uTypes.push(pTypes[dsi]); + } + } + theCols = uColumns; + theTypes = uTypes; + theValues = uValues; + } + else // update all columns, so use default column set + { + theCols = pColumns; + theTypes = pTypes; + theValues = pValues; + } + + if(theCols.length > 0) + { + //show the old and the new data only if anything changed + this.writeLog(this.LogLevels.Preview, "New Data: " + pValues.join("|")); + if (oldValues != undefined) + this.writeLog(this.LogLevels.Preview, "Old Data: " + oldValues.join("|")); + } + + var minchanges = 0; + var dEdit = false; + var uEdit = false; + + for(var i = 0; i < theCols.length; i++) + { + if(uEdit == false && theCols[i].toUpperCase() == "USER_EDIT") + uEdit = true; + + if(dEdit == false && theCols[i].toUpperCase() == "DATE_EDIT") + dEdit = true; + } + + + // process audit columns automagically + if(uEdit == false && this.DataType[this.getTableCase(pTable)][this.getColumnCase("USER_EDIT")] != undefined) + { + theCols.push(this.getColumnCase("USER_EDIT")); + if(pTypes != null) theTypes.push(this.DataType[this.getTableCase(pTable)][this.getColumnCase("USER_EDIT")]); + theValues.push(this.ImportUser); + minchanges++; + } + if(dEdit == false && this.DataType[this.getTableCase(pTable)][this.getColumnCase("DATE_EDIT")] != undefined) + { + theCols.push(this.getColumnCase("DATE_EDIT")); + if(pTypes != null) theTypes.push(this.DataType[this.getTableCase(pTable)][this.getColumnCase("DATE_EDIT")]); + theValues.push(datetime.date()); + minchanges++; + } + if(this.CompleteUpdate == false) + { + if(theCols.length > minchanges) + { + this.updateArray.push([this.getTableCase(pTable), theCols, theTypes, theValues, pCondition, pAlias]); + } + } + else + { + this.updateArray.push([this.getTableCase(pTable), theCols, theTypes, theValues, pCondition, pAlias]); + } + } + } + } + catch(ex) + { + resultData = false; + this.writeLog(this.LogLevels.Error, "Exception at updateData for record: " + getRecordString(pColumns, pValues)); + logging.log(ex); + } + } + else + { + this.writeLog(this.LogLevels.Preview, "Update table " + pTable); + this.writeLog(this.LogLevels.Preview, this.dumpRecord(pTable, pColumns, pTypes, pValues, pCondition)); + } + tt = datetime.date() - tt; + exectimer["updateData"] += tt; + return resultData; + } + + // set default action for a mapping call, if action has not been specified + this.setDefaultAction = function(pObject) + { + if(pObject.Action == undefined) // set reasonable defaults for Action, if not specified + { + if(this.Config.ImportCommand == "insert") pObject.Action = "I"; + else + if(this.Config.ImportCommand == "update") pObject.Action = "U"; + else + if(this.Config.ImportCommand == "insert+update") pObject.Action = "I+U"; + } + } + + // ATTENTION!! This is the *new* version and not the same as resolveSymbols!! + // + // resolve symbol to get import data + // may contain literals string and {#} and {tbl.col} symbols + // if undefined or empty expression is provided, return an empty string + this.resolveSymbol = function(pObject, pExpression, pEvalScript) + { + var expr; + + if((pExpression != undefined) && (pExpression != "")) + { + var inp = this.InputRecord; + var out = this.OutputRecord; + var cCase = this.ColumnCase; + var tCase = this.TableCase; + var uCase = this.Cases.Upper; + + this.setDefaultAction(pObject); + var obj = pObject; + + let self = this; + + expr = pExpression.toString(); + expr = expr.replace(/\{([._a-zA-Z0-9]+)\}/ig, + function(pMatch, pNumber) + { + if(isNaN(Number(pNumber))) + { + var varname = pNumber.split("."); + var res; + + // action verwenden, wenn keine da => importcommand auslesen + var action = obj.Action; + if(out[ self.getTableCase(varname[0]) ] != undefined && out[ self.getTableCase(varname[0]) ][ self.getColumnCase(varname[1]) ] != undefined) + { + switch(obj.Action) + { + case "I": + res = out[ self.getTableCase(varname[0]) ][ self.getColumnCase(varname[1]) ]["I"]; + break; + case "U": + res = out[ self.getTableCase(varname[0]) ][ self.getColumnCase(varname[1]) ]["U"]; + break; + case "I+U": + + res = out[ self.getTableCase(varname[0]) ][ self.getColumnCase(varname[1]) ]["U"]; //2009-06-16 TR + if(res == undefined || res == "") res = out[ self.getTableCase(varname[0])][ self.getColumnCase(varname[1]) ]["I"]; //2009-06-16 TR + + break; + } + } + else // varname does not exist as a property of out + { + res = undefined; + } + if(res == undefined) res = ""; // blank out NULL values + + //transform ' to \\u0027 which is in eval a \u0027; transform " to \\u0022 which is in eval a \u0022 + //needed for eval like: "'3'2' != ''" + if(pEvalScript) + return res.replace("'", "\\\\u0027", "g").replace("\"", "\\\\u0022", "g"); + else + return res; + } + else + { + //transform ' to \\u0027 which is in eval a \u0027; transform " to \\u0022 which is in eval a \u0022 + //needed for eval like: "'3'2' != ''" + if(pEvalScript) + return inp[Number(pNumber)].replace("'", "\\\\u0027", "g").replace("\"", "\\\\u0022", "g"); + else + return inp[Number(pNumber)]; + } + } ); + if(pEvalScript == true) + expr = eval(expr); + } + else + { + expr = ""; + } + return expr; + } + + // read column type information for all columns in all tables of the alias specified + this.getDataTypes = function(pAlias) + { + var tables = db.getTables(pAlias); + var dataTypes = new Object(); + for(var i=0; i < tables.length; i++) + { + if (tables[i] != "trace_xe_action_map" && tables[i] != "trace_xe_event_map") + { + var cols = db.getColumns(tables[i], pAlias); + var types = db.getColumnTypes(tables[i], cols, pAlias); + dataTypes[ tables[i] ] = new Object(); // create sub-object to hold columns + for(var j=0; j < cols.length; j++) dataTypes[tables[i]][cols[j]] = types[j]; + } + } + return dataTypes; // return object with type information + } + + // sets the output record buffer according to "Action" performed + this.setOutput = function(pObject, pValue) + { + try + { + var target = pObject.Target.split("."); + this.setDefaultAction(pObject); + // make sure we do have an output buffer + if(this.OutputRecord[ this.getTableCase(target[0]) ] == undefined) this.OutputRecord[ this.getTableCase(target[0]) ] = new Object(); + if(this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ] == undefined) this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ] = new Object(); + switch(pObject.Action) + { + case "I": + this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["I"] = pValue; + break; + case "U": + this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["U"] = pValue; + break; + case "I+U": + this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["I"] = pValue; //2009-06-16 TR + this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["U"] = pValue; //2009-06-16 TR + this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["I+U"] = pValue; //2009-06-16 TR + break; + } + } + catch(ex) + { + this.writeLog(this.LogLevels.Error, "Property <Target> not set for mapping object!"); + logging.log(ex); + logging.log("[IMPORTER] Property <Target> not set for mapping object!"); + } + } + // get the content of the output record buffer according to "Action" performed + this.getOutput = function(pObject, pTarget) + { + var target; + var action; + + target = pTarget.split("."); + if(pObject != undefined) action = pObject.Action; else action = undefined; + if(action == undefined) // set reasonable defaults for Action, if not specified + { + if(this.Config.ImportCommand == "insert") action = "I"; + else + if(this.Config.ImportCommand == "update") action = "U"; + else + if(this.Config.ImportCommand == "insert+update") action = "I+U"; + } + var resAction; + switch(action) + { + case "I": + resAction = this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1])]["I"]; + break; + case "U": + resAction = this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["U"]; + break; + case "I+U": + resAction = this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["U"]; + if(resAction == undefined || resAction == "") resAction = this.OutputRecord[ this.getTableCase(target[0]) ][ this.getColumnCase(target[1]) ]["I"]; + break; + } + if(resAction == undefined) resAction = ""; // blank out undefined + return resAction; + } + + + // generates condition clause (without "WHERE") to check for existing key column values + this.generateKeyCondition = function(pTable) + { + var whereclause = ""; + for(var col in this.KeyColumn[pTable]) + { + var tmp = this.KeyColumn[pTable][col]; // contains I | I;U | I+U + if(tmp != "") + { + var value = this.OutputRecord[pTable][col]["U"]; + if(value == undefined || value == "") + value = this.OutputRecord[pTable][col]["I+U"]; + if(value == undefined || value == "") + value = this.OutputRecord[pTable][col]["I"]; + + // we cannot distinguish between an empty string and NULL in Jdito, + // so if get an empty string for the value, we do a check for empty string OR NULL + if(value == "") + { + whereclause += " and (" + col + " = '' OR " + col + " is null)"; + } + else + { + whereclause += " and " + col + " = '" + db.quote(value, this.Config.AliasTo) + "'"; + } + } + } + // remove leading "and" from where clause + if(whereclause.substr(0, 4) == " and") whereclause = whereclause.substr(4, whereclause.length); + + return whereclause; + } + /* + * yield a string representation of the record in pColumns and pValues for logging + * purposes and debugging. + * + * @param {String[]} pColumns req columns that are represented + * @param {String[]} pValues req values that are represented + * + * @return {String} + */ + function getRecordString(pColumns, pValues) + { + var s = ""; + for(var i=0; i < pColumns.length; i++) + { + var v = ""; + if(pValues[i]) v = pValues[i].toString(); + if(v.length > 40) v = v.substr(0,39) + "..."; + s += " COL: " + pColumns[i] + ": " + v + "\n"; + } + + return s; + } + + //Converts a string with the tablename in upper or lower case + this.getTableCase = function(pName) + { + if(this.TableCase == this.Cases.Upper) + return pName.toUpperCase(); + else if (this.TableCase == this.Cases.Lower) + return pName.toLowerCase(); + else + return pName; + } + + //Converts a string with the columnname in upper or lower case + this.getColumnCase = function(pName) + { + if (this.ColumnCase == this.Cases.Upper) + return pName.toUpperCase(); + else if (this.ColumnCase == this.Cases.Lower) + return pName.toLowerCase(); + else + return pName; + } + + this.doIfCheck = function(pObject) + { + if (pObject.DoIf == undefined) return true; + + if (typeof(pObject.DoIf) == "function") + { + var expr; + if(pObject.Source != undefined) expr = this.InputRecord[pObject.Source]; + if(pObject.Value != undefined) expr = this.resolveSymbol(pObject, pObject.Value, pObject.Eval); + if(pObject.Map != undefined && pObject.Index) expr = pObject.Map[this.resolveSymbol(pObject, pObect.Index, pObject.Eval)]; + return pObject.DoIf.call(this, this.InputRecord, expr, pObject); + } + + return (this.resolveSymbol(pObject, pObject.DoIf, true)); + } +} + +/** + * Data handler for csv paging + * + * @param pCurrentBatchNum + * @param pBatchSize + */ +function batchCsvLoad(pCurrentBatchNum, pBatchSize) +{ + if (this.BatchStart == undefined) this.BatchStart = 0; + if (this.Config.RowSeparator != undefined) this.writeLog(this.LogLevels.Warning, "'RowSeparator' option is currently not supported!") + var dataStr = ""; + var fn = this.Config.DataFile; + var bs = pBatchSize; + var process = this.Config.Processname; + if (!fileIO.exists(fn)) + throw new Error(translate.withArguments("file '%0' does not exist or you have got no permission on this file", [fn])); + else if (!fileIO.canRead(fn)) + throw new Error(translate.withArguments("file '%0' cannot be read", [fn])); + const MAX_LOOP_SIZE = this.Config.BatchSize; + do + { + dataStr = null; + var loopSize = MAX_LOOP_SIZE; + try + { + dataStr = fileIO.getBulkData(fn, util.DATA_TEXT, this.fileInputCharset, this.BatchStart, bs); + } + catch(ex) + { + break; + } + this.BatchStart += bs; + whileMultiline: while (dataStr.slice(-2) != "\r\n" && (dataStr.slice(-1) == "\n" || dataStr.slice(-1) == "\r"))//we have to load more Data + { + try + { + dataStr += fileIO.getBulkData(fn, util.DATA_TEXT, this.fileInputCharset, this.BatchStart, 1); + } + catch(ex) + { + this.writeLog(this.LogLevels.Warning, ex); + break; + } + this.BatchStart++; + if (loopSize == 0) + { + this.writeLog(this.LogLevels.Error, "MAX_LOOP_SIZE reached; Abort"); + break whileMultiline; + } + loopSize--; + } + if (dataStr.slice(-2) == "\r\n") + dataStr = dataStr.slice(0, -2); + var rs = this.Config.RowSeparator; + if(rs == undefined) rs = "\r\n"; + var cs = this.Config.ColumnSeparator; + if(cs == undefined) cs = ";"; + var sd = this.Config.StringDelimiter; + if(sd == undefined) sd = ""; + return text.parseCSV(dataStr, rs, cs, sd); + } + while(dataStr != null); + return null; +} + + diff --git a/process/_test_importer/_test_importer.aod b/process/_test_importer/_test_importer.aod new file mode 100644 index 0000000000000000000000000000000000000000..054240e430569eceaeff6e6c6e1a7731a30a849d --- /dev/null +++ b/process/_test_importer/_test_importer.aod @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>_test_importer</name> + <title>Importer</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/_test_importer/process.js</process> + <variants> + <element>EXECUTABLE</element> + </variants> +</process> diff --git a/process/_test_importer/process.js b/process/_test_importer/process.js new file mode 100644 index 0000000000000000000000000000000000000000..ad51271dd609329d2675e3ecc01b8f153efabf40 --- /dev/null +++ b/process/_test_importer/process.js @@ -0,0 +1,444 @@ +import("system.text"); +import("system.logging"); +import("Util_lib"); +import("ImporterTest_lib"); +import("Importer_lib"); +import("ImporterMappingFunctions_lib"); + +var testfunctions = { + iKeyword: { + "insertKeywordNew": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeyword, {Container: 0, Keyword: 1}]] + } + return runFn(config); + }, + "insertKeywordContainer": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeyword, {Container: 0, Keyword: 1}]] + } + return runFn(config); + }, + "insertKeywordSame": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeyword, {Container: 0, Keyword: 1}]] + } + return runFn(config); + }, + "insertKeywordMissing": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeyword, {Container: 0, Keyword: 1}]] + } + return runFn(config); + } + }, + iAttribute: { + "insertAttributeNew": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iAttribute, {Attribute: 0, AType: 1}]] + } + return runFn(config); + }, + "insertAttributeUsage": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iAttribute, {Attribute: 0, AType: 1, OType: 2}]] + } + return runFn(config); + }, + "insertAttributeRelation": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iAttribute, {Attribute: 0, AType: 1, OType: 2, OID: 3, Value: 4}]] + } + return runFn(config); + }, + "insertAttributeMissing": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iAttribute, {Attribute: 0, AType: 1}]] + } + return runFn(config); + }, + "insertAttributeSame": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iAttribute, {Attribute: 0, AType: 1, OType: 2, OID: 3, Value: 4}]] + } + return runFn(config); + } + }, + iKeywordAttribute: { + "insertKeywordAttributeNew": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeywordAttribute, {Attribute: 0, AType: 1, Container: 2}]] + } + return runFn(config); + }, + "insertKeywordAttributeRelation": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeywordAttribute, {Attribute: 0, AType: 1, Container: 2, Keyword: 3, Value: 4}]] + } + return runFn(config); + }, + "insertKeywordAttributeMissing": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iKeywordAttribute, {Attribute: 0, AType: 1, Container: 2, Keyword: 3, Value: 4}]] + } + return runFn(config); + } + }, + iComm: { + "insertCommNew": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iComm, {Address: 0, Medium: 1, ContactID: 2}]] + } + return runFn(config); + }, + "insertCommMissing": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iComm, {Address: 0, Medium: 1, ContactID: 2}]] + } + return runFn(config); + } + }, + iActivityLink: { + "insertActivityLinkNew": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iActivityLink, {ActivityID: 0, OID: 1, OType: 2}]] + } + return runFn(config); + }, + "insertActivityLinkMissing": function(runFn) { + var config = { + AliasTo: "Data_alias", + DataFunction: function(pBatchNum, pBatchSize) { + if (pBatchNum > 1) return null; + return this.fixtures; + }.bind(this), + ImportCommand: "insert+update", + Mapping: [[iActivityLink, {ActivityID: 0, OID: 1, OType: 2}]] + } + return runFn(config); + } + } +} + +var fixtures = { + iKeyword: { + "insertKeywordNew": function() { + return [ + ["ContainerTest1", "KeywordTest1"] + ]; + }, + "insertKeywordContainer": function() { + return [ + ["ContainerTest1", "KeywordTest2"], + ["ContainerTest1", "KeywordTest3"], + ["ContainerTest1", "KeywordTest4"], + ["ContainerTest1", "KeywordTest5"] + ]; + }, + "insertKeywordSame": function() { + return [ + ["ContainerTest1", "KeywordTest1"], + ["ContainerTest1", "KeywordTest2"], + ["ContainerTest1", "KeywordTest3"], + ]; + }, + "insertKeywordMissing": function() { + return [ + ["", "KeywordTest2"], + [1234, ""], + ]; + } + }, + iAttribute: { + "insertAttributeNew": function() { + return [ + ["TestString", $AttributeTypes.TEXT.toString()], + ["TestCombo1.TestCombo2.TestCombo3", $AttributeTypes.COMBOVALUE.toString()], + ["TestBool", $AttributeTypes.BOOLEAN.toString()], + ["TestNum", $AttributeTypes.NUMBER.toString()] + ]; + }, + "insertAttributeUsage": function() { + return [ + ["TestUsage1", $AttributeTypes.NUMBER.toString(), "Organisation"], + ["TestUsage1", $AttributeTypes.NUMBER.toString(), "Person"] + ]; + }, + "insertAttributeRelation": function() { + return [ + ["TestRel1", $AttributeTypes.TEXT.toString(), "Person", "666", "six hundred sixty six"], + ["TestRel1", $AttributeTypes.TEXT.toString(), "Person", "777", "seven hundred seventy seven"], + ["TestRel2", $AttributeTypes.NUMBER.toString(), "Organisation", "888", 888], + ["TestRel2", $AttributeTypes.NUMBER.toString(), "Organisation", "999", 999] + ]; + }, + "insertAttributeMissing": function() { + return [ + ["", $AttributeTypes.TEXT.toString()], + ["TestNum", ""], + ["", 234] + ]; + }, + "insertAttributeSame": function() { + return [ + ["TestRel1", $AttributeTypes.TEXT.toString(), "Person", "666", "six hundred sixty four"], + ["TestRel1", $AttributeTypes.TEXT.toString(), "Person", "777", "seven hundred seventy five"] + ]; + } + }, + iKeywordAttribute: { + "insertKeywordAttributeNew": function() { + return [ + ["KeyAttribute1", $AttributeTypes.TEXT.toString(), "ContainerTest2"], + ["KeyAttribute2", $AttributeTypes.NUMBER.toString(), "ContainerTest2"], + ["KeyAttribute3", $AttributeTypes.TEXT.toString(), "ContainerTest3"] + ]; + }, + "insertKeywordAttributeRelation": function() { + return [ + ["KeyAttRel1", $AttributeTypes.TEXT.toString(), "ContainerTest2", "KeywordTest6", "test value"], + ["KeyAttRel2", $AttributeTypes.TEXT.toString(), "ContainerTest2", "KeywordTest7", "test value"], + ["KeyAttribute2", $AttributeTypes.NUMBER.toString(), "ContainerTest1", "KeywordTest8", 888] + ] + }, + "insertKeywordAttributeMissing": function() { + return [ + ["KeyAttRel", 7, 1], + ["KeyAttRel", "My Format", "wrong format"], + [] + ] + } + }, + iComm: { + "insertCommNew": function() { + return [ + ["TestAddress1", "1", "TestContact1"], + ["TestAddress2", "2", "TestContact1"] + ]; + }, + "insertCommMissing": function() { + return [ + ["TestAddress", ""], + ["", 2, ""] + ]; + } + }, + iActivityLink: { + "insertActivityLinkNew": function() { + return [ + ["TestActivityId", "rowTest1", "Person"], + ["TestActivityId", "rowTest2", "Organisation"], + ["TestActivityId", "rowTest2", "Person"] + ]; + }, + "insertActivityLinkMissing": function() { + return [ + ["", "rowTest1", "Person"], + ["ActivityId", 6293, "Organisation"] + ]; + } + } +} + +var testCases = [ + { + name: "Keyword Test", + childs: [ + { + name: "Import new keywords", + fn: testfunctions.iKeyword.insertKeywordNew, + fixtures: fixtures.iKeyword.insertKeywordNew() + }, + { + name: "Import keywords with already existing container", + fn: testfunctions.iKeyword.insertKeywordContainer, + fixtures: fixtures.iKeyword.insertKeywordContainer() + }, + { + name: "Import already existing keywords", + fn: testfunctions.iKeyword.insertKeywordSame, + fixtures: fixtures.iKeyword.insertKeywordSame() + }, + { + name: "Import keywords with missing/wrong values", + fn: testfunctions.iKeyword.insertKeywordMissing, + fixtures: fixtures.iKeyword.insertKeywordMissing() + } + + ] + }, + { + name: "Attribute Test", + childs: [ + { + name: "Import new attributes", + fn: testfunctions.iAttribute.insertAttributeNew, + fixtures: fixtures.iAttribute.insertAttributeNew() + }, + { + name: "Import new attributes with usage", + fn: testfunctions.iAttribute.insertAttributeUsage, + fixtures: fixtures.iAttribute.insertAttributeUsage() + }, + { + name: "Import new attributes with usage and relation", + fn: testfunctions.iAttribute.insertAttributeRelation, + fixtures: fixtures.iAttribute.insertAttributeRelation() + }, + { + name: "Import attributes with missing/wrong values", + fn: testfunctions.iAttribute.insertAttributeMissing, + fixtures: fixtures.iAttribute.insertAttributeMissing() + }, + { + name: "Import already existing attributes", + fn: testfunctions.iAttribute.insertAttributeSame, + fixtures: fixtures.iAttribute.insertAttributeSame() + } + ] + }, + { + name: "Keyword Attribute Test", + childs: [ + { + name: "Import new keyword attributes", + fn: testfunctions.iKeywordAttribute.insertKeywordAttributeNew, + fixtures: fixtures.iKeywordAttribute.insertKeywordAttributeNew() + }, + { + name: "Import keyword attribute relations", + fn: testfunctions.iKeywordAttribute.insertKeywordAttributeRelation, + fixtures: fixtures.iKeywordAttribute.insertKeywordAttributeRelation() + }, + { + name: "Import keyword attributes with missing/wrong values", + fn: testfunctions.iKeywordAttribute.insertKeywordAttributeMissing, + fixtures: fixtures.iKeywordAttribute.insertKeywordAttributeMissing() + } + ] + }, + { + name: "Communication Test", + childs: [ + { + name: "Import new communication entries", + fn: testfunctions.iComm.insertCommNew, + fixtures: fixtures.iComm.insertCommNew() + }, + { + name: "Import communication entries with missing/wrong values", + fn: testfunctions.iComm.insertCommMissing, + fixtures: fixtures.iComm.insertCommMissing() + } + ] + }, + { + name: "Activitylink Test", + childs: [ + { + name: "Import new activity link entries", + fn: testfunctions.iActivityLink.insertActivityLinkNew, + fixtures: fixtures.iActivityLink.insertActivityLinkNew() + }, + { + name: "Import activity link entries with missing/wrong values", + fn: testfunctions.iActivityLink.insertActivityLinkMissing, + fixtures: fixtures.iActivityLink.insertActivityLinkMissing() + } + ] + } +] + + +var impTest = new ImporterTest(); +impTest.startImporter(testCases); \ No newline at end of file