Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c3c5bb7
[CITE-173] Frontend for reprocessing
diya17 Aug 8, 2023
c4344a2
[CITE-173] Implementing reprocessing in citation manager and giles co…
diya17 Aug 9, 2023
6eba322
[CITE-173] Adding giles endpoint
diya17 Aug 29, 2023
2fc2246
[CITE-173] Changes to UI
diya17 Aug 30, 2023
7be90f9
[CITE-173] Adding changes in the controller and citation manager
diya17 Aug 31, 2023
ed05b54
[CITE-173] : Fixing UI Jquery
diya17 Sep 5, 2023
050995d
[CITE-173] Update CitationManager
diya17 Sep 6, 2023
97cb808
[CITE-173] Fixing duplication bug and css
diya17 Sep 7, 2023
3cee57b
[CITE-173] Making css changes and adding tests
diya17 Sep 13, 2023
0ece8b6
[CITE-173] Fixing testcase and config.properies
diya17 Sep 14, 2023
b662de3
[CITE-173] Fixing code factor issues
diya17 Sep 21, 2023
ec2db03
[CITE-173] Fixing intendation
diya17 Sep 21, 2023
459db44
[CITE-173] Addressing review comments in citation manager
diya17 Oct 20, 2023
8520d9f
[CITE-173] Changing tests
diya17 Oct 24, 2023
eb8a85f
Update Citation Manager to use collector
diya17 Oct 25, 2023
8286dfd
Update pom with latest java version used for build
diya17 Oct 25, 2023
f7a8991
[CITE-173] Addressing review comments
diya17 Apr 26, 2024
c8bfddc
Merge branch 'develop' into story/CITE-173
rajvi-patel-22 Aug 7, 2025
85de85a
[CITE-173] Address review comments
rajvi-patel-22 Aug 20, 2025
b0922ad
[CITE-173] Code cleanup
rajvi-patel-22 Sep 12, 2025
cfb6579
[CITE-173] Fixed invalid char in URL error
rajvi-patel-22 Sep 17, 2025
d7e2c63
[CITE-173] Add method to determine if upload can be reprocessed
rajvi-patel-22 Sep 24, 2025
8e5b213
CITE-173] Fix failing test cases
rajvi-patel-22 Sep 24, 2025
b660fb4
[CITE-173] Address codefactor comments
rajvi-patel-22 Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ CloseableIterator<ICitation> getAllGroupItems(IUser user, String groupId, String

void deleteLocalGroupCitations(String groupId);

}
void reprocessFile(IUser user, String zoteroGroupId, String itemId, String documentId)
throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException,
ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException;

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package edu.asu.diging.citesphere.core.service.giles;

import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;

import edu.asu.diging.citesphere.user.IUser;

public interface IGilesConnector {

<T> ResponseEntity<T> sendRequest(IUser user, String endpoint, Class<T> returnType)
<T> ResponseEntity<T> sendRequest(IUser user, String endpoint, Class<T> returnType, HttpMethod httpMethod)
throws HttpClientErrorException;

byte[] getFile(IUser user, String fileId);

}

ResponseEntity<String> reprocessDocument(IUser user, String documentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
Expand All @@ -29,6 +30,9 @@ public class GilesConnector implements IGilesConnector {
@Value("${giles_file_endpoint}")
private String fileEndpoint;

@Value("${giles_file_reprocessing_endpoint}")
private String reprocessingEndpoint;

@Autowired
private InternalTokenManager internalTokenManager;

Expand All @@ -44,7 +48,7 @@ public void init() {
* @see edu.asu.diging.citesphere.core.service.giles.impl.IGilesConnector#sendRequest(edu.asu.diging.citesphere.user.IUser, java.lang.String, java.lang.Class)
*/
@Override
public <T> ResponseEntity<T> sendRequest(IUser user, String endpoint,Class<T> returnType) throws HttpClientErrorException {
public <T> ResponseEntity<T> sendRequest(IUser user, String endpoint,Class<T> returnType, HttpMethod httpMethod) throws HttpClientErrorException {
String token = internalTokenManager.getAccessToken(user).getValue();

HttpHeaders headers = new HttpHeaders();
Expand All @@ -54,15 +58,21 @@ public <T> ResponseEntity<T> sendRequest(IUser user, String endpoint,Class<T> re

return restTemplate.exchange(
gilesBaseurl + endpoint,
HttpMethod.GET, requestEntity, returnType);
httpMethod, requestEntity, returnType);
}

/* (non-Javadoc)
* @see edu.asu.diging.citesphere.core.service.giles.impl.IGilesConnector#getFile(edu.asu.diging.citesphere.user.IUser, java.lang.String)
*/
@Override
public byte[] getFile(IUser user, String fileId) {
ResponseEntity<byte[]> content = sendRequest(user, fileEndpoint.replace("{0}", fileId), byte[].class);
ResponseEntity<byte[]> content = sendRequest(user, fileEndpoint.replace("{0}", fileId), byte[].class, HttpMethod.GET);
return content.getBody();
}

@Override
public ResponseEntity<String> reprocessDocument(IUser user, String documentId) {
ResponseEntity<String> response = sendRequest(user, reprocessingEndpoint.replace("{0}", documentId), String.class, HttpMethod.POST);
return response;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge with line 75

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package edu.asu.diging.citesphere.core.service.impl;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;

Expand All @@ -18,12 +22,16 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.util.CloseableIterator;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.social.zotero.api.ZoteroUpdateItemsStatuses;
import org.springframework.social.zotero.exception.ZoteroConnectionException;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import edu.asu.diging.citesphere.core.exceptions.AccessForbiddenException;
import edu.asu.diging.citesphere.core.exceptions.CannotFindCitationException;
Expand All @@ -37,16 +45,21 @@
import edu.asu.diging.citesphere.core.service.ICitationManager;
import edu.asu.diging.citesphere.core.service.ICitationStore;
import edu.asu.diging.citesphere.core.service.IGroupManager;
import edu.asu.diging.citesphere.core.service.giles.GilesUploadChecker;
import edu.asu.diging.citesphere.core.service.giles.IGilesConnector;
import edu.asu.diging.citesphere.core.zotero.IZoteroManager;
import edu.asu.diging.citesphere.data.bib.CitationGroupRepository;
import edu.asu.diging.citesphere.data.bib.ICitationDao;
import edu.asu.diging.citesphere.model.bib.GilesStatus;
import edu.asu.diging.citesphere.model.bib.ICitation;
import edu.asu.diging.citesphere.model.bib.ICitationCollection;
import edu.asu.diging.citesphere.model.bib.ICitationGroup;
import edu.asu.diging.citesphere.model.bib.IGilesUpload;
import edu.asu.diging.citesphere.model.bib.ItemType;
import edu.asu.diging.citesphere.model.bib.impl.BibField;
import edu.asu.diging.citesphere.model.bib.impl.CitationGroup;
import edu.asu.diging.citesphere.model.bib.impl.CitationResults;
import edu.asu.diging.citesphere.model.bib.impl.GilesUpload;
import edu.asu.diging.citesphere.user.IUser;

@Service
Expand Down Expand Up @@ -83,6 +96,12 @@ public class CitationManager implements ICitationManager {

@Autowired
private IAsyncCitationProcessor asyncCitationProcessor;

@Autowired
private IGilesConnector gilesConnector;

@Autowired
private GilesUploadChecker gilesUploadChecker;

private Map<String, BiFunction<ICitation, ICitation, Integer>> sortFunctions;

Expand Down Expand Up @@ -494,4 +513,77 @@ public CitationPage getPrevAndNextCitation(IUser user, String groupId, String co
public void deleteLocalGroupCitations(String groupId) {
citationStore.deleteCitationByGroupId(groupId);
}

@Override
public void reprocessFile(IUser user, String zoteroGroupId, String itemId, String documentId)
throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException,
ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException {
ICitation citation = getCitation(user, zoteroGroupId, itemId);
for (Iterator<IGilesUpload> gilesUpload = citation.getGilesUploads().iterator(); gilesUpload.hasNext();) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a while loop

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should actually be a stream that filters for the upload with the document id

IGilesUpload upload = gilesUpload.next();
if (upload.getDocumentId() != null && upload.getDocumentId().equals(documentId)) {
ResponseEntity<String> reprocessingResponse = gilesConnector.reprocessDocument(user, documentId);
if (reprocessingResponse.getStatusCode().equals(HttpStatus.OK)) {
IGilesUpload reprocessedUpload = new GilesUpload();
String responseBody = reprocessingResponse.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode;
try {
jsonNode = objectMapper.readTree(responseBody);
String checkUrl = jsonNode.get("checkUrl").asText();
String[] urlSegments = checkUrl.split("/");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be taken directly from id property of response

String progressId = urlSegments[urlSegments.length - 1];
reprocessedUpload.setUploadingUser(user.getUsername());
reprocessedUpload.setProgressId(progressId);
Set<IGilesUpload> checkedUploads = new HashSet<>();
checkedUploads.add(reprocessedUpload);
updateCitationWithUpdatedGilesUpload(checkedUploads, user, citation, documentId);
gilesUploadChecker.add(citation);
} catch (IOException e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move catch block up to where it is thrown.

logger.error("Could not deserialize response.", e);
}
}
}

}
}

private void updateCitationWithUpdatedGilesUpload(Set<IGilesUpload> checkedUploads, IUser user, ICitation citation, String documentId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shorten method name

ICitation currentCitation = getCurrentCitation(citation, user);
if (currentCitation != null) {
for (IGilesUpload upload : checkedUploads) {
Optional<IGilesUpload> oldUpload = currentCitation
.getGilesUploads().stream()
.filter(u -> u.getDocumentId() != null && u
.getDocumentId().equals(documentId))
.findFirst();
if (oldUpload.isPresent()) {
currentCitation.getGilesUploads().remove(oldUpload.get());
}
currentCitation.getGilesUploads().add(upload);
}

try {
updateCitation(user, citation.getGroup(),
currentCitation);
} catch (ZoteroConnectionException | CitationIsOutdatedException
| ZoteroHttpStatusException | ZoteroItemCreationFailedException e) {
logger.error("Could not update citation.", e);
}
}
}

private ICitation getCurrentCitation(ICitation citation, IUser user) {
try {
return getCitation(user,
citation.getGroup(), citation.getKey());
} catch (GroupDoesNotExistException e) {
logger.error("Could not get citation.", e);
} catch (CannotFindCitationException e) {
logger.error("Could not get citation.", e);
} catch (ZoteroHttpStatusException e) {
logger.error("Could not get citation.", e);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, it seems like a bad idea to just ignore this

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.social.zotero.exception.ZoteroConnectionException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import edu.asu.diging.citesphere.core.exceptions.CannotFindCitationException;
import edu.asu.diging.citesphere.core.exceptions.CitationIsOutdatedException;
import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException;
import edu.asu.diging.citesphere.core.exceptions.ZoteroHttpStatusException;
import edu.asu.diging.citesphere.core.exceptions.ZoteroItemCreationFailedException;
import edu.asu.diging.citesphere.core.search.service.SearchEngine;
import edu.asu.diging.citesphere.core.service.ICitationManager;
import edu.asu.diging.citesphere.core.service.impl.CitationPage;
Expand Down Expand Up @@ -66,4 +72,15 @@ public String getItem(Authentication authentication, Model model, @PathVariable(
}
return "auth/group/item";
}

@RequestMapping(value = "/auth/group/{zoteroGroupId}/file/reprocess", method = RequestMethod.POST)
public ResponseEntity<String> deleteFile(Authentication authentication,
@PathVariable("zoteroGroupId") String zoteroGroupId,
@RequestParam(value = "documentId", required = false) String documentId,
@RequestParam(value = "itemId", required = false) String itemId)
throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException,
ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException {
citationManager.reprocessFile((IUser) authentication.getPrincipal(), zoteroGroupId, itemId, documentId);
return new ResponseEntity<>(HttpStatus.OK);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the reprocessing throws an error, the return should not be ok.

}
}
1 change: 1 addition & 0 deletions citesphere/src/main/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,6 @@ elasticsearch.connect.timeout=${elasticsearch.connect.timeout}
giles_upload_endpoint=/api/v2/files/upload
giles_check_endpoint=/api/v2/files/upload/check/
giles_file_endpoint=/api/v2/resources/files/{0}/content
giles_file_reprocessing_endpoint=/api/v2/resources/documents/{0}/reprocess

javers_default_author=${javers.default.author}
78 changes: 75 additions & 3 deletions citesphere/src/main/webapp/WEB-INF/views/auth/group/item.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<html layout:decorate="~{layouts/main}">
<head>
<script th:src="@{/resources/notify/bootstrap-notify.min.js}"></script>
<link th:href="@{/resources/notify/animate.css}" rel="stylesheet"/>
<link th:href="@{/resources/bootstrap/css/word-wrap.css}" type="text/css"
rel="stylesheet">
<script th:inline="javascript">
//# sourceURL=modal.js
$(function() {
Expand Down Expand Up @@ -86,6 +90,40 @@

});

$("#reprocess-file-button").click(function(e) {
e.preventDefault();
var fileId = $(this).data("upload-file");
$('body').append('<div id="reprocessFileDiv" class="modal-backdrop" style="opacity:0.5"><i class="icon-spinner spinner" style="position:fixed; top:50%; left:50%"></i></div>');
$.ajax({
'type': "POST",
'url': "[(@{|/auth/group/${zoteroGroupId}/file/reprocess|})]",
'data': {
"documentId": fileId,
[[${_csrf.parameterName}]]:[[${_csrf.token}]],
"itemId": [[${itemId}]]

},
'success': function(data) {
$("#reprocessFileDiv").remove();
showNotification("File submitted successfully for reprocessing! ", "success", 10000);
location.reload();
},
'error': function(data) {
$("#reprocessFileDiv").remove();
showNotification("Error while reprocessing the file!", "danger", 10000)
}
});
$('#file-reprocess-confirmation').modal("hide");
event.preventDefault();
$(this).removeData();
});


$(".reprocess-file").click(function(e) {
$("#reprocess-file-button").attr("data-upload-file", $(this).data("upload-file"));
$('#file-reprocess-confirmation').modal("show");
});

var queryParams = window.location.search;
if(queryParams) {
var params = new URLSearchParams(queryParams);
Expand All @@ -104,6 +142,21 @@
$('.note-info').each(function() {
$(this).text(truncateNote($(this).attr('data-note-text')));
})

function showNotification(message, status, time){
$.notify('<i class="fas fa-exclamation-circle"></i>'+message, {
type: status,
offset: {
x: 50,
y: 90
},
timer: time,
animate: {
enter: 'animated fadeInRight',
exit: 'animated fadeOutRight'
},
});
}
});

</script>
Expand Down Expand Up @@ -360,17 +413,18 @@ <h2 style="margin-top: 0">
<h3 style="margin-top: 0">Files</h3>

<ul class="list-group" >
<li class="list-group-item" th:each="upload:${citation.gilesUploads}">
<li class="list-group-item word-wrap" th:each="upload,index:${citation.gilesUploads}">
<i class="icon-giles"></i>
<span th:if="${upload.uploadedFile?.filename}">
<a th:href="@{|/auth/group/${zoteroGroupId}/items/${citation.key}/giles/${upload.uploadedFile?.id}|}">[[${upload.uploadedFile?.filename}]]</a>
<a th:href="@{|/auth/group/${zoteroGroupId}/items/${citation.key}/giles/${upload.uploadedFile?.id}|}">[[${upload.uploadedFile?.filename}]]</a>
</span>
<span th:unless="${upload.documentStatus != null && (upload.documentStatus != T(edu.asu.diging.citesphere.model.bib.GilesStatus).SUBMITTED || T(edu.asu.diging.citesphere.model.bib.GilesStatus).NEW)}">
File is being processed... ([[${upload.progressId}]])
</span>
<span th:if="${upload.documentStatus == T(edu.asu.diging.citesphere.model.bib.GilesStatus).FAILED}" class="label label-danger">Failed [[${upload.progressId}]]</span>
<span th:if="${upload.documentStatus == T(edu.asu.diging.citesphere.model.bib.GilesStatus).NEW}" class="label label-default">In Queue</span>
<span th:if="${upload.documentStatus == T(edu.asu.diging.citesphere.model.bib.GilesStatus).SUBMITTED}" class="label label-default">Processing</span>
<button th:id="|reprocess-file-btn-${index.index}|" type="button" th:data-upload-file="${upload.documentId}" class="fa fa-redo fa-xs reprocess-file"></button>
</li>
</ul>

Expand Down Expand Up @@ -489,6 +543,24 @@ <h4 class="modal-title">Upload File</h4>
</div>
</div>
</div>

<div class="modal fade" id="file-reprocess-confirmation" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Reprocess File</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to reprocess this file?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel!</button>
<button id="reprocess-file-button" data-upload-file="" type="button" class="btn btn-primary">Yes, reprocess!</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
</html>
Loading