Skip to content

Commit 0bbdc00

Browse files
issue 1241: ItemFilesMetadataRepair script implementation (#1243) (#1021)
* issue 1241: ItemFilesMetadataRepair script implementation * extend script to be applicabble for all items, and for items with files metadata that have missing bitstreams (files) * implement dry-run option * option description fix * Improve error message * Use "0" instead of "" + 0 * Improve error message (cherry picked from commit 706f6f6) Co-authored-by: kuchtiak-ufal <kuchtiak@ufal.mff.cuni.cz>
1 parent 90f7f7f commit 0bbdc00

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
2+
/**
3+
* The contents of this file are subject to the license and copyright
4+
* detailed in the LICENSE and NOTICE files at the root of the source
5+
* tree and available online at
6+
*
7+
* http://www.dspace.org/license/
8+
*/
9+
package org.dspace.app.itemupdate;
10+
11+
import java.util.Iterator;
12+
import java.util.List;
13+
import java.util.UUID;
14+
15+
import org.apache.commons.cli.CommandLine;
16+
import org.apache.commons.cli.CommandLineParser;
17+
import org.apache.commons.cli.DefaultParser;
18+
import org.apache.commons.cli.HelpFormatter;
19+
import org.apache.commons.cli.Options;
20+
import org.apache.commons.cli.ParseException;
21+
import org.apache.commons.collections4.CollectionUtils;
22+
import org.apache.logging.log4j.LogManager;
23+
import org.apache.logging.log4j.Logger;
24+
import org.dspace.content.Bundle;
25+
import org.dspace.content.Collection;
26+
import org.dspace.content.Item;
27+
import org.dspace.content.MetadataValue;
28+
import org.dspace.content.factory.ClarinServiceFactory;
29+
import org.dspace.content.factory.ContentServiceFactory;
30+
import org.dspace.content.service.CollectionService;
31+
import org.dspace.content.service.ItemService;
32+
import org.dspace.content.service.clarin.ClarinItemService;
33+
import org.dspace.core.Constants;
34+
import org.dspace.core.Context;
35+
import org.dspace.eperson.EPerson;
36+
import org.dspace.eperson.factory.EPersonServiceFactory;
37+
38+
public class ItemFilesMetadataRepair {
39+
40+
private static final Logger log = LogManager.getLogger(ItemFilesMetadataRepair.class);
41+
42+
private ItemFilesMetadataRepair() {
43+
}
44+
45+
public static void main(String[] args) throws Exception {
46+
log.info("Fixing item files metadata started.");
47+
48+
Options options = new Options();
49+
options.addRequiredOption("e", "email", true, "admin email");
50+
options.addOption("c", "collection", true, "collection UUID");
51+
options.addOption("i", "item", true, "item UUID");
52+
options.addOption("d", "dry-run", false, "dry run - with no repair");
53+
options.addOption("h", "help", false, "help");
54+
options.addOption("v", "verbose", false, "verbose output");
55+
56+
CommandLineParser parser = new DefaultParser();
57+
try {
58+
CommandLine line = parser.parse(options, args);
59+
if (line.hasOption('h') || !line.hasOption('e')) {
60+
printHelpAndExit(options);
61+
}
62+
String adminEmail = line.getOptionValue('e');
63+
String collectionUuid = line.getOptionValue('c');
64+
String itemUuid = line.getOptionValue('i');
65+
boolean verboseOutput = line.hasOption('v');
66+
boolean dryRun = line.hasOption('d');
67+
run(adminEmail, collectionUuid, itemUuid, dryRun, verboseOutput);
68+
} catch (ParseException e) {
69+
System.err.println("Failed to parse command line options: " + e.getMessage());
70+
printHelpAndExit(options);
71+
}
72+
73+
log.info("Fixing item files metadata finished.");
74+
}
75+
76+
private static void run(String adminEmail,
77+
String collectionUuid,
78+
String itemUuid,
79+
boolean dryRun,
80+
boolean verboseOutput) throws Exception {
81+
82+
System.out.println("ItemFilesMetadataRepair Started.\n");
83+
84+
try (Context context = new Context(Context.Mode.READ_WRITE)) {
85+
ItemService itemService = ContentServiceFactory.getInstance().getItemService();
86+
ClarinItemService clarinItemService = ClarinServiceFactory.getInstance().getClarinItemService();
87+
88+
EPerson eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, adminEmail);
89+
context.turnOffAuthorisationSystem();
90+
context.setCurrentUser(eperson);
91+
context.restoreAuthSystemState();
92+
93+
String messagePrefix = dryRun ? "Found incorrect files metadata in" : "Updated";
94+
if (itemUuid != null) {
95+
// fixing only one item
96+
Item item = itemService.find(context, UUID.fromString(itemUuid));
97+
if (item == null) {
98+
throw new IllegalArgumentException("Item not found with the provided UUID");
99+
}
100+
boolean updated = updateItem(item, context, clarinItemService, itemService, dryRun, verboseOutput);
101+
if (updated) {
102+
System.out.println(dryRun ? "Files metadata are incorrect." : "Files metadata were updated.");
103+
} else {
104+
System.out.println("Files metadata are correct.");
105+
}
106+
} else if (collectionUuid != null) {
107+
// fixing items in collection
108+
CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
109+
Collection collection = collectionService.find(context, UUID.fromString(collectionUuid));
110+
if (collection == null) {
111+
throw new IllegalArgumentException("Invalid Collection UUID");
112+
}
113+
Iterator<Item> itemIterator = itemService.findAllByCollection(context, collection);
114+
Results results =
115+
updateItems(itemIterator, context, clarinItemService, itemService, dryRun, verboseOutput);
116+
System.out.printf("Checked %d items in Collection: \"%s\".\n",
117+
results.getItemsCount(), collection.getName());
118+
System.out.printf("%s %d items.\n", messagePrefix, results.getUpdatedItemsCount());
119+
} else {
120+
// fixing all items
121+
Iterator<Item> itemIterator = itemService.findAll(context);
122+
Results results =
123+
updateItems(itemIterator, context, clarinItemService, itemService, dryRun, verboseOutput);
124+
System.out.printf("Checked %d items.\n", results.getItemsCount());
125+
System.out.printf("%s %d items.\n", messagePrefix, results.getUpdatedItemsCount());
126+
}
127+
context.complete();
128+
}
129+
130+
System.out.println("\nItemFilesMetadataRepair Finished");
131+
}
132+
133+
private static Results updateItems(Iterator<Item> itemIterator,
134+
Context context,
135+
ClarinItemService clarinItemService,
136+
ItemService itemService,
137+
boolean dryRun,
138+
boolean verboseOutput) throws Exception {
139+
int itemsCount = 0;
140+
int updatedItemsCount = 0;
141+
while (itemIterator.hasNext()) {
142+
itemsCount++;
143+
boolean updated =
144+
updateItem(itemIterator.next(), context, clarinItemService, itemService, dryRun, verboseOutput);
145+
if (updated) {
146+
updatedItemsCount++;
147+
}
148+
}
149+
150+
return new Results(itemsCount, updatedItemsCount);
151+
}
152+
153+
private static boolean updateItem(Item item,
154+
Context context,
155+
ClarinItemService clarinItemService,
156+
ItemService itemService,
157+
boolean dryRun,
158+
boolean verboseOutput) throws Exception {
159+
boolean updated = false;
160+
161+
List<MetadataValue> filesCountValues =
162+
itemService.getMetadata(item, "local", "files", "count", Item.ANY);
163+
List<MetadataValue> filesSizeValues =
164+
itemService.getMetadata(item, "local", "files", "size", Item.ANY);
165+
List<MetadataValue> hasFilesValues =
166+
itemService.getMetadata(item, "local", "has", "files", Item.ANY);
167+
168+
int filesCount = 0;
169+
String filesCountValue = "undefined";
170+
if (!filesCountValues.isEmpty()) {
171+
filesCountValue = filesCountValues.get(0).getValue();
172+
try {
173+
filesCount = Integer.parseInt(filesCountValue);
174+
} catch (NumberFormatException ex) {
175+
// filesCount = 0
176+
}
177+
}
178+
long filesSize = 0;
179+
String filesSizeValue = "undefined";
180+
if (!filesSizeValues.isEmpty()) {
181+
filesSizeValue = filesSizeValues.get(0).getValue();
182+
try {
183+
filesSize = Long.parseLong(filesSizeValue);
184+
} catch (NumberFormatException ex) {
185+
// filesSize = 0
186+
}
187+
}
188+
String hasFiles = hasFilesValues.isEmpty() ? "no" : hasFilesValues.get(0).getValue();
189+
190+
List<Bundle> originalBundles = item.getBundles(Constants.CONTENT_BUNDLE_NAME);
191+
if (!CollectionUtils.isEmpty(originalBundles)) {
192+
Bundle bundle = originalBundles.get(0);
193+
boolean hasBitstreams = !CollectionUtils.isEmpty(bundle.getBitstreams());
194+
if (hasBitstreams && (filesCount == 0 || filesSize == 0 || !"yes".equals(hasFiles))) {
195+
if (verboseOutput) {
196+
String message = "Incorrect metadata: [files.count: %s, files.size: %s, has.files: %s], " +
197+
"in item '%s' with files.";
198+
System.out.printf((message) + "%n", filesCountValue, filesSizeValue, hasFiles, item.getHandle());
199+
}
200+
if (!dryRun) {
201+
clarinItemService.updateItemFilesMetadata(context, item, bundle);
202+
}
203+
updated = true;
204+
} else if (!hasBitstreams && (filesCount > 0 || filesSize > 0 || "yes".equals(hasFiles))) {
205+
if (verboseOutput) {
206+
String message = "Incorrect metadata: [files.count: %s, files.size: %s, has.files: %s], " +
207+
"in item '%s' without files.";
208+
System.out.printf((message) + "%n", filesCountValue, filesSizeValue, hasFiles, item.getHandle());
209+
}
210+
if (!dryRun) {
211+
itemService.clearMetadata(
212+
context, item, "local", "has", "files", Item.ANY);
213+
itemService.clearMetadata(
214+
context, item, "local", "files", "count", Item.ANY);
215+
itemService.clearMetadata(
216+
context, item, "local", "files", "size", Item.ANY);
217+
itemService.addMetadata(
218+
context, item, "local", "has", "files", Item.ANY, "no");
219+
itemService.addMetadata(
220+
context, item, "local", "files", "count", Item.ANY, "0");
221+
itemService.addMetadata(
222+
context, item, "local", "files", "size", Item.ANY, "0");
223+
}
224+
updated = true;
225+
}
226+
}
227+
return updated;
228+
}
229+
230+
private static void printHelpAndExit(Options options) {
231+
// print the help message
232+
HelpFormatter myHelp = new HelpFormatter();
233+
myHelp.printHelp("dsrun org.dspace.app.itemupdate.ItemFilesMetadataRepair \n", options);
234+
System.exit(0);
235+
}
236+
237+
private static class Results {
238+
private final int itemsCount;
239+
private final int updatedItemsCount;
240+
241+
public Results(int itemsCount, int updatedItemsCount) {
242+
this.itemsCount = itemsCount;
243+
this.updatedItemsCount = updatedItemsCount;
244+
}
245+
246+
public int getItemsCount() {
247+
return itemsCount;
248+
}
249+
250+
public int getUpdatedItemsCount() {
251+
return updatedItemsCount;
252+
}
253+
}
254+
255+
}

0 commit comments

Comments
 (0)