From e14c50ec26a9f25e5a73038dd7956ac5f8618cc0 Mon Sep 17 00:00:00 2001 From: dragos-eu Date: Thu, 7 Nov 2024 16:49:18 +0100 Subject: [PATCH] business logic processing --- ESPD/examples/espd.json | 2 +- ESPD/examples/startComponent.js | 6 +- index.html | 7 +- src/js/espd.js | 1024 +++++++++++++++++++++++++++++++ src/js/pdb.js | 95 +++ src/js/service.js | 23 - src/js/utils.js | 724 ++-------------------- 7 files changed, 1184 insertions(+), 697 deletions(-) create mode 100644 src/js/espd.js create mode 100644 src/js/pdb.js diff --git a/ESPD/examples/espd.json b/ESPD/examples/espd.json index 012a1be..68e7695 100644 --- a/ESPD/examples/espd.json +++ b/ESPD/examples/espd.json @@ -38,7 +38,7 @@ "D": ["C61"] }, "partIII": { - "A": ["C1", "C2", "C3", "C4", "C5", "C6"], + "A": ["C1", "C2", "C3", "C4", "C5", "C6"], "B": ["C7", "C8"], "C": ["C9", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20", "C21", "C22", "C23"], "D": ["C24"] diff --git a/ESPD/examples/startComponent.js b/ESPD/examples/startComponent.js index 7a6e643..07bca9d 100644 --- a/ESPD/examples/startComponent.js +++ b/ESPD/examples/startComponent.js @@ -32,7 +32,7 @@ Vue.component("startComponent", { if (thecall.ok) { window.espd_model = data //build the components for this version on demand - //factoryComponents() + componentsFactory() } } catch (error) { console.log(error) @@ -43,8 +43,8 @@ Vue.component("startComponent", { }, updated(){ - saveData(this.exp) this.$store.commit('savekv', {key:'start', value:this.exp}) + window.espd_doc = this.exp }, created(){ @@ -75,7 +75,7 @@ Vue.component("startComponent", { window.espd_model = data window.espd_data = {} //create the components for this version based on the model - //factoryComponents() + componentsFactory() } } } catch (error) { diff --git a/index.html b/index.html index b333e11..fba1dcd 100644 --- a/index.html +++ b/index.html @@ -75,8 +75,11 @@ - + + + + @@ -122,7 +125,7 @@ - + diff --git a/src/js/espd.js b/src/js/espd.js new file mode 100644 index 0000000..070df1b --- /dev/null +++ b/src/js/espd.js @@ -0,0 +1,1024 @@ +/** + * ESPD Library + */ + + +/** + * ESPD specific structures + * + * tag_map - Tags mapped to elements from the Excel + */ +const tag_map = { + 'CRITERION': 'C', + 'ADDITIONAL_DESCRIPTION_LINE': 'ADL', + 'SUBCRITERION': 'SBC', + 'LEGISLATION': 'L', + 'REQUIREMENT_GROUP': 'RG', + 'QUESTION_GROUP': 'QG', + 'REQUIREMENT_SUBGROUP': 'RSG', + 'QUESTION_SUBGROUP': 'QSG', + 'CAPTION': 'CA', + 'REQUIREMENT': 'RQ', + 'QUESTION': 'Q', + 'RESPONSE': 'R', + 'RESPONSE VALUE': 'RV', + 'EVIDENCE SUPPLIED': 'RES', + 'APPLICABLE PERIOD': 'RAP', +} + + +/** + * Render the elementes of JSON to XML ESPD Request, and ESPD Response - the Request part + * + * @param {*} obj - the ESPD Request or part of ESPD Request to be rendered + * @param {*} [part=window.espd_request] - ESPD Response XML document + * @param {boolean} [EG_FLAG=true] - true if the part is an Exclusion Ground + */ +function render_request(obj, part = window.espd_request, EG_FLAG = true) { + let tmp = part, schemeVersionID = window.espd_doc.espd_version.substring(1) + for (const elm in obj) { + + if (Object.hasOwn(obj, elm)) { + const element = obj[elm] + switch (element.type) { + case "CRITERION": + + //All Selection Criteria must have a Lot + if (element.tag.endsWith('SC')) { + tmp = part.com(` Criterion: ${element.name} `) + .ele('@cac', 'TenderingCriterion') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'CriterionTypeCode', { 'listID': "http://publications.europa.eu/resource/authority/criterion", 'listAgencyID': "OP", 'listVersionID': "20230315-0" }).txt(element.elementcode).up() + .ele('@cbc', 'Name').txt(element.name).up() + .ele('@cbc', 'Description').txt(element.description).up() + + //add lots here cac:ProcurementProjectLot + for (let index = 0; index < element.lots.length; index++) { + tmp = tmp.ele('@cac', 'ProcurementProjectLotReference') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "OP", 'schemeVersionID': schemeVersionID }).txt(element.lots[index]).up() + .up() + } + } else { + tmp = part.com(` Criterion: ${element.name} `) + .ele('@cac', 'TenderingCriterion') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'CriterionTypeCode', { 'listID': "http://publications.europa.eu/resource/authority/criterion", 'listAgencyID': "OP", 'listVersionID': "20230315-0" }).txt(element.elementcode).up() + .ele('@cbc', 'Name').txt(element.name).up() + .ele('@cbc', 'Description').txt(element.description).up() + } + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, element.tag.endsWith('- EG')) + part.up() + + break; + case "SUBCRITERION": + tmp = part.ele('@cac', 'SubTenderingCriterion') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'Name').txt(element.name).up() + .ele('@cbc', 'Description').txt(element.description).up() + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, EG_FLAG) + part.up() + break; + case "LEGISLATION": + tmp = part.ele('@cac', 'Legislation') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'Title').txt('[Legislation Title]').up() + .ele('@cbc', 'Description').txt('[Legislation Description]').up() + .ele('@cbc', 'JurisdictionLevel').txt('EU').up() + .ele('@cbc', 'Article').txt('[Article, e.g. Article 2.I.a]').up() + .ele('@cbc', 'URI').txt('http://eur-lex.europa.eu/').up() + .ele('@cac', 'Language') + .ele('@cbc', 'LocaleCode', { 'listID': "http://publications.europa.eu/resource/authority/language", 'listAgencyID': "ISO", 'listVersionID': "20220928-0" }).txt('ENG').up() + .up() + .up() + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, EG_FLAG) + part.up() + break; + case "ADDITIONAL_DESCRIPTION_LINE": + tmp = part.ele('@cbc', 'Description').txt(element.description).up() + /* + if (!EG_FLAG) + tmp = part.ele('@cac', 'ProcurementProjectLotReference') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "OP", 'schemeVersionID': schemeVersionID }).txt('LOT-0000').up() + .up() + */ + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, EG_FLAG) + part.up() + break; + case "REQUIREMENT_GROUP": case "QUESTION_GROUP": + //render only 1st occurence in Request + if (Object.hasOwn(element, 'cardinality') && typeof element.cardinality === 'string') { + //log(element.cardinality, typeof element.cardinality) + if (element.cardinality.indexOf("(2)") != -1 || + element.cardinality.indexOf("(3)") != -1 || + element.cardinality.indexOf("(4)") != -1) + break; + } + tmp = part.ele('@cac', 'TenderingCriterionPropertyGroup') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'PropertyGroupTypeCode', { 'listID': "property-group-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(element.elementcode).up() + + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, EG_FLAG) + part.up() + break; + case "REQUIREMENT_SUBGROUP": case "QUESTION_SUBGROUP": + //render only 1st occurence in Request + if (Object.hasOwn(element, 'cardinality') && typeof element.cardinality === 'string') { + //log(element.cardinality, typeof element.cardinality) + if (element.cardinality.indexOf("(2)") != -1 || + element.cardinality.indexOf("(3)") != -1 || + element.cardinality.indexOf("(4)") != -1) + break; + } + tmp = part.ele('@cac', 'SubsidiaryTenderingCriterionPropertyGroup') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'PropertyGroupTypeCode', { 'listID': "property-group-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(element.elementcode).up() + + //create the inner elements + if (Object.hasOwn(element, 'components')) render_request(element.components, tmp, EG_FLAG) + part.up() + break; + case "QUESTION": case "CAPTION": + element.elementUUID = crypto.randomUUID() + part.ele('@cac', 'TenderingCriterionProperty') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'Name').txt(element.name).up() + .ele('@cbc', 'Description').txt(element.description).up() + .ele('@cbc', 'TypeCode', { 'listID': "criterion-element-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(element.type).up() + .ele('@cbc', 'ValueDataTypeCode', { 'listID': "response-data-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(Object.hasOwn(element, 'propertydatatype') ? element.propertydatatype : 'NONE').up() + .up() + break; + case "REQUIREMENT": + element.elementUUID = crypto.randomUUID() + tmp = part.ele('@cac', 'TenderingCriterionProperty') + .ele('@cbc', 'ID', { 'schemeID': 'Criterion', 'schemeAgencyID': 'OP', 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'Name').txt(element.name).up() + .ele('@cbc', 'Description').txt(element.description).up() + .ele('@cbc', 'TypeCode', { 'listID': "criterion-element-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(element.type).up() + .ele('@cbc', 'ValueDataTypeCode', { 'listID': "response-data-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt(Object.hasOwn(element, 'propertydatatype') ? element.propertydatatype : 'NONE').up() + .com(' No answer is expected here from the economic operator, as this is a REQUIREMENT issued by the contracting authority. Hence the element "cbc:ValueDataTypeCode" contains the type of value of the requirement issued by the contracting authority ') + + switch (element.propertydatatype) { + case 'AMOUNT': + tmp.ele('@cbc', 'ExpectedAmount', { 'currencyID': 'EUR' }).txt(element.sellervalue ? element.sellervalue : 0).up() + break; + case 'IDENTIFIER': case "EVIDENCE_IDENTIFIER": case "ECONOMIC_OPERATOR_IDENTIFIER": case "LOT_IDENTIFIER": + tmp.ele('@cbc', 'ExpectedID', { 'schemeAgencyID': 'OP' }).txt(element.sellervalue).up() + break; + case 'CODE_BOOLEAN': + tmp.ele('@cbc', 'ExpectedCode', { 'listID': 'boolean-gui-control-type', 'listAgencyID': 'OP', 'listVersionID': schemeVersionID }).txt(element.sellervalue).up() + break; + case 'CODE_COUNTRY': + tmp.ele('@cbc', 'ExpectedCode', { 'listID': "http://publications.europa.eu/resource/authority/country", 'listName': "country", 'listAgencyID': "ISO", 'listVersionID': "20220928-0" }).txt(element.sellervalue).up() + break; + case 'ECONOMIC_OPERATOR_ROLE_CODE': + tmp.ele('@cbc', 'ExpectedCode', { 'listID': "http://publications.europa.eu/resource/authority/eo-role-type", 'listAgencyID': "OP", 'listVersionID': "20211208-0" }).txt(element.sellervalue).up() + break; + case 'DESCRIPTION': + tmp.ele('@cbc', 'ExpectedDescription').txt(element.sellervalue).up() + break; + case 'PERCENTAGE': + tmp.ele('@cbc', 'ValueUnitCode').txt('PERCENTAGE').up() + .ele('@cbc', 'ExpectedValueNumeric').txt(element.sellervalue ? element.sellervalue : 0).up() + break; + case 'DATE': case 'PERIOD': + tmp.ele('@cac', 'ApplicablePeriod') + .ele('@cbc', 'StartDate').txt(element.sellervalue ? element.sellervalue : '2000-01-01').up() + .ele('@cbc', 'EndDate').txt(element.sellervalue ? element.sellervalue : '2000-12-31').up() + .up() + break; + case 'QUANTITY_INTEGER': case 'QUANTITY_YEAR': case 'QUANTITY': + tmp.ele('@cbc', 'ExpectedValueNumeric').txt(element.sellervalue ? element.sellervalue : 0).up() + break; + case 'MAXIMUM_AMOUNT': + tmp.ele('@cbc', 'MaximumAmount', { 'currencyID': 'EUR' }).txt(element.sellervalue ? element.sellervalue : 0).up() + break; + case 'MINIMUM_AMOUNT': + tmp.ele('@cbc', 'MinimumAmount', { 'currencyID': 'EUR' }).txt(element.sellervalue ? element.sellervalue : 0).up() + break; + case 'MAXIMUM_VALUE_NUMERIC': + tmp.ele('@cbc', 'MaximumValueNumeric').txt(element.sellervalue).up() + break; + case 'MINIMUM_VALUE_NUMERIC': + tmp.ele('@cbc', 'MinimumValuenumeric').txt(element.sellervalue).up() + break; + case 'TRANSLATION_TYPE_CODE': + tmp.ele('@cbc', 'TranslationTypeCode').txt(element.sellervalue).up() + break; + case 'COPY_QUALITY_TYPE_CODE': + tmp.ele('@cbc', 'CopyQualityTypeCode').txt(element.sellervalue).up() + break; + case 'CERTIFICATION_LEVEL_DESCRIPTION': + tmp.ele('@cbc', 'CertificationLevelDescription').txt(element.sellervalue).up() + break; + + case 'CODE': + if (element.codelist == 'Occupation') tmp.ele('@cbc', 'ExpectedCode', { 'listID': "http://publications.europa.eu/resource/authority/occupation", 'listAgencyID': "EMPL", 'listVersionID': "20221214-0" }).txt('DV').up() + if (element.codelist == 'FinancialRatioType') tmp.ele('@cbc', 'ExpectedCode', { 'listID': "financial-ratio-type", 'listAgencyID': "OP", 'listVersionID': schemeVersionID }).txt('DV').up() + if (element.codelist == 'EORoleType') tmp.ele('@cbc', 'ExpectedCode', { 'listID': "http://publications.europa.eu/resource/authority/eo-role-type", 'listAgencyID': "OP", 'listVersionID': "20211208-0" }).txt('DV').up() + break; + + default: + tmp.com(' PropertyDataType not defined') + break; + } + + part.up() + break; + default: + part.com(` Unkown ${element.type} - UBL mapping not implemented `).up() + break; + } + } + } +} + +/** +* Render the elements of JSON to XML ESPD Response +* +* @param {*} obj - the ESPD Response or part of ESPD Response to be rendered +* @param {*} [part=window.espd_response] - ESPD Response XML document +* @param {string} [crt_criterion='NONE'] - current criterion to be rendered +*/ +function render_response(obj, part = window.espd_response, crt_criterion = 'NONE') { + let tmp = part, schemeVersionID = window.espd_doc.espd_version.substring(1) + + for (const elm in obj) { + if (Object.hasOwn(obj, elm)) { + const element = obj[elm]; + + switch (element.type) { + case 'CRITERION': + //Check for LOT-0001 + if (Object.hasOwn(element, 'lots') && element.lots.indexOf('LOT-0001') == -1) return + //create the inner elements + if (Object.hasOwn(element, 'components')) render_response(element.components, tmp, element.name) + break; + case "QUESTION_GROUP": case "QUESTION_SUBGROUP": + //check for QUESTIONS that are INDICATOR and render only the components that are ON*, + //and ONTRUE or ONFALSE depending on the value of the INDICATOR + let tmpComponents = {}, QI_FLAG = false, INDICATOR_value = null; + if (Object.hasOwn(element, 'components')) { + for (const key in element.components) { + if (Object.hasOwn(element.components, key)) { + const e = element.components[key] + tmpComponents[key] = e + if (e.type == 'QUESTION' && e.propertydatatype == 'INDICATOR') { + QI_FLAG = true + INDICATOR_value = 'true' + } + } + } + if (QI_FLAG) { + for (const cmp in tmpComponents) { + if (Object.hasOwn(tmpComponents, cmp)) { + const c = tmpComponents[cmp] + if (['QUESTION_GROUP', 'QUESTION_SUBGROUP'].indexOf(c.type) != -1) { + if ((c.elementcode == 'ONTRUE' && INDICATOR_value == 'false') || + (c.elementcode == 'ONFALSE' && INDICATOR_value == 'true')) { + delete tmpComponents[cmp] + } + } + } + } + } + render_response(tmpComponents, part, crt_criterion) + } + + break; + case "REQUIREMENT_GROUP": + case "REQUIREMENT_SUBGROUP": + case 'SUBCRITERION': + case 'LEGISLATION': case 'ADDITIONAL_DESCRIPTION_LINE': + case 'REQUIREMENT': case 'CAPTION': + //create the inner elements + if (Object.hasOwn(element, 'components')) render_response(element.components, tmp, crt_criterion) + break; + case 'QUESTION': + tmp = part.com(` Answer to Criterion:${crt_criterion} `) + .com(` Property: ${element.description} (PropertyID: ${element.elementUUID}) `) + .ele('@cac', 'TenderingCriterionResponse') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ValidatedCriterionPropertyID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + + switch (element.propertydatatype) { + case 'PERIOD': + tmp.ele('@cac', 'ApplicablePeriod') + .ele('@cbc', 'StartDate').txt('2017-01-01').up() + .ele('@cbc', 'EndDate').txt('2017-12-12').up() + .up() + break; + case 'EVIDENCE_IDENTIFIER': + tmp.ele('@cac', 'EvidenceSupplied') + .ele('@cbc', 'ID', { 'schemeAgencyID': 'OP' }).txt('EVIDENCE-001').up() + .up() + break; + case 'DESCRIPTION': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'Description').txt(element.sellervalue ? element.sellervalue : 'Dummy Description').up() + .up() + break; + case 'INDICATOR': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseIndicator').txt('true').up() + .up() + break; + case 'IDENTFIER': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseID', { 'schemeAgencyID': 'OP' }).txt(element.sellervalue ? element.sellervalue : 'Dummy ID').up() + .up() + break; + case 'ECONOMIC_OPERATOR_IDENTIFIER': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseID', { 'schemeAgencyID': 'XXXEOIDXXX' }).txt(element.sellervalue ? element.sellervalue : 'Dummy EO_ID').up() + .up() + break; + case 'QUAL_IDENTIFIER': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseID', { 'schemeAgencyID': 'XXXQUALIDXXX' }).txt(element.sellervalue ? element.sellervalue : 'Dummy QUAL_ID').up() + .up() + break; + case 'URL': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseURI').txt(element.sellervalue ? element.sellervalue : 'www.no-such-site.eu').up() + .up() + break; + case 'AMOUNT': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseAmount', { 'currencyID': 'EUR' }).txt(1000000000).up() + .up() + break; + case 'PERCENTAGE': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseNumeric', { 'format': 'PERCENTAGE' }).txt(element.sellervalue ? element.sellervalue : 3.14).up() + .up() + break; + case 'QUANTITY_INTEGER': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseQuantity', { 'unitCode': 'INTEGER' }).txt(element.sellervalue ? element.sellervalue : 42).up() + .up() + break; + case 'QUANTITY_YEAR': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseQuantity', { 'unitCode': 'YEAR' }).txt(element.sellervalue ? element.sellervalue : 2000).up() + .up() + break; + case 'QUANTITY': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseQuantity').txt(element.sellervalue ? element.sellervalue : 60).up() + .up() + break; + case 'DATE': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseDate').txt('2000-01-01').up() + .up() + break; + case 'TIME': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseTime').txt(element.sellervalue ? element.sellervalue : '00:00:00+00:00').up() + .up() + break; + case 'CODE_COUNTRY': + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseCode', { 'listID': "http://publications.europa.eu/resource/authority/country", 'listName': "country", 'listAgencyID': "ISO", 'listVersionID': "20220928-0" }).txt('BEL').up() + .up() + break; + case 'CODE': + if (element.codelist == 'Occupation') { + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseCode', { 'listAgencyID': "EMPL", 'listVersionID': "20221214-0", 'listID': "http://publications.europa.eu/resource/authority/occupation" }).txt('dummy-value').up() + .up() + } else if (element.codelist == 'FinancialRatioType') { + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseCode', { 'listAgencyID': "OP", 'listVersionID': schemeVersionID, 'listID': "financial-ratio-type" }).txt('dummy-value').up() + .up() + } else if (element.codelist == 'EoRoleType') { + tmp.ele('@cac', 'ResponseValue') + .ele('@cbc', 'ID', { 'schemeID': "Criterion", 'schemeAgencyID': "XXXESPD-SERVICEXXX", 'schemeVersionID': schemeVersionID }).txt(element.elementUUID).up() + .ele('@cbc', 'ResponseCode', { 'listAgencyID': "OP", 'listVersionID': "20211208-0", 'listID': "http://publications.europa.eu/resource/authority/eo-role-type" }).txt('dummy-value').up() + .up() + } + + break; + default: + break; + } + + tmp.up() + //part.up() + break; + + default: + break; + } + } + } +} + +/** + * Render HTML/Bootstrap from JSON data for ESPD Example + * The JSON object is stored in window.espd_doc + * Each criteria is a root level entry + * + * @param {string} part - the JSON path to be rendered as HTML + * @param {object} data_load - the data collected from the corresponding fragment + * + * @returns - HTML with the part populated with data that can be inserted into the page + */ +function renderHTML(part, data_load) { + console.log(part, data_load) + let result = `${part}::${Date.now()}` + + //build HTML fragment for each part based on the ESPD EDM corresponding structure + let tag = `${part.substring(0, part.indexOf('_'))}`, target = part.split('/'), dest = window.espd_ds.get(tag).store + target.shift() + //store the data on window.espd_data + //mimic cardinality so that when building the response path + + for (const element of target) { + if(Array.isArray(dest)){ + dest = dest.find((elm) => elm.has(element)) + }else{ + dest = Array.isArray(dest.get(element))? + dest.get(element).at(-1): + dest.get(element) + } + } + //check if the element only element of the array contains values or it is the 1st time they are storing something + //this is an initialization arterfact + //store the values in a new datastructure entry at the end of the array + let INIT = false + dest.forEach( (v,k) => { + if (!INIT) INIT = Array.isArray(v) && v.length > 0 + }) + console.log(INIT, dest, target) + + if (!INIT){ + //use this structure to store data + }else{ + //create a new structure an add it to the end + } + + result = `
+
+ ` + /* + for (const key in dest.components) { + if (Object.hasOwn(dest.components, key)) { + const element = dest.components[key] + + switch (element.type) { + case 'QUESTION': + result += `[Q] ${element.description} :: ${element.responsepath}
` + break; + + default: + console.log(`Unknown element ${element.type}`); + break; + } + + } + } + */ + result += ` +
+ +
+ ` + + return result +} + +function deleteHTML(part) { + console.log(part); + let cmp = `${window.espd_doc.espd_version}-${part.substring(0,part.indexOf('_'))}`, + parent = window.app.$refs['mainComponent'].$refs['startComponent'] ?? + window.app.$refs['mainComponent'].$refs['procedureComponent'] ?? + window.app.$refs['mainComponent'].$refs['selectionCriteria'] ?? + window.app.$refs['mainComponent'].$refs['exclusionGrounds'] ?? + window.app.$refs['mainComponent'].$refs['ESPDDownload'] + + parent.$refs[cmp][0]._data[`html_${stringToProperty(part)}`] = '' +} + +/** + * Produce on demand the Vue components for ESPD example part + * The ESPD structure must be in window.espd_model + * The ESPD version is stored in window.espd_doc_espd_version + */ +function componentsFactory() { + //console.log(window.espd_doc.espd_version, window.espd_model) + switch (window.espd_doc.espd_version) { + case 'v3.3.0': + //not implemented + break; + case 'v4.0.0': + //Build Vue UI components + json2ESPD_v4(window.espd_model) + //Build the underlying data structure + window.espd_ds = new Map() + for (const key in window.espd_model) { + if (Object.hasOwn(window.espd_model, key)) { + const element = window.espd_model[key]; + window.espd_ds.set(key, new ESPD(key, element)) + } + } + break; + default: + console.log(`Version ${window.espd_doc.espd_version} not supported.`); + break; + } +} + +/** + * Auxiliary function to transform espd_json to Vue components that can be used in ESPD Response + * JSON to BootstrapVue for ESPD v4.0.0 tranformation + * @param {*} fragment - JSON object to be processed + * @param {*} result - JSON object to conserver the result + */ +function json2ESPD_v4(fragment, result = { sel_count: 0, template: ''}) { + + var last_sel_count = 0 + + for (const el in fragment) { + if (Object.hasOwn(fragment, el)) { + switch (fragment[el].type) { + case 'CRITERION': + last_sel_count = Math.min(last_sel_count, result.sel_count) + result.data_part = {} + result.exp = {} + //log(last_sel_count) + if (fragment[el].tag.endsWith('- SC')) { + result.exp[`cb_${el}`] = true + result.template = + `
+ + ${fragment[el].name} +

${fragment[el].description}

+
+ ` + result.template += ` +
` + //Produce Vue component + // exp: {} - is data that is exchanged at top level + // + + last_sel_count = Math.max(last_sel_count, result.sel_count) + let d = result.data_part + d.exp = result.exp + + Vue.component(`${window.espd_doc.espd_version}-${el}`, { + data() { return {...d}}, + template: `${result.template}` + }) + + + //DONE - return result + break; + + case 'SUBCRITERION': + result.template += `
` + /* + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.data_part += `"html_${stringToProperty(fragment[el].requestpath)}": '', \n` + result.template += `
` + } + */ + if (Object.hasOwn(fragment[el], 'components')) result = json2ESPD_v4(fragment[el].components, result) + /* + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` + ` + } + */ + result.template += `
` + break; + + case 'REQUIREMENT_GROUP': + result.template += `
` + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.data_part[`html_${stringToProperty(fragment[el].requestpath)}`] = '' + result.template += `
` + } + + if (Object.hasOwn(fragment[el], 'components')) result = json2ESPD_v4(fragment[el].components, result) + + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` + ` + } + result.template += `
` + + break; + + case 'REQUIREMENT_SUBGROUP': + result.template += `
` + + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.data_part[`html_${stringToProperty(fragment[el].requestpath)}`]='' + result.template += `
` + } + + let is_radio_group = false, select_variable = "", option_variable = "" + if (Object.hasOwn(fragment[el], 'components')) { + for (const e in fragment[el].components) { + if (Object.hasOwn(fragment[el].components, e)) { + const tmp_cmp = fragment[el].components[e] + if (tmp_cmp.type == 'REQUIREMENT') { + if (tmp_cmp.buyervalue == 'RADIO_BUTTON_TRUE') { + //The list of values is in elementcode field + //This is a list of exclusive values for a group or radio buttons + result.sel_count++ + let local_indicator = (result.sel_count + '').padStart(2, '0') + is_radio_group = true + select_variable = `${stringToProperty(tmp_cmp.responsepath)}` + option_variable = `opt_${tmp_cmp.responsepath.replaceAll("-", "__").substring(0, tmp_cmp.responsepath.indexOf("/"))}` + let tc = 0, + opt_var = tmp_cmp.elementcode.replace("[", "").replace("]", "").split(",").map(x => { return { text: x.trim(), value: tc++ } }) + result.data_part[`${option_variable}`] = opt_var + result.data_part[`${select_variable}`] = opt_var[0].value + + result[option_variable] = opt_var + result.exp[`${select_variable}`] = result[option_variable][0].value + result.template += ` + + + ` + } else { + //render normally this element + result = json2ESPD_v4({ e: tmp_cmp }, result) + } + } else if (tmp_cmp.type == 'REQUIREMENT_SUBGROUP') { + if (is_radio_group) { + //The section is shown depending on the value in elementcode field + result.template += `` + } else { + //render normally this element + result = json2ESPD_v4({ e: tmp_cmp }, result) + } + } else { + //render normally this element + result = json2ESPD_v4({ e: tmp_cmp }, result) + } + } + } + } + + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` + ` + } + result.template += `
` + + break; + + case 'QUESTION_GROUP': + let local_indicator = '' + result.template += ` +
` + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.data_part[`html_${stringToProperty(fragment[el].requestpath)}`] = '' + result.template += `
` + } + if (Object.hasOwn(fragment[el], 'components')) { + for (const e in fragment[el].components) { + if (Object.hasOwn(fragment[el].components, e)) { + let tmp_cmp = fragment[el].components[e] + if (tmp_cmp.type == 'QUESTION') { + if (tmp_cmp.propertydatatype == 'INDICATOR') { + //log(e, '\t', tmp_cmp.propertydatatype) + result.exp[`${stringToProperty(tmp_cmp.responsepath)}`] = false + //result.sel_count++ + //local_indicator = (result.sel_count + '').padStart(2, '0') + local_indicator = `${stringToProperty(fragment[el].requestpath)}` + result.template += ` +
[Q] ${tmp_cmp.description} + [{{ exp['${local_indicator}']?'Yes':'No' }}] + + ` + } else { + //render normally + result = json2ESPD_v4({ e: tmp_cmp }, result) + } + } + + if (['QUESTION_GROUP', 'QUESTION_SUBGROUP'].indexOf(tmp_cmp.type) != -1) { + if (['ONTRUE', 'ONFALSE'].indexOf(tmp_cmp.elementcode) != -1) { + result.template += ` +
+ ` + //if (Object.hasOwn(tmp_cmp, 'components')) result = J2V4ESPD(tmp_cmp.components, result) + result = json2ESPD_v4({ e: tmp_cmp }, result) + result.template += ` +
` + } + if (tmp_cmp.elementcode == 'ON*') { + result.template += `
` + //if (Object.hasOwn(tmp_cmp, 'components')) result = J2V4ESPD(tmp_cmp.components, result) + result = json2ESPD_v4({ e: tmp_cmp }, result) + result.template += '
' + } + } + + if (tmp_cmp.type == 'CAPTION') { + result.template += `
${tmp_cmp.description ?? 'CAPTION'}
` + } + + if (tmp_cmp.type == 'LEGISLATION') { + result.template += `LEGISLATION` + } + } + } + } + + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` +
` + } + + result.template += ` +
` + break; + + case 'QUESTION_SUBGROUP': + let local_indicator_qsg = '' + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.data_part[`html_${stringToProperty(fragment[el].responsepath)}`] = '' + result.template += `
` + } + + if (Object.hasOwn(fragment[el], 'components')) { + for (const e in fragment[el].components) { + if (Object.hasOwn(fragment[el].components, e)) { + let tmp_cmp = fragment[el].components[e] + if (tmp_cmp.type == 'QUESTION') { + if (tmp_cmp.propertydatatype == 'INDICATOR') { + //log(e, '\t', tmp_cmp.propertydatatype) + result.exp[`${stringToProperty(tmp_cmp.responsepath)}`] = false + //result[tmp_cmp.responsepath] = false + //result.sel_count++ + //local_indicator_qsg = (result.sel_count + '').padStart(2, '0') + local_indicator_qsg = `${stringToProperty(tmp_cmp.responsepath)}` + result.template += ` +
[Q] ${tmp_cmp.description} + [{{ exp['${local_indicator_qsg}']?'Yes':'No' }}] + + ` + //result.data_part += `"selected${local_indicator_qsg}" : false,\n` + //result[`selected${local_indicator_qsg}`] = false + } else { + //render as usual + result = json2ESPD_v4({ e: tmp_cmp }, result) + } + } + + if (['QUESTION_GROUP', 'QUESTION_SUBGROUP'].indexOf(tmp_cmp.type) != -1) { + if (['ONTRUE', 'ONFALSE'].indexOf(tmp_cmp.elementcode) != -1) { + result.template += ` +
+ ` + result = json2ESPD_v4({ e: tmp_cmp }, result) + result.template += ` +
` + } + if (tmp_cmp.elementcode == 'ON*') { + result.template += `
` + result = json2ESPD_v4({ e: tmp_cmp }, result) + result.template += '
' + } + } + + if (tmp_cmp.type == 'CAPTION') { + result.template += `
${tmp_cmp.description ?? 'CAPTION'}
` + } + + if (tmp_cmp.type == 'LEGISLATION') { + result.template += `LEGISLATION` + } + } + } + } + + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` +
` + } + break; + + case 'QUESTION': + if (fragment[el].propertydatatype != 'INDICATOR') { + result.exp[`${stringToProperty(fragment[el].responsepath)}`] = [] + //result[fragment[el].responsepath] = true + if (fragment[el].cardinality.toString().trim().endsWith('..n')) { + result.template += ` + ` + } else { + result.template += ` + + + ` + } + } else { + //This should be rendered inside the QG/QSG + result.exp[`${stringToProperty(fragment[el].responsepath)}`] = true + result.sel_count++ + //let local_indicator = `${result.sel_count}`.padStart(2, '0') + let tmp_li = `${stringToProperty(fragment[el].responsepath)}` + result.template += ` + [Q] ${fragment[el].description} + [{{ exp['${tmp_li}']?'Yes':'No' }}] + ` + //result.data_part += `"selected${local_indicator}" : false,\n` + } + break; + + case 'LEGISLATION': + result.template += `LEGISLATION` + break; + + case 'ADDITIONAL_DESCRIPTION_LINE': case 'CAPTION': + result.template += `
${fragment[el].description ?? 'ADDITIONAL CAPTION'}
` + break; + + case 'REQUIREMENT': + result.exp[`${stringToProperty(fragment[el].responsepath)}`] = '' + //result[fragment[el].responsepath] = '' + //LOT management + if (fragment[el].propertydatatype == 'LOT_IDENTIFIER') { + result.exp[`lotid_${fragment[el].responsepath.substring(0, fragment[el].responsepath.indexOf('_'))}`] = + window.espd_model[fragment[el].responsepath.substring(0, fragment[el].responsepath.indexOf('_'))].lots ?? [] + + result.template += ` + + +