From 2b1ce80b084a9b890e4cdb84ff92c04212ef84cf Mon Sep 17 00:00:00 2001 From: Julia Damerow Date: Mon, 20 Jun 2022 16:55:29 -0400 Subject: [PATCH 01/17] [CITE-177] setup and created service to query crossref --- citesphere/pom.xml | 8 +++- .../service/crossref/CrossrefService.java | 13 +++++++ .../crossref/impl/CrossrefServiceImpl.java | 38 +++++++++++++++++++ .../web/user/MoveItemsController.java | 6 ++- .../src/main/resources/config.properties | 2 + 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java diff --git a/citesphere/pom.xml b/citesphere/pom.xml index e5d4af906..6477fc1c9 100644 --- a/citesphere/pom.xml +++ b/citesphere/pom.xml @@ -26,6 +26,7 @@ 0.12 0.4 1.17 + 0.1 $2a$04$oQo44vqcDIFRoYKiAXoNheurzkwX9dcNmowvTX/hsWuBMwijqn44i @@ -156,6 +157,11 @@ citesphere-model ${citesphere.model.version} + + edu.asu.diging + crossref-connect + ${crossref-connect-version} + @@ -626,7 +632,7 @@ org.apache.maven.plugins maven-war-plugin - 2.6 + 3.3.2 diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java new file mode 100644 index 000000000..f331608fa --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java @@ -0,0 +1,13 @@ +package edu.asu.diging.citesphere.core.service.crossref; + +import java.io.IOException; +import java.util.List; + +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; + +public interface CrossrefService { + + List search(String query, int page) throws RequestFailedException, IOException; + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java new file mode 100644 index 000000000..143aef735 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java @@ -0,0 +1,38 @@ +package edu.asu.diging.citesphere.core.service.crossref.impl; + +import java.io.IOException; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Service; + +import edu.asu.diging.citesphere.core.service.crossref.CrossrefService; +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; +import edu.asu.diging.crossref.service.CrossrefConfiguration; +import edu.asu.diging.crossref.service.CrossrefWorksService; +import edu.asu.diging.crossref.service.impl.CrossrefWorksServiceImpl; + +@Service +@PropertySource("config.properties") +public class CrossrefServiceImpl implements CrossrefService { + + @Value("${_crossref_default_pagesize}") + private int defaultPageSize; + + private CrossrefWorksService service; + + @PostConstruct + public void init() { + service = new CrossrefWorksServiceImpl(CrossrefConfiguration.getDefaultConfig()); + } + + + @Override + public List search(String query, int page) throws RequestFailedException, IOException { + return service.search(query, defaultPageSize, (page-1)*defaultPageSize); + } +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/MoveItemsController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/MoveItemsController.java index 0858c497f..9e703e567 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/MoveItemsController.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/MoveItemsController.java @@ -97,7 +97,8 @@ public class MoveItemsController { "/auth/group/{zoteroGroupId}/collection/{parentcollectionId}/items/move/{targetCollectionId}/sync/start" }) public @ResponseBody Sync startSync(Authentication authentication, - @PathVariable("zoteroGroupId") String zoteroGroupId, @PathVariable("targetCollectionId") String collectionId, + @PathVariable("zoteroGroupId") String zoteroGroupId, + @PathVariable("targetCollectionId") String collectionId, @RequestParam(defaultValue = "1", required = false, value = "page") String page) { try { citationManager.getGroupItems((IUser) authentication.getPrincipal(), zoteroGroupId, collectionId, @@ -119,7 +120,8 @@ public class MoveItemsController { "/auth/group/{zoteroGroupId}/collection/{parentcollectionId}/items/move/{targetCollectionId}/totalItems" }) public @ResponseBody Long getTotalCitationsCollection(Authentication authentication, - @PathVariable("zoteroGroupId") String zoteroGroupId, @PathVariable("targetCollectionId") String collectionId) { + @PathVariable("zoteroGroupId") String zoteroGroupId, + @PathVariable("targetCollectionId") String collectionId) { ICitationCollection collection = collectionManager.getCollection((IUser) authentication.getPrincipal(), zoteroGroupId, collectionId); return collection.getNumberOfItems(); diff --git a/citesphere/src/main/resources/config.properties b/citesphere/src/main/resources/config.properties index 07328b26f..d547d1dbd 100644 --- a/citesphere/src/main/resources/config.properties +++ b/citesphere/src/main/resources/config.properties @@ -89,3 +89,5 @@ giles_check_endpoint=/api/v2/files/upload/check/ giles_file_endpoint=/api/v2/resources/files/{0}/content javers_default_author=${javers.default.author} + +_crossref_default_pagesize=50 From 557f26f0525c7d5b561e9b20f712396c1af2a632 Mon Sep 17 00:00:00 2001 From: Julia Damerow Date: Wed, 6 Jul 2022 15:38:17 -0400 Subject: [PATCH 02/17] [CITE-177] front end and backend changes to start crossref import --- citesphere/pom.xml | 2 +- .../api/v1/user/JobInfoController.java | 10 +- .../core/model/jobs/IImportCrossrefJob.java | 15 +++ .../model/jobs/impl/ImportCrossrefJob.java | 31 ++++++ .../jobs/ImportCrossrefJobRepository.java | 9 ++ .../crossref/impl/CrossrefServiceImpl.java | 2 +- .../jobs/IImportCrossrefJobManager.java | 13 +++ .../jobs/impl/ImportCrossrefJobManager.java | 79 +++++++++++++ .../user/jobs/ImportCrossrefController.java | 83 ++++++++++++++ .../src/main/resources/config.properties | 2 +- .../WEB-INF/views/auth/import/crossref.html | 105 ++++++++++++++++++ .../webapp/WEB-INF/views/layouts/main.html | 2 + 12 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java create mode 100644 citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html diff --git a/citesphere/pom.xml b/citesphere/pom.xml index 6477fc1c9..188201dff 100644 --- a/citesphere/pom.xml +++ b/citesphere/pom.xml @@ -24,7 +24,7 @@ 6.2.3 2.2.6.RELEASE 0.12 - 0.4 + 0.5 1.17 0.1 diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java index cab48f00c..89b95ece0 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java @@ -9,12 +9,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import edu.asu.diging.citesphere.core.export.IExportTaskManager; import edu.asu.diging.citesphere.core.model.IZoteroToken; import edu.asu.diging.citesphere.core.model.export.IExportTask; import edu.asu.diging.citesphere.core.model.jobs.IExportJob; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; import edu.asu.diging.citesphere.core.model.jobs.IJob; import edu.asu.diging.citesphere.core.model.jobs.IUploadJob; import edu.asu.diging.citesphere.core.service.jobs.IUploadJobManager; @@ -62,16 +64,18 @@ public ResponseEntity getProfile(@RequestHeader HttpHeaders headers) { // FIXME: ugly, needs better solution if (job instanceof IUploadJob) { node.put("groupId", ((IUploadJob)job).getCitationGroup()); - } - if (job instanceof IExportJob) { + } else if (job instanceof IExportJob) { IExportTask exportTask = exportTaskManager.get(((IExportJob)job).getTaskId()); node.put("groupId", exportTask.getGroupId()); node.put("collectionId", exportTask.getCollectionId()); node.put("exportType", exportTask.getExportType().name()); node.put("taskId", exportTask.getId()); + } else if (job instanceof IImportCrossrefJob) { + ArrayNode doisNode = mapper.createArrayNode(); + ((IImportCrossrefJob)job).getDois().forEach(d -> doisNode.add(d)); + node.set("dois", doisNode); } - return new ResponseEntity<>(node.toString(), HttpStatus.OK); } } diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java new file mode 100644 index 000000000..e1b37724d --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java @@ -0,0 +1,15 @@ +package edu.asu.diging.citesphere.core.model.jobs; + +import java.util.List; + +public interface IImportCrossrefJob extends IJob { + + /** + * Get the DOIs of the resources to be imported from Crossref. + * @return list of resources to be imported + */ + List getDois(); + + void setDois(List dois); + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java new file mode 100644 index 000000000..dff853fe2 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java @@ -0,0 +1,31 @@ +package edu.asu.diging.citesphere.core.model.jobs.impl; + +import java.util.List; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; + +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; + +@Entity +public class ImportCrossrefJob extends Job implements IImportCrossrefJob { + + @ElementCollection + private List dois; + + /** + * Get the DOIs of the resources to be imported from Crossref. + * @return list of resources to be imported + */ + @Override + public List getDois() { + return dois; + } + + @Override + public void setDois(List dois) { + this.dois = dois; + } + + +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java new file mode 100644 index 000000000..d61f20a27 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java @@ -0,0 +1,9 @@ +package edu.asu.diging.citesphere.core.repository.jobs; + +import org.springframework.data.repository.CrudRepository; + +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; + +public interface ImportCrossrefJobRepository extends CrudRepository { + +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java index 143aef735..fbcb80de3 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java @@ -17,7 +17,7 @@ import edu.asu.diging.crossref.service.impl.CrossrefWorksServiceImpl; @Service -@PropertySource("config.properties") +@PropertySource("classpath:config.properties") public class CrossrefServiceImpl implements CrossrefService { @Value("${_crossref_default_pagesize}") diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java new file mode 100644 index 000000000..4623d3821 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java @@ -0,0 +1,13 @@ +package edu.asu.diging.citesphere.core.service.jobs; + +import java.util.List; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.user.IUser; + +public interface IImportCrossrefJobManager { + + IImportCrossrefJob createJob(IUser user, String groupId, List dois) throws GroupDoesNotExistException; + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java new file mode 100644 index 000000000..1935ddb3d --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java @@ -0,0 +1,79 @@ +package edu.asu.diging.citesphere.core.service.jobs.impl; + +import java.time.OffsetDateTime; +import java.util.List; + +import javax.transaction.Transactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Service; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.exceptions.MessageCreationException; +import edu.asu.diging.citesphere.core.kafka.IKafkaRequestProducer; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.JobStatus; +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.impl.JobPhase; +import edu.asu.diging.citesphere.core.repository.jobs.ImportCrossrefJobRepository; +import edu.asu.diging.citesphere.core.service.IGroupManager; +import edu.asu.diging.citesphere.core.service.jobs.IImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.service.jwt.IJwtTokenService; +import edu.asu.diging.citesphere.messages.KafkaTopics; +import edu.asu.diging.citesphere.messages.model.KafkaJobMessage; +import edu.asu.diging.citesphere.model.bib.ICitationGroup; +import edu.asu.diging.citesphere.user.IUser; + +@Service +@Transactional +@PropertySource("classpath:/config.properties") +public class ImportCrossrefJobManager implements IImportCrossrefJobManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Value("${_job_page_size}") + private int jobPageSize; + + @Autowired + private IGroupManager groupManager; + + @Autowired + private ImportCrossrefJobRepository jobRepo; + + @Autowired + private IKafkaRequestProducer kafkaProducer; + + @Autowired + private IJwtTokenService tokenService; + + @Override + public IImportCrossrefJob createJob(IUser user, String groupId, List dois) throws GroupDoesNotExistException { + ICitationGroup group = groupManager.getGroup(user, groupId); + if (group == null) { + throw new GroupDoesNotExistException(); + } + + IImportCrossrefJob job = new ImportCrossrefJob(); + job.setCreatedOn(OffsetDateTime.now()); + job.setUsername(user.getUsername()); + job.setDois(dois); + job.setStatus(JobStatus.PREPARED); + jobRepo.save((ImportCrossrefJob)job); + + String token = tokenService.generateJobApiToken(job); + try { + kafkaProducer.sendRequest(new KafkaJobMessage(token), KafkaTopics.REFERENCES_IMPORT_CROSSREF_TOPIC); + } catch (MessageCreationException e) { + logger.error("Could not send Kafka message.", e); + job.setStatus(JobStatus.FAILURE); + job.getPhases().add(new JobPhase(JobStatus.FAILURE, e.getMessage())); + jobRepo.save((ImportCrossrefJob)job); + } + + return job; + } +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java new file mode 100644 index 000000000..655ae5919 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java @@ -0,0 +1,83 @@ +package edu.asu.diging.citesphere.web.user.jobs; + +import java.io.IOException; +import java.security.Principal; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.service.ICitationManager; +import edu.asu.diging.citesphere.core.service.IGroupManager; +import edu.asu.diging.citesphere.core.service.crossref.CrossrefService; +import edu.asu.diging.citesphere.core.service.jobs.IImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.user.IUserManager; +import edu.asu.diging.citesphere.user.IUser; +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; + +@Controller +public class ImportCrossrefController { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private CrossrefService crossrefService; + + @Autowired + private ICitationManager citationManager; + + @Autowired + private IUserManager userManager; + + @Autowired + private IImportCrossrefJobManager jobManager; + + @RequestMapping("/auth/import/crossref") + public String get(Model model, Principal principal) { + model.addAttribute("groups", citationManager.getGroups(userManager.findByUsername(principal.getName()))); + return "auth/import/crossref"; + } + + @RequestMapping("/auth/import/crossref/search") + public ResponseEntity> search(@RequestParam("query") String query, @RequestParam("page") int page) { + List results = null; + try { + results = crossrefService.search(query, page); + } catch (RequestFailedException | IOException e) { + logger.error("Could not get Crossref results.", e); + return new ResponseEntity>(HttpStatus.INTERNAL_SERVER_ERROR); + } + + return new ResponseEntity>(results, HttpStatus.OK); + } + + @RequestMapping(value="/auth/import/crossref", method=RequestMethod.POST) + public String post(Authentication authentication, @RequestParam("groupId") String groupId, + @RequestParam("dois[]") List dois, RedirectAttributes redirectAttrs) { + try { + jobManager.createJob((IUser) authentication.getPrincipal(), groupId, dois); + redirectAttrs.addFlashAttribute("show_alert", true); + redirectAttrs.addFlashAttribute("alert_type", "success"); + redirectAttrs.addFlashAttribute("alert_msg", "Import in progress."); + } catch (GroupDoesNotExistException e) { + logger.error("Group not found.", e); + redirectAttrs.addFlashAttribute("show_alert", true); + redirectAttrs.addFlashAttribute("alert_type", "danger"); + redirectAttrs.addFlashAttribute("alert_msg", e.getMessage()); + } + + return "redirect:/auth/import/crossref"; + } +} diff --git a/citesphere/src/main/resources/config.properties b/citesphere/src/main/resources/config.properties index d547d1dbd..a89c162d5 100644 --- a/citesphere/src/main/resources/config.properties +++ b/citesphere/src/main/resources/config.properties @@ -90,4 +90,4 @@ giles_file_endpoint=/api/v2/resources/files/{0}/content javers_default_author=${javers.default.author} -_crossref_default_pagesize=50 +_crossref_default_pagesize=20 diff --git a/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html b/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html new file mode 100644 index 000000000..c9847acee --- /dev/null +++ b/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html @@ -0,0 +1,105 @@ + + + + + + +
+
+
+
+

Import from Crossref

+
+
+
+ +
Search
+
+
+
+ +

Results

+
+ +
+

+ Import into Group: + +

+

+ +

+
+
+ +
+
+
+ + diff --git a/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html b/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html index db0d1104e..7c5ff4c86 100644 --- a/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html +++ b/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html @@ -90,6 +90,8 @@