read_input/format.js (284 lines of code) (raw):

/** * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { lower, cleanKey, sepArray, sepKeyValPairs, nestObject, rmBracket, lowerObj, underscoreRes, toBool, toNumber, customSort, } from "./util.js"; import { mapEntry } from "./resources/custom-map.js"; export { flatRanges, modifyGeneric, getTfRanges, mapTfRanges, readMapRange, readNamedRange, setRangeDataByName, formatHeaderData, }; function readMapRange(eztf, rangeName) { let resource = eztf.rangeResourceTfMap[rangeName] || rangeName; let [headers, rangeValue] = readNamedRange(eztf, rangeName); return mapData(eztf, headers, rangeValue, rangeName, resource); } function mapData(eztf, headers, values, range, resource) { const data = values .map((row) => { const obj = {}; // row = row.map(str => String(str).trim()); if (row.every((str) => !str)) { return {}; } headers.forEach((header, index) => { obj[header] = row[index]; }); let newObj = mapGeneric(eztf.rangeNoteKey, range, obj); if (mapEntry[resource] && !!newObj && Object.keys(newObj).length > 0) { newObj = mapEntry[resource](newObj, eztf); } return newObj; }) .filter((obj) => !!obj && Object.keys(obj).length > 0); // console.log(JSON.stringify(data,null,2)); if (mapEntry[resource]) { console.log("Custom Map:", resource, range); } return data; } function getRangeByName(eztf, rangeName) { return eztf.readRanges[rangeName]; } function setRangeDataByName(eztf, rangeName, data) { eztf.readRanges[rangeName] = data; } function readNamedRange(eztf, rangeName) { var values = getRangeByName(eztf, rangeName); if (!values) { return [[], []]; } const keyMap = eztf.rangeNoteKey?.[rangeName]?.key || {}; let headers = values[0]; let rangeValue = values.slice(1); let newheaders = headers.map((key) => { key = cleanKey(key); return keyMap[key] || key; }); // console.log(rangeName, newheaders, rangeValue) return [newheaders, rangeValue]; } function mapVariable(data) { for (const [key, value] of Object.entries(data)) { data[key] = value[0]; } return data; } function getTfRanges(eztf, rangeName) { const [tfRanges, verticalRangesList] = groupTfRanges( readMapRange(eztf, rangeName) ); eztf.rangeResourceTfMap = Object.assign( {}, ...Object.values(tfRanges).flat(2) ); return [tfRanges, verticalRangesList]; } function mapTfRanges(data) { if (!data.enabled || !data.stack || !data.resource) { return {}; } const resources = sepKeyValPairs(data.resource, ",", "key", []); const [verticalRange, ranges] = checkVerticalRange(resources); data.resource = ranges; data.vertical = verticalRange; return data; } function checkVerticalRange(input) { const vertical = []; const output = input.map((item) => { const newItem = {}; for (const [key, val] of Object.entries(item)) { const newKey = rmBracket(key, "[]"); const newval = rmBracket(val, "[]"); newItem[newKey] = newval; if ( val.match(/\[\s*(?:v|vertical)\s*\]/) || key.match(/\[\s*(?:v|vertical)\s*\]/) ) { vertical.push(newKey); } } return newItem; }); return [vertical, output]; } function groupTfRanges(res) { const verticalRangesList = []; const groupRanges = res.reduce((tfrange, row) => { if (!tfrange[row.stack]) { tfrange[row.stack] = []; } tfrange[row.stack].push(row.resource); verticalRangesList.push(...row.vertical); return tfrange; }, {}); return [groupRanges, verticalRangesList]; } function flatRanges(tfRanges) { let newRange = {}; for (var key in tfRanges) { newRange[key] = tfRanges[key].flat(); } return newRange; } function modifyGeneric(eztf, rangeResource) { eztf.eztfConfig[rangeResource] = readMapRange(eztf, rangeResource); } // allow_empty_keys // required_keys function getRangeSpecificKeys(rangeNoteKey, rangeResource, key) { if (!rangeNoteKey[rangeResource]) { return []; } if (!rangeNoteKey[rangeResource][key]) { return []; } return rangeNoteKey[rangeResource][key]; } function checkRequired(data, requiredKeys) { for (const key of requiredKeys) { if (data[key] === "" || data[key] === undefined || data[key] === null) { return false; } } return true; } function deleteEmpty(data, allowEmpty) { for (const [key, value] of Object.entries(data)) { if (value === "" || value === undefined || value === null) { if (allowEmpty.includes(key)) { data[key] = ""; } else { delete data[key]; } } } } function replaceVariables(input, variables) { return input.replace(/{([^}]+)}/g, (match, variableName) => { return variables[variableName] || match; }); } function metadataFunSwitch(metadata, data, key) { if (metadata === "commaseperated") { data[key] = sepArray(data[key]); } else if (metadata === "semicolonseperated") { data[key] = sepArray(data[key], ";"); } else if (metadata.startsWith("dontallowif_")) { let val = metadata.split("_")[1] || ""; if (lower(data[key]) === lower(val)) { delete data[key]; } } else if (metadata.startsWith("dontkeep")) { delete data[key]; } else if (metadata === "string") { data[key] = String(data[key]); } else if (metadata === "bool") { data[key] = toBool(data[key]); } else if (metadata === "number") { data[key] = toNumber(data[key]); } else if (metadata === "upper") { data[key] = lower(data[key]).toUpperCase(); } else if (metadata === "lower") { data[key] = lower(data[key]); } else if (metadata === "underscore") { data[key] = underscoreRes(data[key]); } else if (metadata === "keyvalpair") { data[key] = sepKeyValPairs(data[key], ";", true); } else if (metadata === "templatekey") { const newKey = replaceVariables(key, data); data[newKey] = data[key]; if (key !== newKey) { delete data[key]; } } else if (metadata === "moduleid") { data["_eztf_module_id"] = data[key]; } } const metadataOrderMap = { string: 0, bool: 0, number: 0, underscore: 1, upper: 1, lower: 1, commaseperated: 2, semicolonseperated: 2, keyvalpair: 2, templatekey: 3, dontallowif_: 4, dontkeep: 4, }; function runMetadataFun(metadataObj, data, skip = [], only = []) { for (const [key, metadataFun] of Object.entries(metadataObj)) { for (const fun of metadataFun) { if ( !Object.prototype.hasOwnProperty.call(data, key) || skip.includes(fun) || (only.length > 0 && !only.includes(fun)) ) { continue; } metadataFunSwitch(fun, data, key); } } } function mapGeneric(rangeNoteKey, rangeResource, data) { const requiredKeys = rangeNoteKey?.[rangeResource]?.required_keys || []; const allowEmptyKeys = rangeNoteKey?.[rangeResource]?.allow_empty_keys || []; if (!checkRequired(data, requiredKeys)) { return {}; } let metadataObj = rangeNoteKey?.[rangeResource]?.metadata || {}; deleteEmpty(data, allowEmptyKeys); // skip dontkeep to preserve templating key data in first iteration runMetadataFun(metadataObj, data, ["dontkeep"], []); // only apply dontkeep if present runMetadataFun(metadataObj, data, [], ["dontkeep"]); return nestObject(data); } function formatHeaderData(rangeNoteKey, rangeName, header, note) { if (!rangeNoteKey[rangeName]) { rangeNoteKey[rangeName] = { key: {}, required_keys: [], allow_empty_keys: [], metadata: {}, module: {}, }; } let headerKey = cleanKey(header); let noteHeader = lowerObj(sepKeyValPairs(note), true); if (noteHeader["field"]) { rangeNoteKey[rangeName]["key"][headerKey] = noteHeader["field"]; headerKey = noteHeader["field"]; } if (noteHeader.metadata) { let keyMetadata = sepArray(noteHeader["metadata"].toLowerCase()); if (keyMetadata.includes("required")) rangeNoteKey[rangeName]["required_keys"].push(headerKey); if (keyMetadata.includes("allowempty")) rangeNoteKey[rangeName]["allow_empty_keys"].push(headerKey); keyMetadata = keyMetadata.filter( (val) => !["required", "allowempty"].includes(val) ); if (keyMetadata.length > 0) { let sortedKeyMatadata = customSort(keyMetadata, metadataOrderMap); rangeNoteKey[rangeName]["metadata"][headerKey] = sortedKeyMatadata; } } if (noteHeader.source) { rangeNoteKey[rangeName]["module"]["source"] = noteHeader["source"]; } if (noteHeader.version) { rangeNoteKey[rangeName]["module"]["version"] = noteHeader["version"]; } }