DATAmaestro Scripts

Related tickets : https://pepite.atlassian.net/browse/DATMA-2916



Multiple functions
/** * –––––––––– * | Jira | * –––––––––– // Please mention on ticket DATMA-2916 when you use this script, and describe your Use Case a bit. /** * –––––––––– * | Config | * –––––––––– */ var baseDir = VirtualPath.parse('Valtris.be/INTENSITES_MOTEURS'); var sourceDir = baseDir; var destinationDir = baseDir.resolve('computed', 'Données nettoyées'); var scriptName = "My script name" var scriptHost = 'https://test.mydatamaestro.com/' //------------------------------------------------ //CHOOSE TAG FILTER RULE (3 methods) /** * @type {FilterTagRule} */ var baseTagFilterRule = { type: 'browse', wildcard: '*.INTENSITE', //Check sur une string, * = N'importe quelle string sourceDir: sourceDir // can add FullPath (true, false) }; // OR define by list /** * @type {FilterTagRule} */ // var _baseTagFilterRule = { // type: 'list', // tags:[tag1Path, tag2Path] // }; //OR BASED on a function // var baseTagFilterRule = { // type: 'browse', // predicate: myFunction // sourceDir: sourceDir // }; //------------------------------------------------ //Do the mapping for each individual computed tag //Modify this function to give all inputs as arguments /** * @type {(tag:JavaVirtualPath, index:number)=>JavaVirtualPath[]} */ var getInputTagsRule = function (intensiteTag) { var parent = intensiteTag.getParent(); var name = intensiteTag.getLastToken(); var nextName = name + '_NOMINALE'; return [intensiteTag, parent.resolve(nextName)]; }; /** * @type {(tag:JavaVirtualPath, index:number)=>TagCreateConfig} */ var outputTagGetter = function (intensiteTag) { var name = intensiteTag.getLastToken(); var nextName = name + '_NETTOYEE'; // var meta= tagConnection.getTagMetadata(intensiteTag) // return createConfigFromTagPath(destinationDir.resolve(nextName),meta); //copy source tag metadata return createConfigFromTagPath(destinationDir.resolve(nextName), {}); //open {} to add parameter like: {isNumeric:false, unit: "ms", title:"", classifier} //Can open {} do ctrl space and scroll down to all the different metaData properties }; /** *@type {ComputeTagFn} */ var customPartagedFunction = function (values, time, lastValue, lastTime) { //Values are in the order defined in getInputTagsRule --> Access with values[0], values[1]... return null; }; /** * @type {boolean} */ var isOptimistic = true; //TAG VALIDY MAP /** * @type {Optional<Optional<number>[]>} */ var validityTagMap = null; //var validityTagMap = [1000,1000] //in MS tag Validy Map is defined identical for all same tag /** * ––––––––––– * | Process | * ––––––––––– */ putTagCreationConfigContext({ isMetadataOwner: true, //False don't update metadata if exist scriptFolder: baseDir, scriptHost: scriptHost, scriptName: scriptName }); function main() { var baseTags = getTags(baseTagFilterRule); var outputTags = []; var outputTagConfigs = []; var inputTags = map(baseTags, function (baseTag, index) { var outputTagConfig = outputTagGetter(baseTag, index); outputTags.push(outputTagConfig.tagPath); outputTagConfigs.push(outputTagConfig); return getInputTagsRule(baseTag, index); }); ensureTagConfigExist(outputTagConfigs); useComputedMultipleTag(inputTags, outputTags, customPartagedFunction, isOptimistic, validityTagMap); } //#region Imports /** * ---------- * | Import | * ---------- */ //#region ../../TagScriptLibrary/CommonLibrary/URL.js // An Helper object to keep information with the URL and the fetch request parameters /** * @template T * @typedef {{ * getValue():T, * getValueAsString():string, * resolved():T, * then<U>(callback:(next:T)=>U):U * }} PepitePromise */ /** * @typedef {{ok:boolean, status:number,statusText:string}} PepiteResponseCommon * @typedef { PepiteResponseCommon & { * ok:false, * json():null, * text():null * }} PepiteResponseFailed * @typedef {{ * ok:true, * status:200, * statusText:"ok" * text():PepitePromise<string>, * json():PepitePromise<unknown> * }} PepiteResponseSuccess * @typedef {PepiteResponseFailed | PepiteResponseSuccess} PepiteResponse */ /** * @typedef {"http"|"https"} Protocol * @typedef {{[key:string]:string}} QueryParams * @typedef { Stringifyable & * { * resolvePath(path:string | string[]):FetchURL, * clone():FetchURL, * addParameter(key:string, value:string):FetchURL, * addParameters(obj:any):FetchURL, * completeFetchRequestParams(partialFetchRequest:Partial<RequestInit>):FetchURL, * changeFetchRequestParams(fetchRequestParams:RequestInit):FetchURL, * useFetch():PepitePromise<PepiteResponse>, * addHeader(key:string, value:any):FetchURL, * addHeaders(map:{[key:string]:any}):FetchURL, * changeBody(body:any):FetchURL * }} URLFN * @typedef {{protocol:Protocol, host:string, port:number, path:PathContainer, queryParams:QueryParams, fetchRequestParams:RequestInit} & URLFN} FetchURL */ /** * @typedef {{tokens:string[], isAbsolute:boolean, clone():PathContainer, resolve(token:string|string[]):PathContainer, toAbsolute():PathContainer, toRelative():PathContainer} & Stringifyable} PathContainer */ /** * @param {string[]} tokens * @param {boolean} isAbolute * @returns {PathContainer} */ function PathContainer(tokens, isAbolute) { function cloneTokens() { var nxt = []; tokens.forEach(function (it) { nxt.push(it); }); return nxt; } function toString() { var str = isAbolute ? '/' : ''; if (tokens.length > 0) { var endWithSlash = tokens[tokens.length - 1].endsWith('/'); str += tokens .map(function (it) { var rts = it; if (rts.endsWith('/')) { rts = rts.substring(0, rts.length - 1); } if (rts.startsWith('/')) { rts = rts.substring(1, rts.length); } return rts; }) .reduce(function (previous, token) { return previous + '/' + token; }); if (endWithSlash) { str += '/'; } } return str; } function clone() { return PathContainer(cloneTokens(), isAbolute); } function toAbsolute() { return PathContainer(cloneTokens(), true); } function toRelative() { return PathContainer(cloneTokens(), false); } /** * @param {string| string[]} token */ function resolve(token) { var nxt = cloneTokens(); if (isTypedArray(token)) { token.forEach(function (it) { nxt.push(it); }); } else { nxt.push(token); } return PathContainer(nxt, isAbolute); } return { tokens: tokens, isAbsolute: isAbolute, clone: clone, resolve: resolve, toAbsolute: toAbsolute, toRelative: toRelative, toString: toString }; } /** * @param {Protocol} protocol * @param {string} host * @param {number} port * @param {PathContainer} path * @param {QueryParams} queryParams * @param {RequestInit} fetchRequestParams * @returns {FetchURL} */ function FetchURL(protocol, host, port, path, queryParams, fetchRequestParams) { function toString() { var p = ''; if (port != 80 && port != 0) { p = ':' + port; } var str = protocol + '://' + host + p + path.toAbsolute().toString(); var withQ = false; for (var k in queryParams) { if (queryParams[k] !== undefined) { if (!withQ) { withQ = true; str += '?'; } else { str += '&'; } var param = queryParams[k]; if (isTypedArray(param)) { for (var i = 0; i < param.length; i++) { if (i != 0) { str += '&'; } str += k + '=' + encodeURIComponent(param[i]); } } else { str += k + '=' + encodeURIComponent(param); } } } return str; } /** * @returns {QueryParams} */ function cloneQueryParams() { /** * @type {QueryParams} */ var queryP = {}; for (var key in queryParams) { queryP[key] = queryParams[key]; } return queryP; } /** * @param {string | string[]} _path * @returns {FetchURL} */ function resolvePath(_path) { var nextPath = path.resolve(_path); return FetchURL(protocol, host, port, nextPath, cloneQueryParams(), fetchRequestParams); } function clone() { return FetchURL(protocol, host, port, path, cloneQueryParams(), fetchRequestParams); } function addParameter(key, value) { var clonedQueryParams = cloneQueryParams(); clonedQueryParams[key] = value; return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams); } function addParameters(obj) { var clonedQueryParams = cloneQueryParams(); clonedQueryParams = assign(clonedQueryParams, obj); return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams); } function changeFetchRequestParams(_fetchRequestParams) { return FetchURL(protocol, host, port, path, cloneQueryParams(), _fetchRequestParams); } /** * @returns {PepitePromise<PepiteResponse>} */ function useFetch() { //@ts-ignore The Pepite java interface is not the same as the web fetch API. some overlaps allow the use of the fetch API //but the promise is not the same return fetch(toString(), fetchRequestParams); } /** * @param {Partial<RequestInit>} partialFetchRequest * @returns {FetchURL} */ function completeFetchRequestParams(partialFetchRequest) { var nextRequestParams = assign({}, fetchRequestParams); nextRequestParams = assign(nextRequestParams, partialFetchRequest); return changeFetchRequestParams(nextRequestParams); } /** * @param {string} key * @param {any} value * @returns {FetchURL} */ function addHeader(key, value) { var headers = assign({}, fetchRequestParams.headers || {}); headers[key] = value; return completeFetchRequestParams({ headers: headers }); } /** * @param {{[key:string]:any}} headers * @returns {FetchURL} */ function addHeaders(headers) { var url = clone(); for (var key in headers) { var value = headers[key]; url = url.addHeader(key, value); } return url; } /** * @param {any} body * @returns {FetchURL} */ function changeBody(body) { return completeFetchRequestParams({ body: body }); } return { protocol: protocol, host: host, path: path, port: port, queryParams: queryParams, fetchRequestParams: fetchRequestParams, toString: toString, resolvePath: resolvePath, clone: clone, addParameter: addParameter, addParameters: addParameters, changeFetchRequestParams: changeFetchRequestParams, useFetch: useFetch, addHeader: addHeader, addHeaders: addHeaders, changeBody: changeBody, completeFetchRequestParams: completeFetchRequestParams }; } /** * @param {string} url * @returns {FetchURL} */ function parseUrl(url) { var protocol = url.split('://')[0]; if (protocol.startsWith('http')) { protocol = protocol; } else { protocol = 'http'; } var l = url.replace(protocol + '://', ''); var hostSplit = l.split(':'); var host = ''; var port = 0; if (hostSplit.length <= 1) { hostSplit = l.split('.'); if (hostSplit.length <= 3) { hostSplit = l.split('/'); host = hostSplit[0]; } else { for (var i = 0; i < 3; i++) { host += hostSplit[i]; } } l = l.replace(host, ''); } else { host = hostSplit[0]; var portSplit = hostSplit[1].split('/'); port = parseInt(portSplit[0]); l = l.replace(host + ':' + port, ''); } var lSplit = l.split('?'); var path = parsePath(lSplit[0]); var queryParamsNotParsed = lSplit[1]; var queryParams = {}; if (queryParamsNotParsed != null) { var queryPairs = queryParamsNotParsed.split('&'); for (var i = 0; i < queryPairs.length; i++) { var keyValue = queryPairs[i].split('='); queryParams[keyValue[0]] = decodeURIComponent(keyValue[1]); } } /** * @type {RequestInit} */ var fetchRequestParams = { method: 'GET' }; //@ts-ignore return FetchURL(protocol, host, port, path, queryParams, fetchRequestParams); } /** * @param {string} path * @returns {PathContainer} */ function parsePath(path) { var isAbsolute = path.startsWith('/'); var index = isAbsolute ? 1 : 0; var tokens = []; var currentToken = ''; var isEscaping = false; while (index < path.length) { var c = path.charAt(index); if (isEscaping) { currentToken += c; isEscaping = false; } else { if (c == '/') { tokens.push(currentToken); currentToken = ''; isEscaping = false; } else if (c == '\\') { isEscaping = true; } else { currentToken += c; } } index++; } if (currentToken != '') { tokens.push(currentToken); } return PathContainer(tokens, isAbsolute); } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/WildCardPredicate.js /** * @typedef {(name:string | {toString():string})=>boolean} WildcardPredicate */ /** * Create a filter function based on a wildcard string * ex: WildcardPredicate('test*')('test123') // true * ex: WildcardPredicate('*test')('123test') // true * ex: WildcardPredicate('test*')('123test') // false * ex: WildcardPredicate('test*123')('test_random_123') // true * ex: WildcardPredicate('test*123')('test123') // true * @param {string} starName * @returns {WildcardPredicate} */ function WildcardPredicate(starName) { var flterValues = starName.split(/\*/gm); var hasStars = starName.indexOf('*') != -1; var startWihStars = starName.startsWith('*'); var endWihStars = starName.endsWith('*'); /** * * @param {string | {toString():string}} _name * @returns {boolean} */ function filter(_name) { var name = typeof _name == 'string' ? _name : _name.toString(); if (!hasStars) { return name == starName; } for (var i = 0; i < flterValues.length; i++) { var fltr = flterValues[i]; if (fltr != '' && fltr.length > 0) { if (i == 0 && !startWihStars) { if (name.startsWith(fltr)) { name = name.substr(fltr.length); } else { return false; } } else if (i == flterValues.length - 1 && !endWihStars) { if (!name.endsWith(fltr)) { return false; } } else { var startOf = name.indexOf(fltr); if (startOf == -1) { return false; } name = name.substr(startOf + fltr.length); } } } return true; } return filter; } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/JavaArrayHelper.js //Common function used with array to be agnostic of the type (either a Java List or a JSArray) /** * The Java List implementation does not possess the method filter, the wrapper will serve this purpose * /!\ will recreate a JS array (not a stream) * @template T * @param {T[] | List<T>} src * @param {(input:T, i:number)=>boolean} filterCallBack * @returns {T[]} */ function filter(src, filterCallBack) { if (isList(src)) { var nxt = []; var max = getLength(src); for (var i = 0; i < max; i++) { if (filterCallBack(src[i], i)) { nxt.push(src[i]); } } return nxt; } else { return src.filter(filterCallBack); } } /** * The Java List implementation does not possess the method map, the wrapper will serve this purpose * /!\ will recreate a JS array (not a stream) * @template T, O * @param {T[] | List<T>} src * @param {(input:T, i:number)=>O} mapper * @returns {O[]} */ function map(src, mapper) { if (isList(src)) { var nxt = []; var max = getLength(src); for (var i = 0; i < max; i++) { nxt.push(mapper(src[i], i)); } return nxt; } else { return src.map(mapper); } } /** * @template T * @param {T[] | List<T>} src */ function getLength(src) { if ('size' in src) { return src.size(); } else { return src.length; } } /** * The Java List implementation does not possess the method forEach, the wrapper will serve this purpose * @template T * @param {T[] | List<T>} src * @param {(input:T, i:number)=>void} onEach */ function forEach(src, onEach) { if (isList(src)) { var max = getLength(src); for (var i = 0; i < max; i++) { onEach(src[i], i); } } else { src.forEach(onEach); } } /** * find the element by predicate * @template T * @template {T} S * @param {T[] | List<T>} src * @param {T extends S ? ((input:T)=>boolean): ((input:T)=>input is S)} predicate * @returns {S | undefined} */ function find(src, predicate) { if (isList(src)) { var max = getLength(src); for (var i = 0; i < max; i++) { if (predicate(src[i])) { return src[i]; } } return undefined; } else { //@ts-ignore return src.find(predicate); } } /** * The Java List implementation does not have all the method like the JSArray object, * Transform the List into a java array * @template T * @param {T[] | List<T>} src * @returns {T[]} */ function asJSArray(src) { //first check if it is not already a JSArray object if (isList(src)) { return map(src, function (it) { return it; }); } else { return src; } } /** * Check if it is a JSArray or a JavaList * @template T * @param {T[] | List<T>} src * @returns {src is List<T>} */ function isList(src) { try { if (typeof src == 'string') { return false; } //@ts-ignore return !(src.length != undefined && src.push != null && src.forEach != null); } finally { return true; } } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/ObjectAssign.js //Clone or assign element to object === to {...obj, ...element} for clone /** * assign the value of obj in target (mutate target) * @template {any} T * @template {any} O * @param {T} target * @param {O} obj * @returns {T & O} */ function assign(target, obj) { for (var key in obj) { var next = obj[key]; if (next !== undefined) { //@ts-ignore target[key] = obj[key]; } } //@ts-ignore return target; } /** * Similar to var out = { ...tag, ...obj} * @template {any} T * @template {Partial<T>} O * @param {O} target * @param {Partial<O> & Omit<T, keyof O>} obj * @returns {T} */ function cloneAssign(target, obj) { var newObj = cloneObj(target); //@ts-ignore return assign(newObj, obj); } /** * Similar to var out = { ...obj } * @template {any} T * @param {T} obj * @returns {T} */ function cloneObj(obj) { return assign({}, obj); } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/isArray.js /** * Check if the object is a JSArray of T or the object T * @template T * @param {T | T[]} obj * @returns {obj is T[]} */ function isTypedArray(obj) { if (Array.isArray(obj)) { return true; } else { if (typeof obj != 'object') { return false; } else { try { return typeof (/**@type {any} */ (obj).length) == 'number'; } catch (e) { return false; } } } } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/ComputeMultipleTag.js //Create a computed tag function to apply on multiple tags /** * @typedef {( values:TagValue[], time:number, lastValue:Optional<TagValue[]>, lastTime:Optional<number>)=>Optional<TagValue>} ComputeTagFn */ /** * @param {JavaVirtualPath[][]} inputsTags The tags to be used as inputs * @param {JavaVirtualPath[]} outputTags The corresponding output tag * @param {ComputeTagFn} computeTagFn The function to be applied on each inputTags (called for each inputTags[i] and outputTags[i] pair) * @param {boolean} [isOptimistic] compute the data until the min date of the input tags included if true, excluded if false, default to true * @param {Optional<Optional<number>[]>} [validityTagMap] The validity of each tags (inputs) */ function useComputedMultipleTag(inputsTags, outputTags, computeTagFn, isOptimistic, validityTagMap) { for (var i = 0; i < inputsTags.length; i++) { useAComputedTag(inputsTags[i], outputTags[i], computeTagFn, isOptimistic == null ? true : isOptimistic, validityTagMap); } } /** * @param {JavaVirtualPath[]} inputsTag * @param {JavaVirtualPath} outputTag * @param {ComputeTagFn} computeTagFn * @param {boolean} isOptimistic * @param {Optional<Optional<number>[]> | undefined} validityTagMap */ function useAComputedTag(inputsTag, outputTag, computeTagFn, isOptimistic, validityTagMap) { var lastDate = tagConnection.getLastDate(outputTag); var lastDateNum = lastDate == null ? null : lastDate.getTime(); /** * @type {SingleRollingInput<TagValue[]>} */ var rollInput = SingleRollingInput(); /** * @type {{lastTime:Optional<number>}} */ var lastTimeHolder = { lastTime: null }; /** * @type {JavaDate | null} */ var minEndTime = null; for (var i = 0; i < inputsTag.length; i++) { var inputTagLastDate = tagConnection.getLastDate(inputsTag[i]); if (inputTagLastDate == null) { //no data for input tag, skip return; } else { var inputTagLastTime = inputTagLastDate.getTime(); var validity = validityTagMap ? validityTagMap[i] : null; if (validity != null) { inputTagLastTime += validity; } if (minEndTime == null || inputTagLastTime < minEndTime.getTime()) { minEndTime = JDate.fromTime(inputTagLastTime); } } } if (isOptimistic) { minEndTime = minEndTime != null ? JDate.fromTime(minEndTime.getTime() + 1) : null; } /** * @param {HistoryRecord} record */ function processor(record) { var recordValues = []; for (var i = 0; i < record.colCount(); i++) { recordValues.push(record.get(i)); } var time = record.getTime(); var lastTime = lastTimeHolder.lastTime; var lastRecordsValues = rollInput.getInput(); var lastValues = lastRecordsValues.previous; var output = computeTagFn(recordValues, time, lastValues, lastTime); if (lastDateNum == null || time > lastDateNum) { tagConnection.putValue(outputTag, JDate.fromTime(time), output); } lastTimeHolder.lastTime = time; rollInput.roll(recordValues); return true; } tagConnection.retrieveValues(lastDate, minEndTime, true, inputsTag).apply(processor); } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/RollInput.js // An input that can be rolled to keep track of previous values /** * @template Values * @typedef {{current:Optional<Values>, previous:Optional<Values>, next:Optional<Values>}} FNInput */ /** * @template Values * @typedef {{current:Optional<Values>, previous:Optional<Values>}} SingleFnInput */ /** * @template Values * @typedef {{size:number, current:Optional<Values>, [back:number]:Optional<Values>}} CountFnInput */ /** * @template V * @typedef {{roll(value:Optional<V>), getInput():FNInput<V> }} RollingInput */ /** * @template V * @typedef {{roll(value:Optional<V>), getInput():SingleFnInput<V> }} SingleRollingInput */ /** * @template V * @typedef {{roll(value:Optional<V>), getInput():CountFnInput<V> }} CountRollingInput */ /** * @template V * @returns {RollingInput<V>} */ function RollingInput() { /** * @type {FNInput<V>} */ var roller = { previous: null, current: null, next: null }; /** * @param {V} value */ function roll(value) { roller.previous = roller.current; roller.current = roller.next; roller.next = value; } /** * @returns {FNInput<V>} */ function getInput() { return assign({}, roller); } return { roll: roll, getInput: getInput }; } /** * @template V * @returns {SingleRollingInput<V>} */ function SingleRollingInput() { /** * @type {SingleFnInput<V>} */ var roller = { previous: null, current: null }; /** * @param {V} value */ function roll(value) { roller.previous = roller.current; roller.current = value; } /** * @returns {SingleFnInput<V>} */ function getInput() { return assign({}, roller); } return { roll: roll, getInput: getInput }; } /** * @template V * @param {number} size * @returns {CountRollingInput<V>} */ function CountRollingInput(size) { /** * @type {CountFnInput<V>} */ var roller = { size: size, current: null }; /** * @param {V} value */ function roll(value) { if (size > 0) { for (var i = -size; i < -1; i++) { roller[i] = roller[i + 1]; } roller[-1] = roller.current; } roller.current = value; } /** * @returns {CountFnInput<V>} */ function getInput() { return assign({}, roller); } return { roll: roll, getInput: getInput }; } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/TypeHelp.js /** * @template T * @typedef {T | null} Optional<T> */ /** * @typedef {()=>void} Toggler * @typedef {{toString():string}} Stringifyable */ /** * @template T * @typedef {{[key:string]:T}} JSMap */ /** * @template { string} K * @template V * @typedef {{[P in K]?:Optional<V>}} PartialEnumTypeToMap */ /** * @template { string} K * @template V * @typedef {{[P in K]:V}} EnumTypeToMap */ /** * @template {string} U * @template {string} F * @typedef {U extends `${infer A}${F}${infer B}` ? U : F} StringMustContains */ /** * @template T * @typedef {T extends null | undefined ? null | undefined : T} IsNotNullType */ /** * @typedef {"Equals" | "Less" | "Greater" } Order */ //#endregion //#region ../../TagScriptLibrary/CommonLibrary/MetaInfo.js /** * @typedef {{name:string, isNumeric:boolean, title?:Optional<string>, classifiers?:Optional<Map<string, any> | JSMap<any>>, description?:Optional<string>, unit?:Optional<string|Unit>}} MetaInfo */ //#endregion //#region ../../TagScriptLibrary/CommonLibrary/isUnit.js /** * @param {string | Unit} unit * @returns {unit is Unit} */ function isUnit(unit) { return isJavaObject(unit); } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/Context.js // Context of the script, to store data to share between function without using global variable (safer as global variable could not be initialized). /** * @type {{[contextName:string]:any}} */ var SCRIPT_CONTEXT; /** * return the context value or create if don't exist. * /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read) * The @param contextName can be null if store in a variable. * @template T * @param {string} contextName * @param {T} [initialValue] * @returns {(initialValue extends undefined ? Partial<T> : T)} */ function getContext(contextName, initialValue) { if (!isInContext(contextName)) { SCRIPT_CONTEXT[contextName] = initialValue; } return SCRIPT_CONTEXT[contextName]; } /** * return the context value or create if don't exist. * /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read) * The @param contextName can be null if store in a variable. * @template T * @param {string} contextName * @param {()=>T} initialValue * @returns {T} */ function getContextOrCreate(contextName, initialValue) { if (!isInContext(contextName)) { SCRIPT_CONTEXT[contextName] = initialValue(); } return SCRIPT_CONTEXT[contextName]; } function initialiseContext() { if (SCRIPT_CONTEXT == null) { SCRIPT_CONTEXT = {}; } } /** * @param {string} contextName */ function isInContext(contextName) { initialiseContext(); return contextName in SCRIPT_CONTEXT; } /** * @template T * @param {string} contextName * @param {T} value * @returns {T} */ function putInContext(contextName, value) { initialiseContext(); SCRIPT_CONTEXT[contextName] = value; return SCRIPT_CONTEXT[contextName]; } /** * @template {{[key:string]:any}} T * @template {keyof T} U * @param {string} contextName * @param {U} key * @param {T[U]} value * @returns {T} */ function putContextValue(contextName, key, value) { var context = /** @type {T} */ (getContext(contextName, {})); context[key] = value; return context; } /** * @template {{[key:string]:any}} T * @param {string} contextName * @param {Partial<T>} value * @returns {T} */ function updateContext(contextName, value) { var context = /** @type {T} */ (getContext(contextName, {})); return assign(context, value); } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/tagCreationConfig.js // Help with the creation of tags and their metadata // by using this lib, all the created tag will be marked as created from a script /** * @typedef {{tagPath:JavaVirtualPath, metaOptions:AttributeMetadata, isMetadataOwner?:Optional<boolean>}} TagCreateConfig * @typedef {{isNumeric?:boolean, title?:Optional<string>, classifiers?:Optional<Map<string, any> | JSMap<any>>, description?:Optional<string>, unit?:Optional<string | Unit>}} OptionalMetaInfo */ /** * Verify all the tags in tagConfigs exist or else, create it * @param {TagCreateConfig[]} tagConfigs : The list of tags config to verify if they exist */ function ensureTagConfigExist(tagConfigs) { for (var i = 0; i < tagConfigs.length; i++) { createIfDontExist(tagConfigs[i]); } } /** * Verify if the current tag exist, if not create the tag with the wanted metadata * @param {TagCreateConfig} tagConfig the config to create if doen't exist */ function createIfDontExist(tagConfig) { var tagPath = tagConfig.tagPath; var metaOptions = tagConfig.metaOptions; var finalMeta = metaOptions.clone(); updateTagCreationConfigClassifiersMetadata(finalMeta, tagConfig.isMetadataOwner); if (!(tagConnection.exists(tagPath))) { var parent = tagPath.getParent(); tagConnection.createTagDirectories(parent); tagConnection.createTag(parent, finalMeta); } else if (isMetadataOwner(tagConfig)) { tagConnection.updateMetadata(tagPath, finalMeta); } return tagPath; } /** * @param {Pick<TagCreateConfig,'isMetadataOwner'>} config * @returns {boolean} */ function isMetadataOwner(config) { return config.isMetadataOwner || getTagCreationConfigContext().isMetadataOwner || false; } /** * @param {AttributeMetadata} meta * @returns {Map<string, any> | JSMap<any>} */ function getClassifiers(meta) { var classifiers = meta.getClassifiers(); if (classifiers == null) { classifiers = {}; meta.setClassifiers(classifiers); } return classifiers; } /** * @param {AttributeMetadata} meta * @param {Optional<boolean> | undefined} _isMetadataOwner */ function updateTagCreationConfigClassifiersMetadata(meta, _isMetadataOwner) { var classifiers = getClassifiers(meta); classifiers['Generated by'] = 'Script Tag Task'; if (isMetadataOwner({ isMetadataOwner: _isMetadataOwner })) { classifiers['Metadata Owner'] = 'Lake-Script'; } var host = constructTagCreationConfigHost(); if (host) { classifiers['Script source url'] = host; } var context = getTagCreationConfigContext(); if (context.scriptFolder) { classifiers['Script source Folder'] = context.scriptFolder.toString(); } if (context.scriptName) { classifiers['Script source Name'] = context.scriptName; } } /** * @returns {string} */ function constructTagCreationConfigHost() { var context = getTagCreationConfigContext(); var str = ''; if (context.scriptHost) { var url = parseUrl(context.scriptHost).resolvePath(['#', 'lake', 'manage', 'jobs']); if (context.scriptFolder) { var folder = context.scriptFolder.toAbsolute().toString(); url = url.addParameter('tagDirectory', folder); } if (context.scriptFolder && context.scriptName) { var jobPath = context.scriptFolder.resolve(context.scriptName); url = url.resolvePath('edit').resolvePath(encodeURIComponent(jobPath.toAbsolute().toString())); } str = url.toString(); } return str; } /** * @typedef {{isMetadataOwner?:boolean, scriptName?:string, scriptFolder?:Optional<JavaVirtualPath>, scriptHost?:string}} TagCreationConfigContext */ /** * @param {TagCreationConfigContext} context */ function putTagCreationConfigContext(context) { putInContext(getTAG_CREATION_CONFIG_CONTEXT_NAME(), context); } /** * @returns {TagCreationConfigContext} */ function getTagCreationConfigContext() { return getContext(getTAG_CREATION_CONFIG_CONTEXT_NAME(), {}); } function getTAG_CREATION_CONFIG_CONTEXT_NAME() { return 'TAG_CREATION_CONFIG_CONTEXT'; } /** * Create a of tag config based on a tagName and a directory * @param {string} tagName : the tag name * @param {JavaVirtualPath} directory : the base dir for the tag * @param {Optional<string>} [prefix] (optional) : a prefix for the tagName * @param {Optional<string>} [suffix] (optional) : a suffix for the tagName * @param {Optional<AttributeMetadata | OptionalMetaInfo>} [metaOptions] (optional) : the associated metadata (if null, all tag will be numeric) * @returns {TagCreateConfig} */ function createTagConfig(tagName, directory, prefix, suffix, metaOptions) { var title = tagName; var tagName = (prefix != null ? prefix : '') + tagName + (suffix != null ? suffix : ''); var metaOpt = asAttributeMetadata(metaOptions) || AttributeMetadataUtil.create(tagName, true); metaOpt.setName(tagName); if (metaOptions == null || metaOpt.getTitle() == null) { metaOpt.setTitle(title); } return { tagPath: directory.resolve(tagName), metaOptions: metaOpt }; } /** * Create a list of tag config based on a tagName and a directory * @param {string[]} tagNames : the tags name * @param {JavaVirtualPath} directory : the base dir for the tag * @param {Optional<string>} prefix (optional) : a prefix for the tagName * @param {Optional<string>} suffix (optional) : a suffix for the tagName * @param {Optional<Optional<AttributeMetadata>[]>} metaOptions (optional) : the associated metadata (if null, all tag will be numeric) * @returns {TagCreateConfig[]} */ function createTagConfigs(tagNames, directory, prefix, suffix, metaOptions) { var configs = []; if (metaOptions == null) { metaOptions = []; } for (var i = 0; i < tagNames.length; i++) { var config = createTagConfig(tagNames[i], directory, prefix, suffix, metaOptions[i]); configs.push(config); } return configs; } /** * @param {JavaVirtualPath} tagPath * @param {Optional<OptionalMetaInfo | AttributeMetadata>} [optionalMetaInfo] * @returns {TagCreateConfig} */ function createConfigFromTagPath(tagPath, optionalMetaInfo) { var metadata = asAttributeMetadata(optionalMetaInfo); var nextName = tagPath.getLastToken(); if (metadata == null) { metadata = AttributeMetadataUtil.create(nextName, true); } else { metadata.setName(nextName); } if (metadata.getTitle() == null) { metadata.setTitle(nextName); } return createTagConfig(metadata.getName(), tagPath.getParent(), '', '', metadata); } /** * @param {Optional<OptionalMetaInfo | AttributeMetadata> | undefined} optionalMetaInfo * @returns {Optional<AttributeMetadata>} */ function asAttributeMetadata(optionalMetaInfo) { if (optionalMetaInfo == null) { return null; } else if (isAttributeMetadata(optionalMetaInfo)) { return optionalMetaInfo; } else { var metadata = AttributeMetadataUtil.create('TEMP', optionalMetaInfo.isNumeric != null ? optionalMetaInfo.isNumeric : true); metadata.setDescription(optionalMetaInfo.description || null); metadata.setClassifiers(optionalMetaInfo.classifiers || null); metadata.setTitle(optionalMetaInfo.title || null); if (optionalMetaInfo.unit) { try { var unit = isUnit(optionalMetaInfo.unit) ? optionalMetaInfo.unit : new Unit(optionalMetaInfo.unit); metadata.setUnit(unit); } catch (__) { //not working yet ? } } return metadata; } } /** * @param {OptionalMetaInfo | AttributeMetadata} optionalMetaInfo * @returns {optionalMetaInfo is AttributeMetadata} */ function isAttributeMetadata(optionalMetaInfo) { return isJavaObject(optionalMetaInfo); } /** * @param {AttributeMetadata} metadata * @param {JavaVirtualPath} baseDir * @returns {TagCreateConfig} */ function createConfigFromMetadata(metadata, baseDir) { return createTagConfig(metadata.getName(), baseDir, '', '', metadata); } /** * @param {MetaInfo} metaLike * @returns {AttributeMetadata} */ function createMetadata(metaLike) { var metadata = AttributeMetadataUtil.create(metaLike.name, metaLike.isNumeric); metadata.setTitle(metaLike.title || null); metadata.setDescription(metaLike.description || null); if (metaLike.classifiers) { metadata.setClassifiers(metaLike.classifiers); } return metadata; } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/TagValue.js /** * @typedef {number|string | null} TagValue */ //#endregion //#region FolderTagRules.js /** * @typedef {{type:"browse", sourceDir:JavaVirtualPath}} BrowseDirectoryBaseRule * @typedef {BrowseDirectoryBaseRule & { wildcard:string, fullPath?:Optional<boolean> }} BrowseDirectoryWildcardRule * @typedef {BrowseDirectoryBaseRule & { predicate?:Optional<(tag:JavaVirtualPath)=>boolean> }} BrowseDirectoryPredicateRule * @typedef {BrowseDirectoryWildcardRule | BrowseDirectoryPredicateRule} BrowseDirectoryRule * @typedef {{type:"list", tags:JavaVirtualPath[]}} ListTagRule * @typedef {BrowseDirectoryRule | ListTagRule } FilterTagRule */ /** * @param {FilterTagRule} rule * @returns {JavaVirtualPath[]} */ function getTags(rule) { if (rule.type == 'list') { return rule.tags; } else { var tags = tagConnection.getTags(rule.sourceDir); /** @type {JavaVirtualPath[]}*/ var filteredTags = []; if (isWildcardBrowseDirectoryRule(rule)) { var predicate = WildcardPredicate(rule.wildcard); for (var i = 0; i < tags.length; i++) { var tag = tags[i]; var name; if (rule.fullPath) { name = tag.toString(); } else { name = tag.getLastToken(); } if (predicate(name)) { filteredTags.push(tag); } } } else if (rule.predicate) { for (var i = 0; i < tags.length; i++) { var tag = tags[i]; if (rule.predicate(tag)) { filteredTags.push(tag); } } } else { return asJSArray(tags); } return filteredTags; } } /** * @param {BrowseDirectoryRule} rule * @returns {rule is BrowseDirectoryWildcardRule} */ function isWildcardBrowseDirectoryRule(rule) { return 'wildcard' in rule; } //#endregion //#region ../../TagScriptLibrary/CommonLibrary/IsJavaObject.js /** * check if the object is a JavaObject of T or the object T (javascript) * @template {JavaObject} T * @param {T | any} element * @returns {element is T} */ function isJavaObject(element) { return ( typeof element != 'string' && typeof (/** @type {T} */ (element).getClass) == 'function' && /** @type {T} */ (element).getClass() != null ); } //#endregion //#endregion



/**
* ––––––––––
* | Jira |
* ––––––––––

// Please mention on ticket DATMA-2916 when you use this script, and describe your Use Case a bit.




/**
* ––––––––––
* | Config |
* ––––––––––
*/
var baseDir = VirtualPath.parse('Valtris.be/INTENSITES_MOTEURS');
var sourceDir = baseDir;
var destinationDir = baseDir.resolve('computed', 'Données nettoyées');

var scriptName = "My script name"
var scriptHost = 'https://test.mydatamaestro.com/'


//------------------------------------------------
//CHOOSE TAG FILTER RULE (3 methods)

/**
* @type {FilterTagRule}
*/
var baseTagFilterRule = {
type: 'browse',
wildcard: '*.INTENSITE', //Check sur une string, * = N'importe quelle string
sourceDir: sourceDir // can add FullPath (true, false)
};

// OR define by list

/**
* @type {FilterTagRule}
*/
// var _baseTagFilterRule = {
// type: 'list',
// tags:[tag1Path, tag2Path]
// };

//OR BASED on a function

// var baseTagFilterRule = {
// type: 'browse',
// predicate: myFunction
// sourceDir: sourceDir
// };

//------------------------------------------------

//Do the mapping for each individual computed tag
//Modify this function to give all inputs as arguments

/**
* @type {(tag:JavaVirtualPath, index:number)=>JavaVirtualPath[]}
*/
var getInputTagsRule = function (intensiteTag) {
var parent = intensiteTag.getParent();
var name = intensiteTag.getLastToken();
var nextName = name + '_NOMINALE';
return [intensiteTag, parent.resolve(nextName)];
};

/**
* @type {(tag:JavaVirtualPath, index:number)=>TagCreateConfig}
*/
var outputTagGetter = function (intensiteTag) {
var name = intensiteTag.getLastToken();
var nextName = name + '_NETTOYEE';
// var meta= tagConnection.getTagMetadata(intensiteTag)
// return createConfigFromTagPath(destinationDir.resolve(nextName),meta); //copy source tag metadata
return createConfigFromTagPath(destinationDir.resolve(nextName), {}); //open {} to add parameter like: {isNumeric:false, unit: "ms", title:"", classifier}
//Can open {} do ctrl space and scroll down to all the different metaData properties
};

/**
*@type {ComputeTagFn}
*/
var customPartagedFunction = function (values, time, lastValue, lastTime) {
//Values are in the order defined in getInputTagsRule --> Access with values[0], values[1]...
return null;
};

/**
* @type {boolean}
*/
var isOptimistic = true;

//TAG VALIDY MAP
/**
* @type {Optional<Optional<number>[]>}
*/
var validityTagMap = null;
//var validityTagMap = [1000,1000] //in MS tag Validy Map is defined identical for all same tag
/**
* –––––––––––
* | Process |
* –––––––––––
*/

putTagCreationConfigContext({
isMetadataOwner: true, //False don't update metadata if exist
scriptFolder: baseDir,
scriptHost: scriptHost,
scriptName: scriptName
});

function main() {
var baseTags = getTags(baseTagFilterRule);
var outputTags = [];
var outputTagConfigs = [];
var inputTags = map(baseTags, function (baseTag, index) {
var outputTagConfig = outputTagGetter(baseTag, index);
outputTags.push(outputTagConfig.tagPath);
outputTagConfigs.push(outputTagConfig);
return getInputTagsRule(baseTag, index);
});
ensureTagConfigExist(outputTagConfigs);
useComputedMultipleTag(inputTags, outputTags, customPartagedFunction, isOptimistic, validityTagMap);
}

//#region Imports

/**
* ----------
* | Import |
* ----------
*/


//#region ../../TagScriptLibrary/CommonLibrary/URL.js
// An Helper object to keep information with the URL and the fetch request parameters


/**
* @template T
* @typedef {{
* getValue():T,
* getValueAsString():string,
* resolved():T,
* then<U>(callback:(next:T)=>U):U
* }} PepitePromise
*/
/**
* @typedef {{ok:boolean, status:number,statusText:string}} PepiteResponseCommon
* @typedef { PepiteResponseCommon & {
* ok:false,
* json():null,
* text():null
* }} PepiteResponseFailed
* @typedef {{
* ok:true,
* status:200,
* statusText:"ok"
* text():PepitePromise<string>,
* json():PepitePromise<unknown>
* }} PepiteResponseSuccess
* @typedef {PepiteResponseFailed | PepiteResponseSuccess} PepiteResponse
*/


/**
* @typedef {"http"|"https"} Protocol
* @typedef {{[key:string]:string}} QueryParams
* @typedef { Stringifyable &
* {
* resolvePath(path:string | string[]):FetchURL,
* clone():FetchURL,
* addParameter(key:string, value:string):FetchURL,
* addParameters(obj:any):FetchURL,
* completeFetchRequestParams(partialFetchRequest:Partial<RequestInit>):FetchURL,
* changeFetchRequestParams(fetchRequestParams:RequestInit):FetchURL,
* useFetch():PepitePromise<PepiteResponse>,
* addHeader(key:string, value:any):FetchURL,
* addHeaders(map:{[key:string]:any}):FetchURL,
* changeBody(body:any):FetchURL
* }} URLFN
* @typedef {{protocol:Protocol, host:string, port:number, path:PathContainer, queryParams:QueryParams, fetchRequestParams:RequestInit} & URLFN} FetchURL
*/

/**
* @typedef {{tokens:string[], isAbsolute:boolean, clone():PathContainer, resolve(token:string|string[]):PathContainer, toAbsolute():PathContainer, toRelative():PathContainer} & Stringifyable} PathContainer
*/

/**
* @param {string[]} tokens
* @param {boolean} isAbolute
* @returns {PathContainer}
*/
function PathContainer(tokens, isAbolute) {
function cloneTokens() {
var nxt = [];
tokens.forEach(function (it) {
nxt.push(it);
});
return nxt;
}

function toString() {
var str = isAbolute ? '/' : '';
if (tokens.length > 0) {
var endWithSlash = tokens[tokens.length - 1].endsWith('/');
str += tokens
.map(function (it) {
var rts = it;
if (rts.endsWith('/')) {
rts = rts.substring(0, rts.length - 1);
}
if (rts.startsWith('/')) {
rts = rts.substring(1, rts.length);
}
return rts;
})
.reduce(function (previous, token) {
return previous + '/' + token;
});
if (endWithSlash) {
str += '/';
}
}
return str;
}

function clone() {
return PathContainer(cloneTokens(), isAbolute);
}

function toAbsolute() {
return PathContainer(cloneTokens(), true);
}

function toRelative() {
return PathContainer(cloneTokens(), false);
}

/**
* @param {string| string[]} token
*/
function resolve(token) {
var nxt = cloneTokens();
if (isTypedArray(token)) {
token.forEach(function (it) {
nxt.push(it);
});
} else {
nxt.push(token);
}
return PathContainer(nxt, isAbolute);
}

return {
tokens: tokens,
isAbsolute: isAbolute,
clone: clone,
resolve: resolve,
toAbsolute: toAbsolute,
toRelative: toRelative,
toString: toString
};
}

/**
* @param {Protocol} protocol
* @param {string} host
* @param {number} port
* @param {PathContainer} path
* @param {QueryParams} queryParams
* @param {RequestInit} fetchRequestParams
* @returns {FetchURL}
*/
function FetchURL(protocol, host, port, path, queryParams, fetchRequestParams) {
function toString() {
var p = '';
if (port != 80 && port != 0) {
p = ':' + port;
}
var str = protocol + '://' + host + p + path.toAbsolute().toString();
var withQ = false;
for (var k in queryParams) {
if (queryParams[k] !== undefined) {
if (!withQ) {
withQ = true;
str += '?';
} else {
str += '&';
}
var param = queryParams[k];
if (isTypedArray(param)) {
for (var i = 0; i < param.length; i++) {
if (i != 0) {
str += '&';
}
str += k + '=' + encodeURIComponent(param[i]);
}
} else {
str += k + '=' + encodeURIComponent(param);
}
}
}
return str;
}

/**
* @returns {QueryParams}
*/
function cloneQueryParams() {
/**
* @type {QueryParams}
*/
var queryP = {};

for (var key in queryParams) {
queryP[key] = queryParams[key];
}
return queryP;
}

/**
* @param {string | string[]} _path
* @returns {FetchURL}
*/
function resolvePath(_path) {
var nextPath = path.resolve(_path);
return FetchURL(protocol, host, port, nextPath, cloneQueryParams(), fetchRequestParams);
}

function clone() {
return FetchURL(protocol, host, port, path, cloneQueryParams(), fetchRequestParams);
}

function addParameter(key, value) {
var clonedQueryParams = cloneQueryParams();
clonedQueryParams[key] = value;
return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams);
}

function addParameters(obj) {
var clonedQueryParams = cloneQueryParams();
clonedQueryParams = assign(clonedQueryParams, obj);
return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams);
}

function changeFetchRequestParams(_fetchRequestParams) {
return FetchURL(protocol, host, port, path, cloneQueryParams(), _fetchRequestParams);
}

/**
* @returns {PepitePromise<PepiteResponse>}
*/
function useFetch() {

//@ts-ignore The Pepite java interface is not the same as the web fetch API. some overlaps allow the use of the fetch API
//but the promise is not the same
return fetch(toString(), fetchRequestParams);
}

/**
* @param {Partial<RequestInit>} partialFetchRequest
* @returns {FetchURL}
*/
function completeFetchRequestParams(partialFetchRequest) {
var nextRequestParams = assign({}, fetchRequestParams);
nextRequestParams = assign(nextRequestParams, partialFetchRequest);
return changeFetchRequestParams(nextRequestParams);
}

/**
* @param {string} key
* @param {any} value
* @returns {FetchURL}
*/
function addHeader(key, value) {
var headers = assign({}, fetchRequestParams.headers || {});
headers[key] = value;

return completeFetchRequestParams({ headers: headers });
}

/**
* @param {{[key:string]:any}} headers
* @returns {FetchURL}
*/
function addHeaders(headers) {
var url = clone();
for (var key in headers) {
var value = headers[key];
url = url.addHeader(key, value);
}
return url;
}

/**
* @param {any} body
* @returns {FetchURL}
*/
function changeBody(body) {
return completeFetchRequestParams({ body: body });
}

return {
protocol: protocol,
host: host,
path: path,
port: port,
queryParams: queryParams,
fetchRequestParams: fetchRequestParams,
toString: toString,
resolvePath: resolvePath,
clone: clone,
addParameter: addParameter,
addParameters: addParameters,
changeFetchRequestParams: changeFetchRequestParams,
useFetch: useFetch,
addHeader: addHeader,
addHeaders: addHeaders,
changeBody: changeBody,
completeFetchRequestParams: completeFetchRequestParams
};
}

/**
* @param {string} url
* @returns {FetchURL}
*/
function parseUrl(url) {
var protocol = url.split('://')[0];
if (protocol.startsWith('http')) {
protocol = protocol;
} else {
protocol = 'http';
}
var l = url.replace(protocol + '://', '');

var hostSplit = l.split(':');
var host = '';
var port = 0;
if (hostSplit.length <= 1) {
hostSplit = l.split('.');
if (hostSplit.length <= 3) {
hostSplit = l.split('/');
host = hostSplit[0];
} else {
for (var i = 0; i < 3; i++) {
host += hostSplit[i];
}
}
l = l.replace(host, '');
} else {
host = hostSplit[0];
var portSplit = hostSplit[1].split('/');
port = parseInt(portSplit[0]);
l = l.replace(host + ':' + port, '');
}

var lSplit = l.split('?');
var path = parsePath(lSplit[0]);
var queryParamsNotParsed = lSplit[1];

var queryParams = {};
if (queryParamsNotParsed != null) {
var queryPairs = queryParamsNotParsed.split('&');
for (var i = 0; i < queryPairs.length; i++) {
var keyValue = queryPairs[i].split('=');
queryParams[keyValue[0]] = decodeURIComponent(keyValue[1]);
}
}

/**
* @type {RequestInit}
*/
var fetchRequestParams = {
method: 'GET'
};
//@ts-ignore
return FetchURL(protocol, host, port, path, queryParams, fetchRequestParams);
}

/**
* @param {string} path
* @returns {PathContainer}
*/
function parsePath(path) {
var isAbsolute = path.startsWith('/');
var index = isAbsolute ? 1 : 0;
var tokens = [];
var currentToken = '';
var isEscaping = false;

while (index < path.length) {
var c = path.charAt(index);
if (isEscaping) {
currentToken += c;
isEscaping = false;
} else {
if (c == '/') {
tokens.push(currentToken);
currentToken = '';
isEscaping = false;
} else if (c == '\\') {
isEscaping = true;
} else {
currentToken += c;
}
}
index++;
}

if (currentToken != '') {
tokens.push(currentToken);
}

return PathContainer(tokens, isAbsolute);
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/WildCardPredicate.js

/**
* @typedef {(name:string | {toString():string})=>boolean} WildcardPredicate
*/

/**
* Create a filter function based on a wildcard string
* ex: WildcardPredicate('test*')('test123') // true
* ex: WildcardPredicate('*test')('123test') // true
* ex: WildcardPredicate('test*')('123test') // false
* ex: WildcardPredicate('test*123')('test_random_123') // true
* ex: WildcardPredicate('test*123')('test123') // true
* @param {string} starName
* @returns {WildcardPredicate}
*/
function WildcardPredicate(starName) {
var flterValues = starName.split(/\*/gm);
var hasStars = starName.indexOf('*') != -1;
var startWihStars = starName.startsWith('*');
var endWihStars = starName.endsWith('*');
/**
*
* @param {string | {toString():string}} _name
* @returns {boolean}
*/
function filter(_name) {
var name = typeof _name == 'string' ? _name : _name.toString();
if (!hasStars) {
return name == starName;
}

for (var i = 0; i < flterValues.length; i++) {
var fltr = flterValues[i];
if (fltr != '' && fltr.length > 0) {
if (i == 0 && !startWihStars) {
if (name.startsWith(fltr)) {
name = name.substr(fltr.length);
} else {
return false;
}
} else if (i == flterValues.length - 1 && !endWihStars) {
if (!name.endsWith(fltr)) {
return false;
}
} else {
var startOf = name.indexOf(fltr);
if (startOf == -1) {
return false;
}
name = name.substr(startOf + fltr.length);
}
}
}

return true;
}

return filter;
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/JavaArrayHelper.js
//Common function used with array to be agnostic of the type (either a Java List or a JSArray)

/**
* The Java List implementation does not possess the method filter, the wrapper will serve this purpose
* /!\ will recreate a JS array (not a stream)
* @template T
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>boolean} filterCallBack
* @returns {T[]}
*/
function filter(src, filterCallBack) {
if (isList(src)) {
var nxt = [];
var max = getLength(src);
for (var i = 0; i < max; i++) {
if (filterCallBack(src[i], i)) {
nxt.push(src[i]);
}
}
return nxt;
} else {
return src.filter(filterCallBack);
}
}

/**
* The Java List implementation does not possess the method map, the wrapper will serve this purpose
* /!\ will recreate a JS array (not a stream)
* @template T, O
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>O} mapper
* @returns {O[]}
*/
function map(src, mapper) {
if (isList(src)) {
var nxt = [];
var max = getLength(src);
for (var i = 0; i < max; i++) {
nxt.push(mapper(src[i], i));
}
return nxt;
} else {
return src.map(mapper);
}
}

/**
* @template T
* @param {T[] | List<T>} src
*/
function getLength(src) {
if ('size' in src) {
return src.size();
} else {
return src.length;
}
}

/**
* The Java List implementation does not possess the method forEach, the wrapper will serve this purpose
* @template T
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>void} onEach
*/
function forEach(src, onEach) {
if (isList(src)) {
var max = getLength(src);
for (var i = 0; i < max; i++) {
onEach(src[i], i);
}
} else {
src.forEach(onEach);
}
}

/**
* find the element by predicate
* @template T
* @template {T} S
* @param {T[] | List<T>} src
* @param {T extends S ? ((input:T)=>boolean): ((input:T)=>input is S)} predicate
* @returns {S | undefined}
*/
function find(src, predicate) {
if (isList(src)) {
var max = getLength(src);
for (var i = 0; i < max; i++) {
if (predicate(src[i])) {
return src[i];
}
}
return undefined;
} else {
//@ts-ignore
return src.find(predicate);
}
}

/**
* The Java List implementation does not have all the method like the JSArray object,
* Transform the List into a java array
* @template T
* @param {T[] | List<T>} src
* @returns {T[]}
*/
function asJSArray(src) {
//first check if it is not already a JSArray object
if (isList(src)) {
return map(src, function (it) {
return it;
});
} else {
return src;
}
}

/**
* Check if it is a JSArray or a JavaList
* @template T
* @param {T[] | List<T>} src
* @returns {src is List<T>}
*/
function isList(src) {
try {
if (typeof src == 'string') {
return false;
}
//@ts-ignore
return !(src.length != undefined && src.push != null && src.forEach != null);
} finally {
return true;
}
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/ObjectAssign.js
//Clone or assign element to object === to {...obj, ...element} for clone
/**
* assign the value of obj in target (mutate target)
* @template {any} T
* @template {any} O
* @param {T} target
* @param {O} obj
* @returns {T & O}
*/
function assign(target, obj) {
for (var key in obj) {
var next = obj[key];
if (next !== undefined) {
//@ts-ignore
target[key] = obj[key];
}
}
//@ts-ignore
return target;
}

/**
* Similar to var out = { ...tag, ...obj}
* @template {any} T
* @template {Partial<T>} O
* @param {O} target
* @param {Partial<O> & Omit<T, keyof O>} obj
* @returns {T}
*/
function cloneAssign(target, obj) {
var newObj = cloneObj(target);
//@ts-ignore
return assign(newObj, obj);
}

/**
* Similar to var out = { ...obj }
* @template {any} T
* @param {T} obj
* @returns {T}
*/
function cloneObj(obj) {
return assign({}, obj);
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/isArray.js
/**
* Check if the object is a JSArray of T or the object T
* @template T
* @param {T | T[]} obj
* @returns {obj is T[]}
*/
function isTypedArray(obj) {
if (Array.isArray(obj)) {
return true;
} else {
if (typeof obj != 'object') {
return false;
} else {
try {
return typeof (/**@type {any} */ (obj).length) == 'number';
} catch (e) {
return false;
}
}
}
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/ComputeMultipleTag.js
//Create a computed tag function to apply on multiple tags

/**
* @typedef {( values:TagValue[], time:number, lastValue:Optional<TagValue[]>, lastTime:Optional<number>)=>Optional<TagValue>} ComputeTagFn
*/

/**
* @param {JavaVirtualPath[][]} inputsTags The tags to be used as inputs
* @param {JavaVirtualPath[]} outputTags The corresponding output tag
* @param {ComputeTagFn} computeTagFn The function to be applied on each inputTags (called for each inputTags[i] and outputTags[i] pair)
* @param {boolean} [isOptimistic] compute the data until the min date of the input tags included if true, excluded if false, default to true
* @param {Optional<Optional<number>[]>} [validityTagMap] The validity of each tags (inputs)
*/
function useComputedMultipleTag(inputsTags, outputTags, computeTagFn, isOptimistic, validityTagMap) {
for (var i = 0; i < inputsTags.length; i++) {
useAComputedTag(inputsTags[i], outputTags[i], computeTagFn, isOptimistic == null ? true : isOptimistic, validityTagMap);
}
}

/**
* @param {JavaVirtualPath[]} inputsTag
* @param {JavaVirtualPath} outputTag
* @param {ComputeTagFn} computeTagFn
* @param {boolean} isOptimistic
* @param {Optional<Optional<number>[]> | undefined} validityTagMap
*/
function useAComputedTag(inputsTag, outputTag, computeTagFn, isOptimistic, validityTagMap) {
var lastDate = tagConnection.getLastDate(outputTag);
var lastDateNum = lastDate == null ? null : lastDate.getTime();
/**
* @type {SingleRollingInput<TagValue[]>}
*/
var rollInput = SingleRollingInput();

/**
* @type {{lastTime:Optional<number>}}
*/
var lastTimeHolder = {
lastTime: null
};

/**
* @type {JavaDate | null}
*/
var minEndTime = null;
for (var i = 0; i < inputsTag.length; i++) {
var inputTagLastDate = tagConnection.getLastDate(inputsTag[i]);
if (inputTagLastDate == null) {
//no data for input tag, skip
return;
} else {
var inputTagLastTime = inputTagLastDate.getTime();
var validity = validityTagMap ? validityTagMap[i] : null;
if (validity != null) {
inputTagLastTime += validity;
}
if (minEndTime == null || inputTagLastTime < minEndTime.getTime()) {
minEndTime = JDate.fromTime(inputTagLastTime);
}
}
}

if (isOptimistic) {
minEndTime = minEndTime != null ? JDate.fromTime(minEndTime.getTime() + 1) : null;
}

/**
* @param {HistoryRecord} record
*/
function processor(record) {
var recordValues = [];
for (var i = 0; i < record.colCount(); i++) {
recordValues.push(record.get(i));
}
var time = record.getTime();
var lastTime = lastTimeHolder.lastTime;
var lastRecordsValues = rollInput.getInput();
var lastValues = lastRecordsValues.previous;

var output = computeTagFn(recordValues, time, lastValues, lastTime);
if (lastDateNum == null || time > lastDateNum) {
tagConnection.putValue(outputTag, JDate.fromTime(time), output);
}

lastTimeHolder.lastTime = time;
rollInput.roll(recordValues);
return true;
}

tagConnection.retrieveValues(lastDate, minEndTime, true, inputsTag).apply(processor);
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/RollInput.js
// An input that can be rolled to keep track of previous values

/**
* @template Values
* @typedef {{current:Optional<Values>, previous:Optional<Values>, next:Optional<Values>}} FNInput
*/
/**
* @template Values
* @typedef {{current:Optional<Values>, previous:Optional<Values>}} SingleFnInput
*/
/**
* @template Values
* @typedef {{size:number, current:Optional<Values>, [back:number]:Optional<Values>}} CountFnInput
*/

/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():FNInput<V> }} RollingInput
*/
/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():SingleFnInput<V> }} SingleRollingInput
*/
/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():CountFnInput<V> }} CountRollingInput
*/
/**
* @template V
* @returns {RollingInput<V>}
*/
function RollingInput() {
/**
* @type {FNInput<V>}
*/
var roller = {
previous: null,
current: null,
next: null
};

/**
* @param {V} value
*/
function roll(value) {
roller.previous = roller.current;
roller.current = roller.next;
roller.next = value;
}

/**
* @returns {FNInput<V>}
*/
function getInput() {
return assign({}, roller);
}

return {
roll: roll,
getInput: getInput
};
}

/**
* @template V
* @returns {SingleRollingInput<V>}
*/
function SingleRollingInput() {
/**
* @type {SingleFnInput<V>}
*/
var roller = {
previous: null,
current: null
};

/**
* @param {V} value
*/
function roll(value) {
roller.previous = roller.current;
roller.current = value;
}

/**
* @returns {SingleFnInput<V>}
*/
function getInput() {
return assign({}, roller);
}

return {
roll: roll,
getInput: getInput
};
}

/**
* @template V
* @param {number} size
* @returns {CountRollingInput<V>}
*/
function CountRollingInput(size) {
/**
* @type {CountFnInput<V>}
*/
var roller = {
size: size,
current: null
};

/**
* @param {V} value
*/
function roll(value) {
if (size > 0) {
for (var i = -size; i < -1; i++) {
roller[i] = roller[i + 1];
}
roller[-1] = roller.current;
}
roller.current = value;
}

/**
* @returns {CountFnInput<V>}
*/
function getInput() {
return assign({}, roller);
}

return {
roll: roll,
getInput: getInput
};
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/TypeHelp.js
/**
* @template T
* @typedef {T | null} Optional<T>
*/

/**
* @typedef {()=>void} Toggler
* @typedef {{toString():string}} Stringifyable
*/

/**
* @template T
* @typedef {{[key:string]:T}} JSMap
*/

/**
* @template { string} K
* @template V
* @typedef {{[P in K]?:Optional<V>}} PartialEnumTypeToMap
*/

/**
* @template { string} K
* @template V
* @typedef {{[P in K]:V}} EnumTypeToMap
*/

/**
* @template {string} U
* @template {string} F
* @typedef {U extends `${infer A}${F}${infer B}` ? U : F} StringMustContains
*/

/**
* @template T
* @typedef {T extends null | undefined ? null | undefined : T} IsNotNullType
*/

/**
* @typedef {"Equals" | "Less" | "Greater" } Order
*/

//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/MetaInfo.js
/**
* @typedef {{name:string, isNumeric:boolean, title?:Optional<string>, classifiers?:Optional<Map<string, any> | JSMap<any>>, description?:Optional<string>, unit?:Optional<string|Unit>}} MetaInfo
*/

//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/isUnit.js

/**
* @param {string | Unit} unit
* @returns {unit is Unit}
*/
function isUnit(unit) {
return isJavaObject(unit);
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/Context.js
// Context of the script, to store data to share between function without using global variable (safer as global variable could not be initialized).

/**
* @type {{[contextName:string]:any}}
*/
var SCRIPT_CONTEXT;

/**
* return the context value or create if don't exist.
* /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read)
* The @param contextName can be null if store in a variable.
* @template T
* @param {string} contextName
* @param {T} [initialValue]
* @returns {(initialValue extends undefined ? Partial<T> : T)}
*/
function getContext(contextName, initialValue) {
if (!isInContext(contextName)) {
SCRIPT_CONTEXT[contextName] = initialValue;
}
return SCRIPT_CONTEXT[contextName];
}
/**
* return the context value or create if don't exist.
* /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read)
* The @param contextName can be null if store in a variable.
* @template T
* @param {string} contextName
* @param {()=>T} initialValue
* @returns {T}
*/
function getContextOrCreate(contextName, initialValue) {
if (!isInContext(contextName)) {
SCRIPT_CONTEXT[contextName] = initialValue();
}
return SCRIPT_CONTEXT[contextName];
}

function initialiseContext() {
if (SCRIPT_CONTEXT == null) {
SCRIPT_CONTEXT = {};
}
}

/**
* @param {string} contextName
*/
function isInContext(contextName) {
initialiseContext();
return contextName in SCRIPT_CONTEXT;
}

/**
* @template T
* @param {string} contextName
* @param {T} value
* @returns {T}
*/
function putInContext(contextName, value) {
initialiseContext();
SCRIPT_CONTEXT[contextName] = value;
return SCRIPT_CONTEXT[contextName];
}

/**
* @template {{[key:string]:any}} T
* @template {keyof T} U
* @param {string} contextName
* @param {U} key
* @param {T[U]} value
* @returns {T}
*/
function putContextValue(contextName, key, value) {
var context = /** @type {T} */ (getContext(contextName, {}));
context[key] = value;
return context;
}

/**
* @template {{[key:string]:any}} T
* @param {string} contextName
* @param {Partial<T>} value
* @returns {T}
*/
function updateContext(contextName, value) {
var context = /** @type {T} */ (getContext(contextName, {}));
return assign(context, value);
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/tagCreationConfig.js
// Help with the creation of tags and their metadata
// by using this lib, all the created tag will be marked as created from a script


/**
* @typedef {{tagPath:JavaVirtualPath, metaOptions:AttributeMetadata, isMetadataOwner?:Optional<boolean>}} TagCreateConfig
* @typedef {{isNumeric?:boolean, title?:Optional<string>, classifiers?:Optional<Map<string, any> | JSMap<any>>, description?:Optional<string>, unit?:Optional<string | Unit>}} OptionalMetaInfo
*/


/**
* Verify all the tags in tagConfigs exist or else, create it
* @param {TagCreateConfig[]} tagConfigs : The list of tags config to verify if they exist
*/
function ensureTagConfigExist(tagConfigs) {
for (var i = 0; i < tagConfigs.length; i++) {
createIfDontExist(tagConfigs[i]);
}
}

/**
* Verify if the current tag exist, if not create the tag with the wanted metadata
* @param {TagCreateConfig} tagConfig the config to create if doen't exist
*/
function createIfDontExist(tagConfig) {
var tagPath = tagConfig.tagPath;
var metaOptions = tagConfig.metaOptions;
var finalMeta = metaOptions.clone();
updateTagCreationConfigClassifiersMetadata(finalMeta, tagConfig.isMetadataOwner);

if (!(tagConnection.exists(tagPath))) {
var parent = tagPath.getParent();
tagConnection.createTagDirectories(parent);
tagConnection.createTag(parent, finalMeta);
} else if (isMetadataOwner(tagConfig)) {
tagConnection.updateMetadata(tagPath, finalMeta);
}
return tagPath;
}

/**
* @param {Pick<TagCreateConfig,'isMetadataOwner'>} config
* @returns {boolean}
*/
function isMetadataOwner(config) {
return config.isMetadataOwner || getTagCreationConfigContext().isMetadataOwner || false;
}

/**
* @param {AttributeMetadata} meta
* @returns {Map<string, any> | JSMap<any>}
*/
function getClassifiers(meta) {
var classifiers = meta.getClassifiers();
if (classifiers == null) {
classifiers = {};
meta.setClassifiers(classifiers);
}
return classifiers;
}

/**
* @param {AttributeMetadata} meta
* @param {Optional<boolean> | undefined} _isMetadataOwner
*/
function updateTagCreationConfigClassifiersMetadata(meta, _isMetadataOwner) {
var classifiers = getClassifiers(meta);

classifiers['Generated by'] = 'Script Tag Task';

if (isMetadataOwner({ isMetadataOwner: _isMetadataOwner })) {
classifiers['Metadata Owner'] = 'Lake-Script';
}

var host = constructTagCreationConfigHost();

if (host) {
classifiers['Script source url'] = host;
}
var context = getTagCreationConfigContext();

if (context.scriptFolder) {
classifiers['Script source Folder'] = context.scriptFolder.toString();
}
if (context.scriptName) {
classifiers['Script source Name'] = context.scriptName;
}
}

/**
* @returns {string}
*/
function constructTagCreationConfigHost() {
var context = getTagCreationConfigContext();
var str = '';
if (context.scriptHost) {
var url = parseUrl(context.scriptHost).resolvePath(['#', 'lake', 'manage', 'jobs']);
if (context.scriptFolder) {
var folder = context.scriptFolder.toAbsolute().toString();
url = url.addParameter('tagDirectory', folder);
}

if (context.scriptFolder && context.scriptName) {
var jobPath = context.scriptFolder.resolve(context.scriptName);
url = url.resolvePath('edit').resolvePath(encodeURIComponent(jobPath.toAbsolute().toString()));
}
str = url.toString();
}

return str;
}

/**
* @typedef {{isMetadataOwner?:boolean, scriptName?:string, scriptFolder?:Optional<JavaVirtualPath>, scriptHost?:string}} TagCreationConfigContext
*/

/**
* @param {TagCreationConfigContext} context
*/
function putTagCreationConfigContext(context) {
putInContext(getTAG_CREATION_CONFIG_CONTEXT_NAME(), context);
}
/**
* @returns {TagCreationConfigContext}
*/
function getTagCreationConfigContext() {
return getContext(getTAG_CREATION_CONFIG_CONTEXT_NAME(), {});
}

function getTAG_CREATION_CONFIG_CONTEXT_NAME() {
return 'TAG_CREATION_CONFIG_CONTEXT';
}

/**
* Create a of tag config based on a tagName and a directory
* @param {string} tagName : the tag name
* @param {JavaVirtualPath} directory : the base dir for the tag
* @param {Optional<string>} [prefix] (optional) : a prefix for the tagName
* @param {Optional<string>} [suffix] (optional) : a suffix for the tagName
* @param {Optional<AttributeMetadata | OptionalMetaInfo>} [metaOptions] (optional) : the associated metadata (if null, all tag will be numeric)
* @returns {TagCreateConfig}
*/
function createTagConfig(tagName, directory, prefix, suffix, metaOptions) {
var title = tagName;
var tagName = (prefix != null ? prefix : '') + tagName + (suffix != null ? suffix : '');
var metaOpt = asAttributeMetadata(metaOptions) || AttributeMetadataUtil.create(tagName, true);
metaOpt.setName(tagName);
if (metaOptions == null || metaOpt.getTitle() == null) {
metaOpt.setTitle(title);
}
return {
tagPath: directory.resolve(tagName),
metaOptions: metaOpt
};
}

/**
* Create a list of tag config based on a tagName and a directory
* @param {string[]} tagNames : the tags name
* @param {JavaVirtualPath} directory : the base dir for the tag
* @param {Optional<string>} prefix (optional) : a prefix for the tagName
* @param {Optional<string>} suffix (optional) : a suffix for the tagName
* @param {Optional<Optional<AttributeMetadata>[]>} metaOptions (optional) : the associated metadata (if null, all tag will be numeric)
* @returns {TagCreateConfig[]}
*/
function createTagConfigs(tagNames, directory, prefix, suffix, metaOptions) {
var configs = [];
if (metaOptions == null) {
metaOptions = [];
}
for (var i = 0; i < tagNames.length; i++) {
var config = createTagConfig(tagNames[i], directory, prefix, suffix, metaOptions[i]);
configs.push(config);
}
return configs;
}

/**
* @param {JavaVirtualPath} tagPath
* @param {Optional<OptionalMetaInfo | AttributeMetadata>} [optionalMetaInfo]
* @returns {TagCreateConfig}
*/
function createConfigFromTagPath(tagPath, optionalMetaInfo) {
var metadata = asAttributeMetadata(optionalMetaInfo);
var nextName = tagPath.getLastToken();
if (metadata == null) {
metadata = AttributeMetadataUtil.create(nextName, true);
} else {
metadata.setName(nextName);
}
if (metadata.getTitle() == null) {
metadata.setTitle(nextName);
}
return createTagConfig(metadata.getName(), tagPath.getParent(), '', '', metadata);
}

/**
* @param {Optional<OptionalMetaInfo | AttributeMetadata> | undefined} optionalMetaInfo
* @returns {Optional<AttributeMetadata>}
*/
function asAttributeMetadata(optionalMetaInfo) {
if (optionalMetaInfo == null) {
return null;
} else if (isAttributeMetadata(optionalMetaInfo)) {
return optionalMetaInfo;
} else {
var metadata = AttributeMetadataUtil.create('TEMP', optionalMetaInfo.isNumeric != null ? optionalMetaInfo.isNumeric : true);
metadata.setDescription(optionalMetaInfo.description || null);
metadata.setClassifiers(optionalMetaInfo.classifiers || null);
metadata.setTitle(optionalMetaInfo.title || null);
if (optionalMetaInfo.unit) {
try {
var unit = isUnit(optionalMetaInfo.unit) ? optionalMetaInfo.unit : new Unit(optionalMetaInfo.unit);
metadata.setUnit(unit);
} catch (__) {
//not working yet ?
}
}
return metadata;
}
}

/**
* @param {OptionalMetaInfo | AttributeMetadata} optionalMetaInfo
* @returns {optionalMetaInfo is AttributeMetadata}
*/
function isAttributeMetadata(optionalMetaInfo) {
return isJavaObject(optionalMetaInfo);
}

/**
* @param {AttributeMetadata} metadata
* @param {JavaVirtualPath} baseDir
* @returns {TagCreateConfig}
*/
function createConfigFromMetadata(metadata, baseDir) {
return createTagConfig(metadata.getName(), baseDir, '', '', metadata);
}

/**
* @param {MetaInfo} metaLike
* @returns {AttributeMetadata}
*/
function createMetadata(metaLike) {
var metadata = AttributeMetadataUtil.create(metaLike.name, metaLike.isNumeric);
metadata.setTitle(metaLike.title || null);
metadata.setDescription(metaLike.description || null);
if (metaLike.classifiers) {
metadata.setClassifiers(metaLike.classifiers);
}
return metadata;
}
//#endregion

//#region ../../TagScriptLibrary/CommonLibrary/TagValue.js
/**
* @typedef {number|string | null} TagValue
*/