Skip to content

Commit

Permalink
Merge branch 'dspace-cris-7' into dspace-cris-7-relationmetadatagroup
Browse files Browse the repository at this point in the history
  • Loading branch information
floriangantner committed Oct 13, 2023
2 parents b54bcb6 + 0dc58de commit 470c0f6
Show file tree
Hide file tree
Showing 168 changed files with 8,642 additions and 503 deletions.
2 changes: 1 addition & 1 deletion dspace-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>cris-2023.01.01-SNAPSHOT</version>
<version>cris-2023.02.00-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

Expand Down
19 changes: 11 additions & 8 deletions dspace-api/src/main/java/org/dspace/app/bulkedit/BulkImport.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import static org.apache.commons.lang3.StringUtils.isAllBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.split;
import static org.apache.commons.lang3.StringUtils.splitByWholeSeparator;
import static org.apache.commons.lang3.StringUtils.startsWith;
import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage;
import static org.apache.commons.lang3.math.NumberUtils.isCreatable;
Expand Down Expand Up @@ -609,7 +609,8 @@ private boolean areMetadataValuesValid(Row row, boolean manyMetadataValuesAllowe
for (int index = firstMetadataIndex; index < row.getLastCellNum(); index++) {

String cellValue = WorkbookUtils.getCellValue(row, index);
String[] values = isNotBlank(cellValue) ? split(cellValue, METADATA_SEPARATOR) : new String[] { "" };
String[] values = isNotBlank(cellValue) ? splitByWholeSeparator(cellValue, METADATA_SEPARATOR)
: new String[] { "" };
if (values.length > 1 && !manyMetadataValuesAllowed) {
handleValidationErrorOnRow(row, "Multiple metadata value on the same cell not allowed "
+ "in the metadata group sheets: " + cellValue);
Expand Down Expand Up @@ -743,7 +744,7 @@ private List<String> validateAccessConditions(Row row) {
Map<String, AccessConditionOption> accessConditionOptions = getUploadAccessConditions();

return Arrays.stream(getAccessConditionValues(row))
.map(accessCondition -> split(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)[0])
.map(accessCondition -> splitByWholeSeparator(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)[0])
.filter(accessConditionName -> !accessConditionOptions.containsKey(accessConditionName))
.collect(Collectors.toList());
}
Expand Down Expand Up @@ -788,14 +789,14 @@ private List<AccessCondition> buildAccessConditions(Row row, String[] accessCond
}

return Arrays.stream(accessConditions)
.map(accessCondition -> split(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR))
.map(accessCondition -> splitByWholeSeparator(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR))
.map(accessConditionAttributes -> buildAccessCondition(accessConditionAttributes))
.collect(Collectors.toList());
}

private String[] getAccessConditionValues(Row row) {
String accessConditionCellValue = getCellValue(row, ACCESS_CONDITION_HEADER);
return split(accessConditionCellValue, METADATA_SEPARATOR);
return splitByWholeSeparator(accessConditionCellValue, METADATA_SEPARATOR);
}

private AccessCondition buildAccessCondition(String[] accessCondition) {
Expand Down Expand Up @@ -1306,12 +1307,13 @@ private void removeSingleMetadata(DSpaceObject dso, MetadataField field, String
}

private String getMetadataField(String field) {
return field.contains(LANGUAGE_SEPARATOR_PREFIX) ? split(field, LANGUAGE_SEPARATOR_PREFIX)[0] : field;
return field.contains(LANGUAGE_SEPARATOR_PREFIX) ? splitByWholeSeparator(field, LANGUAGE_SEPARATOR_PREFIX)[0]
: field;
}

private String getMetadataLanguage(String field) {
if (field.contains(LANGUAGE_SEPARATOR_PREFIX)) {
return split(field, LANGUAGE_SEPARATOR_PREFIX)[1].replace(LANGUAGE_SEPARATOR_SUFFIX, "");
return splitByWholeSeparator(field, LANGUAGE_SEPARATOR_PREFIX)[1].replace(LANGUAGE_SEPARATOR_SUFFIX, "");
}
return null;
}
Expand Down Expand Up @@ -1364,7 +1366,8 @@ private MultiValuedMap<String, MetadataValueVO> getMetadataFromRow(Row row, Map<
if (index >= firstMetadataIndex) {

String cellValue = WorkbookUtils.getCellValue(row, index);
String[] values = isNotBlank(cellValue) ? split(cellValue, METADATA_SEPARATOR) : new String[] { "" };
String[] values = isNotBlank(cellValue) ? splitByWholeSeparator(cellValue, METADATA_SEPARATOR)
: new String[] { "" };

List<MetadataValueVO> metadataValues = Arrays.stream(values)
.map(value -> buildMetadataValueVO(row, value, isMetadataGroupsSheet))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
Expand Down Expand Up @@ -107,7 +108,12 @@ public void appendValueOnLastRow(String header, String value, String separator)
throw new IllegalArgumentException("Unknown header '" + header + "'");
}
String cellContent = WorkbookUtils.getCellValue(lastRow, column);
createCell(lastRow, column, isEmpty(cellContent) ? value : cellContent + separator + value);
createCell(lastRow, column,
getValueLimitedByLength(isEmpty(cellContent) ? value : cellContent + separator + value));
}

private String getValueLimitedByLength(String value) {
return StringUtils.length(value) > 32726 ? value.substring(0, 32725) + "…" : value;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.filetype.consumer;

import static org.dspace.util.FunctionalUtils.throwingConsumerWrapper;
import static org.dspace.util.FunctionalUtils.throwingMapperWrapper;

import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.codec.binary.StringUtils;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataFieldName;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.exception.SQLRuntimeException;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileTypeMetadataEnhancerConsumer implements Consumer {

private static final Logger logger = LoggerFactory.getLogger(FileTypeMetadataEnhancerConsumer.class);

protected static final MetadataFieldName entityTypeMetadata = new MetadataFieldName("dc", "type");
protected static final MetadataFieldName fileTypeMetadata = new MetadataFieldName("dspace", "file", "type");
private static final List<MetadataFieldName> itemMetadatas = List.of(fileTypeMetadata);
private static final List<MetadataFieldName> bitstreamMetadatas = List.of(entityTypeMetadata);
private static final Map<String, MetadataFieldName> bitstreamToItemMetadatasMap = Map.of(
entityTypeMetadata.toString(), fileTypeMetadata
);

private BitstreamService bitstreamService;
private ItemService itemService;

private Set<Bitstream> bitstreamAlreadyProcessed = new HashSet<>();
private Set<Item> itemsToProcess = new HashSet<>();

@Override
public void initialize() throws Exception {
this.bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
this.itemService = ContentServiceFactory.getInstance().getItemService();
}

@Override
public void consume(Context ctx, Event event) throws Exception {
if (Constants.BITSTREAM == event.getSubjectType()) {
this.handleBitStreamConsumer(
ctx,
Optional.ofNullable((Bitstream) event.getObject(ctx))
.orElse(this.loadBitstream(ctx, event)),
event
);
} else if (Constants.ITEM == event.getSubjectType() && Event.CREATE == event.getEventType()) {
this.handleItemConsumer(
ctx,
Optional.ofNullable((Item) event.getObject(ctx))
.orElse(this.loadItem(ctx, event))
);
} else {
logger.warn(
"Can't consume the DSPaceObject with id {}, only BITSTREAM and ITEMS'CREATION events are consumable!",
event.getSubjectID()
);
}
}

@Override
public void end(Context ctx) throws Exception {
bitstreamAlreadyProcessed.clear();
this.itemsToProcess
.stream()
.forEach(item -> this.handleItemConsumer(ctx, item));
itemsToProcess.clear();
}

@Override
public void finish(Context ctx) throws Exception {}

private Bitstream loadBitstream(Context ctx, Event event) {
Bitstream found = null;
try {
found = this.bitstreamService.find(ctx, event.getSubjectID());
} catch (SQLException e) {
logger.error("Error while retrieving the bitstream with ID: " + event.getSubjectID(), e);
throw new SQLRuntimeException("Error while retrieving the bitstream with ID: " + event.getSubjectID(), e);
}
return found;
}

private Item loadItem(Context ctx, Event event) {
Item found = null;
try {
found = this.itemService.find(ctx, event.getSubjectID());
} catch (SQLException e) {
logger.error("Error while retrieving the bitstream with ID: " + event.getSubjectID(), e);
throw new SQLRuntimeException("Error while retrieving the bitstream with ID: " + event.getSubjectID(), e);
}
return found;
}

private void handleBitStreamConsumer(Context ctx, Bitstream bitstream, Event event) {

if (bitstream == null || this.alreadyProcessed(bitstream)) {
return;
}
List<Item> bitstreamItems = List.of();
try {
bitstreamItems = bitstream.getBundles()
.stream()
.filter(bundle -> "ORIGINAL".equals(bundle.getName()))
.map(Bundle::getItems)
.flatMap(Collection::stream)
.collect(Collectors.toList());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
bitstreamAlreadyProcessed.add(bitstream);
bitstreamItems
.stream()
.forEach(item -> this.itemsToProcess.add(item));
}
}

private void handleItemConsumer(Context ctx, Item item) {

if (item == null) {
return;
}

try {
Item loadedItem = this.itemService.find(ctx, item.getID());
Map<MetadataField, List<String>> grouped =
Optional.ofNullable(loadedItem)
.map(i -> i.getBundles("ORIGINAL"))
.filter(bundles -> !bundles.isEmpty())
.map(bundles -> bundles.get(0))
.map(Bundle::getBitstreams)
.filter(bitstreams -> !bitstreams.isEmpty())
.map(bitstreams -> getMetadatasForItem(ctx, bitstreams).collect(Collectors.toList()))
.map(metadatas -> groupByMetadataField(metadatas))
.filter(metadatas -> !metadatas.isEmpty())
.orElse(Map.of());

this.itemService.removeMetadataValues(ctx, loadedItem, getRemovableMetadatas(loadedItem));

grouped
.entrySet()
.stream()
.map(entry ->
Map.entry(bitstreamToItemMetadatasMap.get(entry.getKey().toString('.')), entry.getValue())
)
.filter(entry -> entry.getKey() != null)
.forEach(
throwingConsumerWrapper(entry ->
this.addMetadata(
ctx,
loadedItem,
entry.getKey(),
entry.getValue()
)
)
);

} catch (SQLException e) {
logger.error(MessageFormat.format("Error while processing item {}!", item.getID().toString()), e);
throw new SQLRuntimeException(e);
}

}

private void addMetadata(Context ctx, Item loadedItem, MetadataFieldName metadata, List<String> value)
throws SQLException {
this.itemService.addMetadata(
ctx,
loadedItem,
metadata.schema,
metadata.element,
metadata.qualifier,
null,
value
);
}

private Stream<MetadataValue> getMetadatasForItem(Context ctx, List<Bitstream> bitstreams) {
return bitstreams
.stream()
.map(
throwingMapperWrapper(bitstream ->
this.bitstreamService.find(ctx, bitstream.getID()),
null
)
)
.filter(Objects::nonNull)
.flatMap(bitstream -> filterBitstreamMetadatasForItem(bitstream));
}

private Stream<MetadataValue> filterBitstreamMetadatasForItem(Bitstream bitstream) {
return bitstream.getMetadata()
.stream()
.filter(
metadataFilter(
bitstreamMetadatas
)
);
}

private Map<MetadataField, List<String>> groupByMetadataField(List<MetadataValue> metadatas) {
return this.collectByGroupingMetadataFieldMappingValue(metadatas.stream());
}

private Map<MetadataField, List<String>> collectByGroupingMetadataFieldMappingValue(Stream<MetadataValue> stream) {
return stream
.collect(
Collectors.groupingBy(
MetadataValue::getMetadataField,
Collectors.mapping(MetadataValue::getValue, Collectors.toList())
)
);
}

private boolean alreadyProcessed(Bitstream bitstream) {
return bitstreamAlreadyProcessed.contains(bitstream);
}

private List<MetadataValue> getRemovableMetadatas(DSpaceObject dspaceObject) {
return dspaceObject
.getMetadata()
.stream()
.filter(
metadataFilter(
itemMetadatas
)
)
.collect(Collectors.toList());
}

private Predicate<? super MetadataValue> metadataFilter(List<MetadataFieldName> metadataFields) {
return metadata ->
metadataFields
.stream()
.filter(field ->
StringUtils.equals(field.schema, metadata.getSchema()) &&
StringUtils.equals(field.element, metadata.getElement()) &&
StringUtils.equals(field.qualifier, metadata.getQualifier())
)
.findFirst()
.isPresent();
}
}
Loading

0 comments on commit 470c0f6

Please sign in to comment.