Skip to content

Commit aa5e63e

Browse files
authored
Bxc 4605 csv export parent info (#1761)
* BXC-4605 update export csv * BXC-4605 update tests and add correct info for parent work * BXC-4605 update imports * BXC-4605 fix up tests and add headers * BXC-4605 fixing last test * BXC-4605 standardie URLs
1 parent 53efbbf commit aa5e63e

File tree

4 files changed

+144
-72
lines changed

4 files changed

+144
-72
lines changed

web-services-app/src/main/java/edu/unc/lib/boxc/web/services/processing/ExportCsvService.java

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,14 @@
11
package edu.unc.lib.boxc.web.services.processing;
22

3-
import static edu.unc.lib.boxc.auth.api.AccessPrincipalConstants.AUTHENTICATED_PRINC;
4-
import static edu.unc.lib.boxc.auth.api.AccessPrincipalConstants.PUBLIC_PRINC;
5-
import static edu.unc.lib.boxc.auth.api.Permission.viewHidden;
6-
import static edu.unc.lib.boxc.auth.api.UserRole.canViewOriginals;
7-
import static edu.unc.lib.boxc.auth.api.UserRole.none;
8-
import static edu.unc.lib.boxc.model.api.DatastreamType.ACCESS_SURROGATE;
9-
import static edu.unc.lib.boxc.model.api.DatastreamType.ORIGINAL_FILE;
10-
import static edu.unc.lib.boxc.model.api.ids.RepositoryPathConstants.CONTENT_ROOT_ID;
11-
import static edu.unc.lib.boxc.model.api.rdf.CdrAcl.embargoUntil;
12-
import static edu.unc.lib.boxc.search.api.FacetConstants.MARKED_FOR_DELETION;
13-
14-
import java.io.BufferedWriter;
15-
import java.io.IOException;
16-
import java.io.OutputStream;
17-
import java.io.OutputStreamWriter;
18-
import java.io.Writer;
19-
import java.nio.charset.StandardCharsets;
20-
import java.text.SimpleDateFormat;
21-
import java.util.Arrays;
22-
import java.util.Date;
23-
import java.util.HashMap;
24-
import java.util.List;
25-
import java.util.Map;
26-
import java.util.Objects;
27-
28-
import edu.unc.lib.boxc.model.api.ResourceType;
29-
import edu.unc.lib.boxc.model.api.objects.RepositoryObjectLoader;
30-
import org.apache.commons.csv.CSVFormat;
31-
import org.apache.commons.csv.CSVPrinter;
32-
import org.apache.commons.lang3.StringUtils;
33-
import org.slf4j.Logger;
34-
import org.slf4j.LoggerFactory;
35-
363
import edu.unc.lib.boxc.auth.api.models.AccessGroupSet;
374
import edu.unc.lib.boxc.auth.api.models.AgentPrincipals;
385
import edu.unc.lib.boxc.auth.api.services.AccessControlService;
6+
import edu.unc.lib.boxc.common.util.URIUtil;
7+
import edu.unc.lib.boxc.model.api.ResourceType;
398
import edu.unc.lib.boxc.model.api.exceptions.NotFoundException;
409
import edu.unc.lib.boxc.model.api.exceptions.RepositoryException;
4110
import edu.unc.lib.boxc.model.api.ids.PID;
11+
import edu.unc.lib.boxc.model.api.objects.RepositoryObjectLoader;
4212
import edu.unc.lib.boxc.search.api.FacetConstants;
4313
import edu.unc.lib.boxc.search.api.SearchFieldKey;
4414
import edu.unc.lib.boxc.search.api.models.ContentObjectRecord;
@@ -48,6 +18,35 @@
4818
import edu.unc.lib.boxc.search.solr.responses.SearchResultResponse;
4919
import edu.unc.lib.boxc.search.solr.services.ChildrenCountService;
5020
import edu.unc.lib.boxc.web.common.services.SolrQueryLayerService;
21+
import org.apache.commons.csv.CSVFormat;
22+
import org.apache.commons.csv.CSVPrinter;
23+
import org.apache.commons.lang3.StringUtils;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import java.io.BufferedWriter;
28+
import java.io.IOException;
29+
import java.io.OutputStream;
30+
import java.io.OutputStreamWriter;
31+
import java.io.Writer;
32+
import java.nio.charset.StandardCharsets;
33+
import java.text.SimpleDateFormat;
34+
import java.util.Arrays;
35+
import java.util.Date;
36+
import java.util.HashMap;
37+
import java.util.List;
38+
import java.util.Map;
39+
import java.util.Objects;
40+
41+
import static edu.unc.lib.boxc.auth.api.AccessPrincipalConstants.AUTHENTICATED_PRINC;
42+
import static edu.unc.lib.boxc.auth.api.AccessPrincipalConstants.PUBLIC_PRINC;
43+
import static edu.unc.lib.boxc.auth.api.Permission.viewHidden;
44+
import static edu.unc.lib.boxc.auth.api.UserRole.canViewOriginals;
45+
import static edu.unc.lib.boxc.auth.api.UserRole.none;
46+
import static edu.unc.lib.boxc.model.api.DatastreamType.ORIGINAL_FILE;
47+
import static edu.unc.lib.boxc.model.api.ids.RepositoryPathConstants.CONTENT_ROOT_ID;
48+
import static edu.unc.lib.boxc.model.api.rdf.CdrAcl.embargoUntil;
49+
import static edu.unc.lib.boxc.search.api.FacetConstants.MARKED_FOR_DELETION;
5150

5251
/**
5352
* Service which outputs a CSV listing of a repository object and all of its children,
@@ -77,12 +76,15 @@ public class ExportCsvService {
7776
public static final String PATRON_PERMISSIONS_HEADER = "Patron Permissions";
7877
public static final String EMBARGO_HEADER = "Embargo Date";
7978
public static final String VIEW_BEHAVIOR_HEADER = "View";
79+
public static final String PARENT_WORK_URL = "Parent Work URL";
80+
public static final String PARENT_WORK_TITLE = "Parent Work Title";
8081

8182
private static final String[] CSV_HEADERS = new String[] {
8283
OBJ_TYPE_HEADER, PID_HEADER, TITLE_HEADER, PATH_HEADER,
8384
DEPTH_HEADER, DELETED_HEADER, DATE_ADDED_HEADER, DATE_UPDATED_HEADER,
8485
MIME_TYPE_HEADER, CHECKSUM_HEADER, FILE_SIZE_HEADER, ACCESS_SURROGATE_HEADER, NUM_CHILDREN_HEADER,
85-
DESCRIBED_HEADER, PATRON_PERMISSIONS_HEADER, EMBARGO_HEADER, VIEW_BEHAVIOR_HEADER
86+
DESCRIBED_HEADER, PATRON_PERMISSIONS_HEADER, EMBARGO_HEADER, VIEW_BEHAVIOR_HEADER,
87+
PARENT_WORK_URL, PARENT_WORK_TITLE
8688
};
8789

8890
private static final List<String> SEARCH_FIELDS = Arrays.asList(SearchFieldKey.ID.name(),
@@ -99,6 +101,7 @@ public class ExportCsvService {
99101
private AccessControlService aclService;
100102
private SolrQueryLayerService queryLayer;
101103
private RepositoryObjectLoader repositoryObjectLoader;
104+
private String baseUrl;
102105
private int pageSize = DEFAULT_PAGE_SIZE;
103106

104107
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss");
@@ -224,7 +227,7 @@ private CSVPrinter getPrinter(Writer writer) throws IOException {
224227
}
225228

226229
private void printObject(CSVPrinter printer, ContentObjectRecord object) throws IOException {
227-
// Vitals: object type, pid, title, path, label, depth
230+
// Vitals: object type, pid, title, path, depth
228231
printer.print(object.getResourceType());
229232
printer.print(object.getId());
230233
printer.print(object.getTitle());
@@ -310,6 +313,18 @@ private void printObject(CSVPrinter printer, ContentObjectRecord object) throws
310313
var behavior = object.getViewBehavior();
311314
printer.print(Objects.requireNonNullElse(behavior, ""));
312315

316+
// Parent info for FileObjects
317+
if (ResourceType.File.name().equals(object.getResourceType())) {
318+
// parentWorkUrl, parentWorkTitle
319+
var parentWorkId = object.getAncestorPathFacet().getHighestTierNode().getSearchKey();
320+
printer.print(getUrl(parentWorkId));
321+
var parentWorkTitle = getTitle(object.getAncestorNames());
322+
printer.print(parentWorkTitle);
323+
} else {
324+
printer.print("");
325+
printer.print("");
326+
}
327+
313328
printer.println();
314329
}
315330

@@ -335,6 +350,27 @@ private String getEmbargoDate(ContentObjectRecord object) {
335350
return StringUtils.substringBefore(embargoProperty.getString(), "T");
336351
}
337352

353+
/**
354+
* Creating a record URL with just the ID
355+
* @param id
356+
* @return
357+
*/
358+
private String getUrl(String id) {
359+
return URIUtil.join(baseUrl, id);
360+
}
361+
362+
/**
363+
* Transforming ancestor path names to get the second to last one (for the work), but keep escaped slashes if necessary
364+
* @param input
365+
* @return
366+
*/
367+
private String getTitle(String input) {
368+
String regex = "(?<!\\\\)/";
369+
String[] result = input.split(regex);
370+
// for the last one escape any backslashes in the title
371+
return result[result.length - 2].replace("\\/", "/");
372+
}
373+
338374
public void setChildrenCountService(ChildrenCountService childrenCountService) {
339375
this.childrenCountService = childrenCountService;
340376
}
@@ -354,4 +390,8 @@ public void setPageSize(int pageSize) {
354390
public void setRepositoryObjectLoader(RepositoryObjectLoader repositoryObjectLoader) {
355391
this.repositoryObjectLoader = repositoryObjectLoader;
356392
}
393+
394+
public void setBaseUrl(String baseUrl) {
395+
this.baseUrl = baseUrl;
396+
}
357397
}

web-services-app/src/main/webapp/WEB-INF/service-context.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@
409409
<property name="aclService" ref="aclService"/>
410410
<property name="queryLayer" ref="queryLayer"/>
411411
<property name="repositoryObjectLoader" ref="repositoryObjectLoader"/>
412+
<property name="baseUrl" value="${base.record.url}"/>
412413
</bean>
413414

414415
<bean id="facetValuesService" class="edu.unc.lib.boxc.search.solr.services.FacetValuesService"

web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/modify/ExportCsvIT.java

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public void exportWorkWithFile() throws Exception {
197197
List<CSVRecord> csvList = parseCsvResponse(response);
198198
assertEquals(3, csvList.size(), "Unexpected number of results");
199199

200-
assertCsvContentIsCorrect(csvList, folderObj.getPid(), workPid, filePid);
200+
assertCsvContentIsCorrect(csvList, folderPid, workPid, filePid);
201201
}
202202

203203
@Test
@@ -221,15 +221,17 @@ public void exportWorkWithAccessSurrogate() throws Exception {
221221

222222
List<CSVRecord> csvList = parseCsvResponse(response);
223223

224-
String pathToWork = FOLDER_PATH + "/" + workPid.getId();
225-
assertCsvRecord(csvList, ResourceType.Work, workPid, "TestWork",
224+
var workId = workPid.getId();
225+
String pathToWork = FOLDER_PATH + "/" + workId;
226+
assertCsvRecord(csvList, ResourceType.Work, workPid, workId,
226227
pathToWork, 4, false, null, null, null, null,
227-
1, false, "Authenticated", "", "");
228+
1, false, "Authenticated", "", "", null, null);
228229

229-
String pathToFile = pathToWork + "/file.txt";
230-
assertCsvRecord(csvList, ResourceType.File, pidList.get("filePid"), "TestWork",
230+
var fileTitle = "file.txt";
231+
String pathToFile = pathToWork + "/" + fileTitle;
232+
assertCsvRecord(csvList, ResourceType.File, filePid, fileTitle,
231233
pathToFile, 5, false, "text/plain", null, (long) 7, "Y",
232-
null, false, "Authenticated", "", "");
234+
null, false, "Authenticated", "", "", getUrl(workPid.getId()), workId);
233235
}
234236

235237
@Test
@@ -268,12 +270,13 @@ public void exportDescribedResource() throws Exception {
268270
String pathToWork = pathToFolder + "/Work Test";
269271
assertCsvRecord(csvList, ResourceType.Work, workPid, "Work Test",
270272
pathToWork, 4, false, null, null, null, null,
271-
1, true, "Authenticated", "", "");
273+
1, true, "Authenticated", "", "", null, null);
272274

273-
String pathToFile = pathToWork + "/file.txt";
274-
assertCsvRecord(csvList, ResourceType.File, filePid, "TestWork2",
275+
var fileTitle = "file.txt";
276+
String pathToFile = pathToWork + "/" + fileTitle;
277+
assertCsvRecord(csvList, ResourceType.File, filePid, fileTitle,
275278
pathToFile, 5, false, "text/plain", null, (long) 7, null,
276-
null, false, "Authenticated", "", "");
279+
null, false, "Authenticated", "", "", getUrl(workPid.getId()), "Work Test");
277280
}
278281

279282
@Test
@@ -303,15 +306,17 @@ public void exportDeletedResource() throws Exception {
303306
assertContainerRecord(csvList, ResourceType.Folder, folderPid, "Folder",
304307
FOLDER_PATH, 3, true, 1, false, "Staff-only", "");
305308

306-
String pathToWork = FOLDER_PATH + "/" + workPid.getId();
307-
assertCsvRecord(csvList, ResourceType.Work, workPid, "TestWorkDeleted",
309+
var workId = workPid.getId();
310+
String pathToWork = FOLDER_PATH + "/" + workId;
311+
assertCsvRecord(csvList, ResourceType.Work, workPid, workId,
308312
pathToWork, 4, true, null, null, null, null,
309-
1, false, "Staff-only", "", "");
313+
1, false, "Staff-only", "", "", null, null);
310314

311-
String pathToFile = pathToWork + "/file.txt";
312-
assertCsvRecord(csvList, ResourceType.File, filePid, "TestWork2",
315+
var fileTitle = "file.txt";
316+
String pathToFile = pathToWork + "/" + fileTitle;
317+
assertCsvRecord(csvList, ResourceType.File, filePid, fileTitle,
313318
pathToFile, 5, true, "text/plain", null, (long) 7, null,
314-
null, false, "Staff-only", "", "");
319+
null, false, "Staff-only", "", "", getUrl(workPid.getId()), workId);
315320
}
316321

317322
@Test
@@ -335,10 +340,12 @@ public void exportFileResourceDirectly() throws Exception {
335340
List<CSVRecord> csvList = parseCsvResponse(response);
336341
assertEquals(1, csvList.size(), "Unexpected number of results");
337342

338-
String pathToFile = FOLDER_PATH + "/" + workPid.getId() + "/file.txt";
339-
assertCsvRecord(csvList, ResourceType.File, filePid, "TestWork3",
343+
var fileTitle = "file.txt";
344+
var workId = workPid.getId();
345+
String pathToFile = FOLDER_PATH + "/" + workId + "/file.txt";
346+
assertCsvRecord(csvList, ResourceType.File, filePid, fileTitle,
340347
pathToFile, 5, false, "text/plain", null, (long) 7, null,
341-
null, false, "Authenticated", "", "");
348+
null, false, "Authenticated", "", "", getUrl(workPid.getId()), workId);
342349
}
343350

344351
@Test
@@ -622,15 +629,17 @@ public void multiPageExport() throws Exception {
622629
assertContainerRecord(csvList, ResourceType.Folder, folderObj.getPid(), "Folder",
623630
FOLDER_PATH, 3, false, 1, false, "Authenticated", "");
624631

625-
String pathToWork = FOLDER_PATH + "/" + workPid.getId();
626-
assertCsvRecord(csvList, ResourceType.Work, workPid, "TestWork",
632+
var workId = workPid.getId();
633+
String pathToWork = FOLDER_PATH + "/" + workId;
634+
assertCsvRecord(csvList, ResourceType.Work, workPid, workId,
627635
pathToWork, 4, false, null, null, null, null,
628-
1, false, "Authenticated", "", "");
636+
1, false, "Authenticated", "", "", null, null);
629637

630-
String pathToFile = pathToWork + "/file.txt";
631-
assertCsvRecord(csvList, ResourceType.File, pidList.get("filePid"), "TestWork",
638+
var fileTitle = "file.txt";
639+
String pathToFile = pathToWork + "/" + fileTitle;
640+
assertCsvRecord(csvList, ResourceType.File, pidList.get("filePid"), fileTitle,
632641
pathToFile, 5, false, "text/plain", null, (long) 7, null,
633-
null, false, "Authenticated", "", "");
642+
null, false, "Authenticated", "", "", getUrl(workPid.getId()), workId);
634643
}
635644

636645
@Test
@@ -658,10 +667,11 @@ public void exportWorkWithViewBehavior() throws Exception {
658667
List<CSVRecord> csvList = parseCsvResponse(response);
659668
assertEquals(3, csvList.size(), "Unexpected number of results");
660669

661-
String pathToWork = FOLDER_PATH + "/" + workPid.getId();
662-
assertCsvRecord(csvList, ResourceType.Work, workPid, "TestWork",
670+
var workId = workPid.getId();
671+
String pathToWork = FOLDER_PATH + "/" + workId;
672+
assertCsvRecord(csvList, ResourceType.Work, workPid, workId,
663673
pathToWork, 4, false, null, null, null, null,
664-
1, false, "Authenticated", "", paged);
674+
1, false, "Authenticated", "", paged, null, null);
665675
}
666676

667677
@Test
@@ -784,13 +794,14 @@ private void assertContainerRecord(List<CSVRecord> csvList, ResourceType objType
784794
String path, int depth, boolean deleted, Integer numChildren, boolean described, String permissions,
785795
String embargoDate) {
786796
assertCsvRecord(csvList, objType, expectedPid, title, path, depth, deleted,
787-
null, null, null, null, numChildren, described, permissions, embargoDate, "");
797+
null, null, null, null, numChildren, described, permissions,
798+
embargoDate, "", "", "");
788799
}
789800

790801
private void assertCsvRecord(List<CSVRecord> csvList, ResourceType objType, PID expectedPid, String title,
791802
String path, int depth, boolean deleted, String mimetype, String checksum, Long fileSize,
792803
String accessSurrogate, Integer numChildren, boolean described, String permissions,
793-
String embargoDate, String behavior) {
804+
String embargoDate, String behavior, String parentWorkUrl, String parentWorkTitle) {
794805
path = path == null ? "" : path;
795806
mimetype = mimetype == null ? "" : mimetype;
796807
checksum = checksum == null ? "" : checksum;
@@ -801,6 +812,7 @@ private void assertCsvRecord(List<CSVRecord> csvList, ResourceType objType, PID
801812
continue;
802813
}
803814
assertEquals(objType.name(), rec.get(ExportCsvService.OBJ_TYPE_HEADER));
815+
assertEquals(title, rec.get(ExportCsvService.TITLE_HEADER));
804816
assertEquals(path, rec.get(ExportCsvService.PATH_HEADER));
805817
assertEquals(depth, Integer.parseInt(rec.get(ExportCsvService.DEPTH_HEADER)));
806818
assertEquals(deleted, Boolean.parseBoolean(rec.get(ExportCsvService.DELETED_HEADER)));
@@ -828,6 +840,18 @@ private void assertCsvRecord(List<CSVRecord> csvList, ResourceType objType, PID
828840
assertEquals(permissions, rec.get(ExportCsvService.PATRON_PERMISSIONS_HEADER));
829841
assertEquals(embargoDate, rec.get(ExportCsvService.EMBARGO_HEADER));
830842
assertEquals(behavior, rec.get(ExportCsvService.VIEW_BEHAVIOR_HEADER));
843+
844+
if (parentWorkUrl == null) {
845+
assertTrue(StringUtils.isBlank(rec.get(ExportCsvService.PARENT_WORK_URL)));
846+
} else {
847+
assertEquals(parentWorkUrl, rec.get(ExportCsvService.PARENT_WORK_URL));
848+
}
849+
850+
if (parentWorkTitle == null) {
851+
assertTrue(StringUtils.isBlank(rec.get(ExportCsvService.PARENT_WORK_TITLE)));
852+
} else {
853+
assertEquals(parentWorkTitle, rec.get(ExportCsvService.PARENT_WORK_TITLE));
854+
}
831855
return;
832856
}
833857
fail("No CSV record with PID " + expectedPid.getId() + " present");
@@ -837,15 +861,17 @@ private void assertCsvContentIsCorrect(List<CSVRecord> csvList, PID folderPid, P
837861
assertContainerRecord(csvList, ResourceType.Folder, folderPid, "Folder",
838862
FOLDER_PATH, 3, false, 1, false, "Authenticated", "");
839863

840-
String pathToWork = FOLDER_PATH + "/" + workPid.getId();
841-
assertCsvRecord(csvList, ResourceType.Work, workPid, "TestWork",
864+
var workId = workPid.getId();
865+
String pathToWork = FOLDER_PATH + "/" + workId;
866+
assertCsvRecord(csvList, ResourceType.Work, workPid, workId,
842867
pathToWork, 4, false, null, null, null, null,
843-
1, false, "Authenticated", "", "");
868+
1, false, "Authenticated", "", "", "", "");
844869

845-
String pathToFile = pathToWork + "/file.txt";
846-
assertCsvRecord(csvList, ResourceType.File, filePid, "TestWork",
870+
var fileTitle = "file.txt";
871+
String pathToFile = pathToWork + "/" + fileTitle;
872+
assertCsvRecord(csvList, ResourceType.File, filePid, fileTitle,
847873
pathToFile, 5, false, "text/plain", null, (long) 7, null,
848-
null, false, "Authenticated", "", "");
874+
null, false, "Authenticated", "", "", getUrl(workId), workId);
849875
}
850876

851877
private List<CSVRecord> parseCsvResponse(MockHttpServletResponse response) throws Exception {
@@ -857,4 +883,8 @@ private List<CSVRecord> parseCsvResponse(MockHttpServletResponse response) throw
857883
.forEach(csvList::add);
858884
return csvList;
859885
}
886+
887+
private String getUrl(String id) {
888+
return "http://example.com/record/" + id;
889+
}
860890
}

web-services-app/src/test/resources/export-csv-it-servlet.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<property name="aclService" ref="aclService"/>
3636
<property name="queryLayer" ref="queryLayer"/>
3737
<property name="repositoryObjectLoader" ref="repositoryObjectLoader"/>
38+
<property name="baseUrl" value="http://example.com/record/"/>
3839
</bean>
3940

4041
<util:set id="accessGroups" set-class="edu.unc.lib.boxc.auth.fcrepo.models.AccessGroupSetImpl">

0 commit comments

Comments
 (0)