package eu.eudat.logic.mapper.prefilling; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.entities.Dataset; import eu.eudat.data.entities.DescriptionTemplate; import eu.eudat.elastic.entities.Tag; import eu.eudat.logic.managers.DatasetManager; import eu.eudat.logic.managers.DatasetProfileManager; import eu.eudat.logic.managers.LicenseManager; import eu.eudat.logic.proxy.config.entities.DefaultPrefillingMapping; import eu.eudat.logic.proxy.config.entities.PrefillingFixedMapping; import eu.eudat.logic.proxy.config.entities.PrefillingGet; import eu.eudat.logic.proxy.config.entities.PrefillingMapping; import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; import eu.eudat.logic.proxy.config.exceptions.NoURLFound; import eu.eudat.logic.utilities.helpers.StreamDistinctBy; import eu.eudat.logic.utilities.json.JsonSearcher; import eu.eudat.models.data.components.commons.datafield.AutoCompleteData; import eu.eudat.models.data.datasetprofile.DatasetProfileOverviewModel; import eu.eudat.models.data.datasetprofile.RenderStyle; import eu.eudat.models.data.datasetwizard.DatasetWizardModel; import eu.eudat.models.data.externaldataset.ExternalAutocompleteFieldModel; import eu.eudat.models.data.license.LicenseModel; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @Component public class PrefillingMapper { private static final Logger logger = LoggerFactory.getLogger(PrefillingMapper.class); private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); private final DatasetProfileManager datasetProfileManager; @Autowired public PrefillingMapper(DatasetProfileManager datasetProfileManager) { this.datasetProfileManager = datasetProfileManager; } public DatasetWizardModel mapPrefilledEntityToDatasetWizard(Map prefilledEntity, PrefillingGet prefillingGet, String type, DescriptionTemplate profile, DatasetManager datasetManager, LicenseManager licenseManager) throws Exception { DatasetWizardModel datasetWizardModel = new DatasetWizardModel(); datasetWizardModel.setProfile(new DatasetProfileOverviewModel().fromDataModel(profile)); Dataset dataset = new Dataset(); dataset.setProfile(profile); Map properties = new HashMap<>(); JsonNode parentNode = mapper.readTree(mapper.writeValueAsString(datasetManager.getPagedProfile(datasetWizardModel, dataset))); for (DefaultPrefillingMapping prefillingMapping: prefillingGet.getMappings()) { List sourceKeys = Arrays.asList(prefillingMapping.getSource().split("\\.")); Object sourceValue = null; for (String sourceKey: sourceKeys) { if (sourceValue == null) { sourceValue = prefilledEntity.get(sourceKey); } else if (sourceValue instanceof Map) { sourceValue = ((Map)sourceValue).get(sourceKey); } } try { setValue(prefillingMapping, mapper.writeValueAsString(sourceValue), datasetWizardModel, parentNode, properties, type, licenseManager); } catch (Exception e) { if (prefillingMapping.getSemanticTarget() != null && !prefillingMapping.getSemanticTarget().isEmpty()) { logger.warn("Couldn't map " + prefillingMapping.getSemanticTarget()); } else if (prefillingMapping.getTarget() != null && !prefillingMapping.getTarget().isEmpty()) { logger.warn("Couldn't map " + prefillingMapping.getTarget()); } } } for (PrefillingFixedMapping fixedMapping: prefillingGet.getFixedMappings()) { setValue(fixedMapping, fixedMapping.getValue(), datasetWizardModel, parentNode, properties, type, licenseManager); } dataset.setProperties(mapper.writeValueAsString(properties)); datasetWizardModel.setDatasetProfileDefinition(datasetManager.getPagedProfile(datasetWizardModel, dataset)); return datasetWizardModel; } private void setValue(PrefillingMapping prefillingMapping, String value, DatasetWizardModel datasetWizardModel, JsonNode parentNode, Map properties, String type, LicenseManager licenseManager) throws InvocationTargetException, IllegalAccessException, JsonProcessingException { String trimRegex = prefillingMapping.getTrimRegex() != null ? prefillingMapping.getTrimRegex() : ""; if (!value.startsWith("\"") && !value.startsWith("[") && !value.equals("null")) { value = "\"" + value + "\""; } JsonNode valueNode = mapper.readTree(value); List parsedValues = new ArrayList<>(); if (valueNode.isArray() && (valueNode.get(0) != null && !valueNode.get(0).isTextual())) { if (prefillingMapping.getSubSource() == null || prefillingMapping.getSubSource().isEmpty()) { throw new IllegalArgumentException("Source value is an array but no subSource field have been set"); } String parsedValue; for(int i = 0; i < valueNode.size(); i++){ JsonNode jsonObj = valueNode.get(i); String subSource = jsonObj.get(prefillingMapping.getSubSource()).asText(); parsedValue = subSource.replaceAll(trimRegex, ""); parsedValues.add(parsedValue); } parsedValues = parsedValues.stream().distinct().collect(Collectors.toList()); } String parsedValue = null; if (valueNode.isTextual()) { parsedValue = valueNode.textValue().replace(trimRegex, ""); }else if (valueNode.isArray() && (valueNode.get(0) != null && valueNode.get(0).isTextual())) { List values = new LinkedList<>(); for (int i = 0; i < valueNode.size(); i++) { values.add(valueNode.get(i).textValue().replace(trimRegex, "")); } parsedValue = String.join(", ", values); } if (prefillingMapping.getTarget() != null) { try { String methodName = "set" + prefillingMapping.getTarget().substring(0, 1).toUpperCase(Locale.ROOT) + prefillingMapping.getTarget().substring(1); Method setterMethod = Arrays.stream(DatasetWizardModel.class.getDeclaredMethods()) .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()).get(0); Class[] params = setterMethod.getParameterTypes(); //GK: Tags Special logic if (parsedValue != null && !parsedValue.equals("null") && prefillingMapping.getTarget().equals("tags")) { parsedValue = mapper.valueToTree(parseTags(parsedValue)).toString(); } else if(!parsedValues.isEmpty() && prefillingMapping.getTarget().equals("tags")) { parsedValue = mapper.valueToTree(parseTags(String.join(", ", parsedValues))).toString(); } else { parsedValue = mapper.valueToTree(parsedValue).toString(); } setterMethod.invoke(datasetWizardModel, mapper.readValue(parsedValue, params[0])); }catch (InvocationTargetException | IllegalAccessException | JsonProcessingException e) { throw e; } } else { List nodes = JsonSearcher.findNodes(parentNode, "schematics", prefillingMapping.getSemanticTarget()); // zenodo prefilling customizations if(type.equals("zenodo")){ if(prefillingMapping.getSemanticTarget().equals("rda.dataset.distribution.data_access")){ if(parsedValue != null && parsedValue.equals("open")){ List issuedNodes = JsonSearcher.findNodes(parentNode, "schematics", "rda.dataset.issued"); if(!issuedNodes.isEmpty()){ String issuedIdNode = issuedNodes.get(0).get("id").asText(); String issuedValue = (String) properties.get(issuedIdNode); List licStartDateNodes = JsonSearcher.findNodes(parentNode, "schematics", "rda.dataset.distribution.license.start_date"); for (JsonNode licStartDateNode : licStartDateNodes) { String licStartDateId = licStartDateNode.get(0) != null ? licStartDateNode.get(0).get("id").asText() : licStartDateNode.get("id").asText(); properties.put(licStartDateId, issuedValue); } } } } if (prefillingMapping.getSemanticTarget().equals("rda.dataset.distribution.available_until") && parsedValue != null && !parsedValue.equals("null")) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd"); LocalDate date = LocalDate.parse(parsedValue, formatter); date = date.plusYears(20); parsedValue = date.toString(); } } for (JsonNode node: nodes) { String id = node.isArray() ? node.get(0).get("id").asText() : node.get("id").asText(); String renderStyle = node.isArray() ? node.get(0).get("viewStyle").get("renderStyle").asText() : node.get("viewStyle").get("renderStyle").asText(); switch (RenderStyle.fromValue(renderStyle)) { case COMBO_BOX: if (parsedValues.isEmpty()) parsedValues.add(parsedValue); if (!parsedValues.stream().allMatch(Objects::isNull)) { properties.put(id, parseComboBoxValues(node, parsedValues)); } break; case TAGS: if(parsedValues.isEmpty()) { properties.put(id, mapper.valueToTree(parseTags(parsedValue)).toString()); } else { properties.put(id, mapper.valueToTree(parseTags(String.join(", ", parsedValues))).toString()); } break; case DATASET_IDENTIFIER: JSONObject datasetID = new JSONObject(); datasetID.put("identifier", parsedValue); if(type.equals("zenodo")){ datasetID.put("type", "doi"); } properties.put(id, datasetID.toString()); break; case LICENSES: try { List licenses = licenseManager.getLicenses("", ""); String finalParsedValue = parsedValue; licenses = licenses.stream().filter(license -> license.getPid().equals(finalParsedValue)).collect(Collectors.toList()); boolean isMultiAutocomplete = node.isArray() ? node.get(0).get("data").get("multiAutoComplete").booleanValue() : node.get("data").get("multiAutoComplete").booleanValue(); if(isMultiAutocomplete){ List lic = new ArrayList<>(); for(LicenseModel license: licenses){ lic.add(mapper.writeValueAsString(license)); } properties.put(id, mapper.writeValueAsString(lic)); } else { properties.put(id, mapper.writeValueAsString(licenses.get(0))); } } catch (NoURLFound | HugeResultSet e){ properties.put(id, parsedValue); } break; default: if (!parsedValues.isEmpty()) properties.put(id, String.join(", ", parsedValues)); else { properties.put(id, parsedValue); } break; } } } } private Object parseComboBoxValues(JsonNode node, List parsedValues) throws JsonProcessingException { List normalizedValues = new ArrayList<>(); boolean isMultiSelect; String type = node.isArray() ? node.get(0).get("data").get("type").asText() : node.get("data").get("type").asText(); if(type.equals("autocomplete")) { JsonNode dataNode = node.isArray() ? node.get(0).get("data") : node.get("data"); AutoCompleteData autoCompleteData = mapper.treeToValue(dataNode, AutoCompleteData.class); isMultiSelect = autoCompleteData.getMultiAutoComplete(); for (String format : parsedValues) { List result = new ArrayList<>(); try { result = datasetProfileManager.getAutocomplete(autoCompleteData, format); } catch (Exception e) { logger.error(e.getMessage(), e); } result = result.stream().filter(StreamDistinctBy.distinctByKey(ExternalAutocompleteFieldModel::getId)).collect(Collectors.toList()); if(!result.isEmpty()){ List tempValues = new LinkedList<>(); for (ExternalAutocompleteFieldModel f : result) { if (format.equals(f.getId()) || f.getLabel().toUpperCase(Locale.ROOT).contains(format.toUpperCase(Locale.ROOT))) tempValues.add(mapper.valueToTree(f).toString()); } if (isMultiSelect) normalizedValues.addAll(tempValues); else if (!tempValues.isEmpty()) normalizedValues.add(tempValues.get(0)); } } return !normalizedValues.isEmpty() ? (isMultiSelect ? normalizedValues : normalizedValues.get(0)) : null; } else { JsonNode optionsNode = node.isArray() ? node.get(0).get("data").get("options") : node.get("data").get("options"); isMultiSelect = node.isArray() ? node.get(0).get("data").get("multiList").booleanValue() : node.get("data").get("multiList").booleanValue(); for (int i = 0; i < optionsNode.size(); i++) { String value = optionsNode.get(i).get("value").textValue(); if (parsedValues.contains(value)) { normalizedValues.add(value); } } List normalizedStringValues = normalizedValues.stream().map(Object::toString).collect(Collectors.toList()); return !normalizedValues.isEmpty() ? (isMultiSelect ? String.join(", ", normalizedStringValues) : normalizedValues.get(0)) : null; } } private static List parseTags(String value) throws JsonProcessingException { if (value == null || value.isEmpty()) return new LinkedList<>(); String[] rawTags = value.split(", "); List parsedTags = new LinkedList<>(); for (String rawTag : rawTags) { parsedTags.add(new Tag(rawTag, rawTag)); } return parsedTags; } }