-
Notifications
You must be signed in to change notification settings - Fork 0
/
createCodesTable.js
343 lines (295 loc) · 11.6 KB
/
createCodesTable.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/** createCodesTable.js
* is responsible for creating the codes table in the modal.
* The module exports a function that initializes the codes table with data.
* The module uses the transposeDataForDataTable function to prepare the data for the table.
*/
import { sessionKeys } from "./sessionStorageKeys.js";
import { getStorageData } from "./utils.js";
let countryCode = "";
/**
* Creates a codes table based on the artefacts collection, concept ID, and country.
* @param {Array} artefactsCollection - The collection of artefacts to display in the table.
* @param {string} conceptId - The ID of the concept associated with the artefacts.
* @param {string} country - The country code to set for the table.
* @returns The DataTable object representing the codes table.
*/
export function createCodesTable(artefactsCollection, conceptId, country) {
countryCode = country;
$("#codes-table").DataTable().destroy();
$("#codes-table tbody").empty();
$("#codes-table thead tr").empty();
const artefactsCodeAppearances = artefactsCollection.map((artefact) =>
processArtefactForCodesTable(artefact, conceptId)
);
const headers = generateTableHeaders(artefactsCodeAppearances, artefactsCollection);
return $("#codes-table").DataTable({
columnDefs: [
{ width: "10%",targets: 0, type: "string"},
{ width: "80%", targets: 1, type: "string"},
{ width: "10%", targets: 2 },
],
initComplete: function () {
this.api()
.columns()
.data()
.each((value, index) => {
// Check if all values in the column are empty or undefined
const isEmptyColumn = value.every(cellValue => cellValue === "" || cellValue === undefined);
if (isEmptyColumn) {
this.api().column(index).visible(false);
}
});
},
data: buildCodeAppearanceRows(artefactsCodeAppearances),
columns: headers.map(function (col) {
return { data: col, defaultContent: '' };
}),
scrollY: "45vh",
scrollCollapse: true
});
}
/**
* Generates table headers based on the provided table rows and artefacts collection.
* @param {Array} tableRows - The array of table rows containing artefact information.
* @param {Array} artefactsCollection - The collection of artefacts.
* @returns {Array} An array of column names for the table headers.
*/
function generateTableHeaders(tableRows, artefactsCollection) {
const dfIds = tableRows
.filter(artefact => artefact.codes.length > 0)
.map(artefact => artefact.dfId);
const headers = buildTableHeaderRows(artefactsCollection, dfIds);
$("#codes-table thead").html(headers.join(""));
const cols = ["code", "name", ...dfIds];
return cols;
}
/**
* Builds the header rows for a table based on the artefacts collection and dataflow IDs provided.
* @param {object} artefactsCollection - The collection of artefacts.
* @param {array} dfIds - An array of dataflow IDs.
* @returns {array} An array of strings representing the header rows for the table.
*/
function buildTableHeaderRows(artefactsCollection, dfIds) {
const yearsCount = new Map();
const dfIdParameters = dfIds.map((dfId) => {
const parameters = extractDfIdParameters(artefactsCollection, dfId);
yearsCount.set(parameters.MICRODATA_COLLECTION_YEAR,
(yearsCount.get(parameters.MICRODATA_COLLECTION_YEAR) || 0) + 1);
return parameters;
});
const rowspans = ["id", "Code Name"].map(key => `<th rowspan='3'>${key}</th>`);
const row1 = getFirstRow(yearsCount);
const row2 = getSecondRow(dfIdParameters);
const row3 = getThirdRow(dfIdParameters);
const headers = ["<tr>"].concat(rowspans, row1, ["</tr><tr>"], row2, ["</tr><tr>"], row3, ["</tr>"]);
return headers;
}
function getFirstRow(yearsCount) {
const yearsKeys = Array.from(yearsCount.keys());
const tableHeaders = yearsKeys.map(key => {
const colspanValue = yearsCount.get(key);
return `<th colspan='${colspanValue}'>${key}</th>`;
});
return tableHeaders;
}
/**
* Generates the second row of a table based on the given dfIdParameters.
* @param {Array} dfIdParameters - An array of parameters for the table rows.
* @returns {Array} An array representing the second row of the table.
*/
function getSecondRow(dfIdParameters) {
let row2 = [];
let previousHeader = null;
let colspan = 1;
for (const parameters of dfIdParameters) {
if (previousHeader && parameters.MICRODATA_FILE_TYPE === previousHeader.MICRODATA_FILE_TYPE) {
colspan++;
} else {
if (previousHeader) {
row2.push(`<th colspan='${colspan}'>${previousHeader.MICRODATA_FILE_TYPE}</th>`);
}
previousHeader = parameters;
colspan = 1;
}
}
if (previousHeader) {
row2.push(`<th colspan='${colspan}'>${previousHeader.MICRODATA_FILE_TYPE}</th>`);
}
return row2;
}
function getThirdRow(dfIdParameters) {
return dfIdParameters.map(parameters => {
const parts = parameters.MICRODATA_DOMAINS.split('_');
const lastPart = parts.pop();
return `<th>${1 ? parameters.MICRODATA_DOMAINS : lastPart}</th>`;
});
}
/**
* Extracts parameters based on the dfId from the artefacts collection.
* @param {Array} artefactsCollection - The collection of artefacts to search through.
* @param {string} dfId - The dfId to match and extract parameters for.
* @returns {Object|null} - The parameters object extracted based on the dfId, or null if no artefact is found.
*/
function extractDfIdParameters(artefactsCollection, dfId) {
const artefact = artefactsCollection.find(artefact => artefact.dfId === dfId);
if (!artefact) return null;
const parameters = artefact.categories.reduce((acc, category) => {
Object.keys(category).forEach(categorisationSchemeKey => {
acc[categorisationSchemeKey] = category[categorisationSchemeKey].id;
});
return acc;
}, {});
return parameters;
}
/**
* Processes an artefact to generate data for a codes table based on the given concept ID.
* @param {object} artefact - The artefact to process.
* @param {string} conceptId - The concept ID to search for in the artefact.
* @returns {object} The accumulated concept data structure for the codes table.
*/
function processArtefactForCodesTable(artefact, conceptId) {
const accumulatedConceptData = initializeEmptyConceptDataStructure(artefact);
const conceptExists = hasConcept(artefact, conceptId);
if (conceptExists) {
handleConceptExists(artefact, conceptId, accumulatedConceptData);
}
return accumulatedConceptData;
}
function initializeEmptyConceptDataStructure(artefact) {
return { dfId: artefact.dfId, codes: [] };
}
function hasConcept(artefact, conceptId) {
return artefact.concepts.hasOwnProperty(conceptId);
}
function handleConceptExists(artefact, conceptId, accumulatedConceptData) {
const clId = artefact.concepts[conceptId].id;
const codesExist = artefact.codes?.[clId];
if (codesExist) {
applyConstraints(artefact, conceptId, clId, accumulatedConceptData);
} else {
accumulatedConceptData[conceptId] = "";
}
}
function findDfIdConstraints(artefact) {
const constraintsCollection = getStorageData(sessionKeys.constraintCollection);
const dfIdConstraints = constraintsCollection.find((constraintObj) => constraintObj.dfId === artefact.dfId);
if (!dfIdConstraints) {
console.error(`No element found with dfId: ${artefact.dfId}`);
return null;
}
return dfIdConstraints;
}
/**
* Extracts constraints from ether provision agreements or from dataflows for a specific concept ID within artefact constraints.
* @param {object} artefactConstraints - The artefact constraints object containing cube regions.
* @param {string} conceptId - The concept ID to extract provision agreements for.
* @returns An object with included and excluded codes based on the concept ID.
*/
function extractConstraintsForConcept(artefactConstraints, conceptId) {
const match = { includedCodes: [], excludedCodes: [] };
artefactConstraints.cubeRegions.forEach(region => {
const { isIncluded, provisionAgreements, dataflows } = region;
const constraints = provisionAgreements || dataflows;
if (isCurrentCountry(constraints)) {
const codes = extractAgreementObjects(constraints, conceptId);
if (codes) {
if (isIncluded) {
match.includedCodes.push(...codes);
} else {
match.excludedCodes.push(...codes);
}
}
}
});
return {
included: match.includedCodes.length > 0 ? match.includedCodes : null,
excluded: match.excludedCodes.length > 0 ? match.excludedCodes : null
};
}
function isCurrentCountry(constraints) {
const [anyKey] = Object.keys(constraints);
const region = countryCode === "codes" ? "ALL" : countryCode
return constraints[anyKey].hasOwnProperty(region);
}
function extractAgreementObjects(provisionAgreements, conceptId) {
const conceptAgreement = provisionAgreements[conceptId];
return conceptAgreement ? Object.values(conceptAgreement).flat() : null;
}
function getConceptCodes(artefact, clId) {
return artefact.codes[clId].codes;
}
/**
* Filters and transforms concept codes based on the provided parameters.
* @param {Object} conceptCodes - An object containing concept codes and their names.
* @param {string} artefact - The artefact to check constraints against.
* @param {string} conceptId - The concept ID to check constraints against.
* @param {string[]} countryAgreement - An array of country agreements.
* @param {boolean} [isIncluded=true] - Flag to determine if the code should be included or excluded.
* @returns An array of objects containing filtered concept codes and their names.
*/
function filterAndTransformConceptCodes(conceptCodes, countryAgreement, isIncluded = true) {
const countryAgreementSet = new Set(countryAgreement);
return Object.entries(conceptCodes)
.filter(([code]) => {
return isIncluded ? countryAgreementSet.has(code) : !countryAgreementSet.has(code);
})
.map(([code, name]) => ({ code, name }));
}
/**
* Apply constraints to the artefact based on the provided conceptId, codeListId, and accumulatedConceptData.
* @param {any} artefact - The artefact to apply constraints to.
* @param {string} conceptId - The concept ID to use for constraints.
* @param {string} codeListId - The code list ID to use for constraints.
* @param {object} accumulatedConceptData - The accumulated concept data object to store the filtered codes.
* @returns None
*/
function applyConstraints(
artefact,
conceptId,
codeListId,
accumulatedConceptData
) {
const artefactConstraints = findDfIdConstraints(artefact);
if (!artefactConstraints) return;
const constraints = extractConstraintsForConcept(artefactConstraints, conceptId);
const conceptCodes = getConceptCodes(artefact, codeListId);
const addFilteredCodes = (codes, isIncluded = true) => {
const filteredCodes = filterAndTransformConceptCodes(
conceptCodes,
codes,
isIncluded
);
accumulatedConceptData.codes =
accumulatedConceptData.codes.concat(filteredCodes);
};
if (constraints.included) {
addFilteredCodes(constraints.included);
} else if (constraints.excluded) {
addFilteredCodes(constraints.excluded, false);
} else {
addFilteredCodes([], false);
}
}
/**
* Builds an array of code appearance rows based on the artefact code appearances provided.
* The unique combinations of code and name are used to create the rows.
* @param {Array} artefactCodeAppearances - An array of artefact code appearances.
* @returns {Array} An array of code appearance rows with unique combinations of code and name.
*/
function buildCodeAppearanceRows(artefactCodeAppearances) {
const codeMap = artefactCodeAppearances.reduce((acc, artefact) => {
const dfId = artefact.dfId;
const codes = artefact.codes;
codes.forEach((codeObj) => {
const code = codeObj.code;
const name = codeObj.name;
const combination = `${code}_${name}`;
if (!acc[combination]) {
acc[combination] = { code: code, name: name };
}
acc[combination][dfId] = "x";
});
return acc;
}, {});
return Object.values(codeMap);
}