Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8073 guestbook response api #8084

Merged
merged 11 commits into from
Sep 16, 2021
3 changes: 3 additions & 0 deletions doc/release-notes/8073-gb-response_api_update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Update Retrieve Guestbook Response API to remove default output file

With this release the Retrieve Guestbook Responses for a Dataverse Collection will no longer produce a file by default. You may specify an output file by adding a -o $YOURFILENAME to the curl command.
7 changes: 4 additions & 3 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ You should expect a 200 ("OK") response and JSON output.
Retrieve Guestbook Responses for a Dataverse Collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In order to retrieve a file containing a list of Guestbook Responses in csv format for Dataverse collection, you must know either its "alias" (which the GUI calls an "identifier") or its database ID. If the Dataverse collection has more than one guestbook you may provide the id of a single guestbook as an optional parameter. If no guestbook id is provided the results returned will be the same as pressing the "Download All Responses" button on the Manage Dataset Guestbook page. If the guestbook id is provided then only those responses from that guestbook will be included in the file.
In order to retrieve a file containing a list of Guestbook Responses in csv format for Dataverse collection, you must know either its "alias" (which the GUI calls an "identifier") or its database ID. If the Dataverse collection has more than one guestbook you may provide the id of a single guestbook as an optional parameter. If no guestbook id is provided the results returned will be the same as pressing the "Download All Responses" button on the Manage Dataset Guestbook page. If the guestbook id is provided then only those responses from that guestbook will be included. The FILENAME parameter is optional without it the responses will be displayed in the console.

.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of ``export`` below.

Expand All @@ -597,14 +597,15 @@ In order to retrieve a file containing a list of Guestbook Responses in csv form
export SERVER_URL=https://demo.dataverse.org
export ID=root
export GUESTBOOK_ID=1
export FILENAME=myResponses.csv

curl -O -J -f -H X-Dataverse-key:$API_TOKEN $SERVER_URL/api/dataverses/$ID/guestbookResponses?guestbookId=$GUESTBOOK_ID
curl -H X-Dataverse-key:$API_TOKEN $SERVER_URL/api/dataverses/$ID/guestbookResponses?guestbookId=$GUESTBOOK_ID -o $FILENAME

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl -O -J -f -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx https://demo.dataverse.org/api/dataverses/root/guestbookResponses?guestbookId=1
curl -H X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx https://demo.dataverse.org/api/dataverses/root/guestbookResponses?guestbookId=1 -o myResponses.csv

Datasets
--------
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/DataverseServiceBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public class DataverseServiceBean implements java.io.Serializable {
@PersistenceContext(unitName = "VDCNet-ejbPU")
private EntityManager em;

private static final String BASE_QUERY_DATASET_TITLES_WITHIN_DV = "select v.value, o.id\n"
+ "from datasetfieldvalue v, dvobject o "
+ "where "
+ "v.datasetfield_id = (select id from datasetfield f where datasetfieldtype_id = 1 "
+ "and datasetversion_id = (select max(id) from datasetversion where dataset_id = o.id))";

public Dataverse save(Dataverse dataverse) {

dataverse.setModificationTime(new Timestamp(new Date().getTime()));
Expand Down Expand Up @@ -913,4 +919,14 @@ public String addRoleAssignmentsToChildren(Dataverse owner, ArrayList<String> ro
return (result);
}

// A quick custom query that finds all the (direct children) dataset titles
// with a dataverse and returns a list of (dataset_id, title) pairs.
public List<Object[]> getDatasetTitlesWithinDataverse(Long dataverseId) {
String cqString = BASE_QUERY_DATASET_TITLES_WITHIN_DV
+ "and o.owner_id = " + dataverseId;

return em.createNativeQuery(cqString).getResultList();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
Expand All @@ -43,11 +44,14 @@
public class GuestbookResponseServiceBean {
private static final Logger logger = Logger.getLogger(GuestbookResponseServiceBean.class.getCanonicalName());

@EJB
DataverseServiceBean dataverseService;

// The query below is used for retrieving guestbook responses used to download
// the collected data, in CSV format, from the manage-guestbooks and
// guestbook-results pages. (for entire dataverses, and for the individual
// guestbooks within dataverses, respectively). -- L.A.
private static final String BASE_QUERY_STRING_FOR_DOWNLOAD_AS_CSV = "select r.id, g.name, v.value, r.responsetime, f.downloadtype,"
/*private static final String BASE_QUERY_STRING_FOR_DOWNLOAD_AS_CSV = "select r.id, g.name, v.value, r.responsetime, f.downloadtype,"
+ " m.label, r.dataFile_id, r.name, r.email, r.institution, r.position,"
+ " o.protocol, o.authority, o.identifier, d.protocol, d.authority, d.identifier "
+ "from guestbookresponse r, filedownload f, datasetfieldvalue v, filemetadata m, dvobject o, guestbook g, dvobject d "
Expand All @@ -59,6 +63,18 @@ public class GuestbookResponseServiceBean {
+ " and d.id = r.datafile_id "
+ " and r.id = f.guestbookresponse_id "
+ " and r.dataset_id = o.id "
+ " and r.guestbook_id = g.id ";*/

private static final String BASE_QUERY_STRING_FOR_DOWNLOAD_AS_CSV = "select r.id, g.name, o.id, r.responsetime, f.downloadtype,"
+ " m.label, r.dataFile_id, r.name, r.email, r.institution, r.position,"
+ " o.protocol, o.authority, o.identifier, d.protocol, d.authority, d.identifier "
+ "from guestbookresponse r, filedownload f, filemetadata m, dvobject o, guestbook g, dvobject d "
+ "where "
+ "m.datasetversion_id = (select max(datasetversion_id) from filemetadata where datafile_id =r.datafile_id ) "
+ " and m.datafile_id = r.datafile_id "
+ " and d.id = r.datafile_id "
+ " and r.id = f.guestbookresponse_id "
+ " and r.dataset_id = o.id "
+ " and r.guestbook_id = g.id ";

// And this query is used for retrieving guestbook responses for displaying
Expand All @@ -82,7 +98,7 @@ public class GuestbookResponseServiceBean {
+ "where q.id = r.customquestion_id "
+ "and r.guestbookResponse_id = g.id "
+ "and g.dataset_id = o.id ";


private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/d/yyyy");

Expand Down Expand Up @@ -128,12 +144,13 @@ public void streamResponsesByDataverseIdAndGuestbookId(OutputStream out, Long da
// of queries now) -- L.A.

Map<Integer, Object> customQandAs = mapCustomQuestionAnswersAsStrings(dataverseId, guestbookId);
Map<Integer, String> datasetTitles = mapDatasetTitles(dataverseId);

List<Object[]> guestbookResults = getGuestbookResults( dataverseId, guestbookId );
// the CSV header:
out.write("Guestbook, Dataset, Dataset PID, Date, Type, File Name, File Id, File PID, User Name, Email, Institution, Position, Custom Questions\n".getBytes());
for (Object[] result : guestbookResults) {
StringBuilder sb = convertGuestbookResponsesToCSV(customQandAs, result);
StringBuilder sb = convertGuestbookResponsesToCSV(customQandAs, datasetTitles, result);
out.write(sb.toString().getBytes());
out.flush();
}
Expand All @@ -156,7 +173,7 @@ public List<Object[]> getGuestbookResults(Long dataverseId, Long guestbookId ){

}

public StringBuilder convertGuestbookResponsesToCSV ( Map<Integer, Object> customQandAs, Object[] result) throws IOException {
public StringBuilder convertGuestbookResponsesToCSV ( Map<Integer, Object> customQandAs, Map<Integer, String> datasetTitles, Object[] result) throws IOException {

Integer guestbookResponseId = (Integer)result[0];

Expand All @@ -172,7 +189,9 @@ public StringBuilder convertGuestbookResponsesToCSV ( Map<Integer, Object> custo


// Dataset name:
sb.append(((String)result[2]).replace(',', ' '));
Integer datasetId = (Integer) result[2];
String datasetTitle = datasetTitles.get(datasetId);
sb.append(datasetTitle == null ? "" : datasetTitle.replace(',', ' '));
sb.append(SEPARATOR);

// Dataset persistent identifier:
Expand Down Expand Up @@ -908,5 +927,24 @@ public List<GuestbookResponse> findByAuthenticatedUserId(AuthenticatedUser user)
query.setParameter("authenticatedUserId", user.getId());
return query.getResultList();
}

public Map<Integer, String> mapDatasetTitles(Long dataverseId) {
Map<Integer, String> ret = new HashMap<>();

List<Object[]> titleResults = dataverseService.getDatasetTitlesWithinDataverse(dataverseId);

if (titleResults != null) {
for (Object[] titleObj : titleResults) {
Integer datasetId = (Integer) titleObj[1];
String datasetTitle = (String) titleObj[0];

ret.put(datasetId, datasetTitle);

}
}

return ret;

}

}
51 changes: 28 additions & 23 deletions src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,17 @@
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.toJsonArray;
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.json;
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.stream.XMLStreamException;

/**
Expand Down Expand Up @@ -141,6 +143,9 @@ public class Dataverses extends AbstractApiBean {

@EJB
GuestbookServiceBean guestbookService;

@EJB
DataverseServiceBean dataverseService;

@POST
public Response addRoot(String body) {
Expand Down Expand Up @@ -946,10 +951,9 @@ public Response getGroupByOwnerAndAliasInOwner(@PathParam("identifier") String d

@GET
@Path("{identifier}/guestbookResponses/")
@Produces({"application/download"})
public Response getGuestbookResponsesByDataverse(@PathParam("identifier") String dvIdtf,
@QueryParam("guestbookId") Long gbId, @Context HttpServletResponse response) {

try {
Dataverse dv = findDataverseOrDie(dvIdtf);
User u = findUserOrDie();
Expand All @@ -960,30 +964,31 @@ public Response getGuestbookResponsesByDataverse(@PathParam("identifier") String
} else {
return error(Status.FORBIDDEN, "Not authorized");
}

String fileTimestamp = dateFormatter.format(new Date());
String filename = dv.getAlias() + "_GBResponses_" + fileTimestamp + ".csv";

response.setHeader("Content-Disposition", "attachment; filename="
+ filename);
ServletOutputStream outputStream = response.getOutputStream();

Map<Integer, Object> customQandAs = guestbookResponseService.mapCustomQuestionAnswersAsStrings(dv.getId(), gbId);

List<Object[]> guestbookResults = guestbookResponseService.getGuestbookResults(dv.getId(), gbId);
outputStream.write("Guestbook, Dataset, Dataset PID, Date, Type, File Name, File Id, File PID, User Name, Email, Institution, Position, Custom Questions\n".getBytes());
for (Object[] result : guestbookResults) {
StringBuilder sb = guestbookResponseService.convertGuestbookResponsesToCSV(customQandAs, result);
outputStream.write(sb.toString().getBytes());
outputStream.flush();
}
return Response.ok().build();
} catch (IOException io) {
return error(Status.BAD_REQUEST, "Failed to produce response file. Exception: " + io.getMessage());

} catch (WrappedResponse wr) {
return wr.getResponse();
}

StreamingOutput stream = new StreamingOutput() {

@Override
public void write(OutputStream os) throws IOException,
WebApplicationException {

Dataverse dv = dataverseService.findByAlias(dvIdtf);
Map<Integer, Object> customQandAs = guestbookResponseService.mapCustomQuestionAnswersAsStrings(dv.getId(), gbId);
Map<Integer, String> datasetTitles = guestbookResponseService.mapDatasetTitles(dv.getId());

List<Object[]> guestbookResults = guestbookResponseService.getGuestbookResults(dv.getId(), gbId);
os.write("Guestbook, Dataset, Dataset PID, Date, Type, File Name, File Id, File PID, User Name, Email, Institution, Position, Custom Questions\n".getBytes());
for (Object[] result : guestbookResults) {
StringBuilder sb = guestbookResponseService.convertGuestbookResponsesToCSV(customQandAs, datasetTitles, result);
os.write(sb.toString().getBytes());

}
}
};
return Response.ok(stream).build();
}

@PUT
Expand Down