Commit e01c1fd590effcd78598eb1723cfb9837f88bae3

Authored by Paulo Graça
1 parent 19b59a898f
Exists in tests

PG: maven java src dir

ptcrisync/src/main/java/pt/ptcris/ORCIDClient.java
... ... @@ -0,0 +1,67 @@
  1 +package pt.ptcris;
  2 +
  3 +import java.math.BigInteger;
  4 +
  5 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  6 +import org.um.dsi.gavea.orcid.model.work.Work;
  7 +import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
  8 +
  9 +public interface ORCIDClient {
  10 +
  11 +
  12 + /**
  13 + * Retrieves a full work from the ORCID profile.
  14 + *
  15 + * @param putCode The put-code of the work.
  16 + * @return The full work.
  17 + * @throws ORCIDException
  18 + */
  19 + public Work getWork(BigInteger putCode) throws OrcidClientException;
  20 +
  21 + /**
  22 + * Add a work to the ORCID profile.
  23 + *
  24 + * @param work
  25 + * The work to be added to the ORCID profile
  26 + * @return the put-code in the ORCID profile of the newly created work.
  27 + * @throws ORCIDException
  28 + */
  29 + public String addWork(Work work) throws OrcidClientException;
  30 +
  31 + /**
  32 + * Delete a work from the ORCID profile.
  33 + *
  34 + * @param putCode
  35 + * The put-code of the work to be deleted.
  36 + * @throws ORCIDException
  37 + */
  38 + public void deleteWork(BigInteger putCode) throws OrcidClientException;
  39 +
  40 + /**
  41 + * Update a work in the ORCID profile.
  42 + *
  43 + * @param putCode
  44 + * The put-code of the work to be updated.
  45 + * @param work
  46 + * the new state of the work.
  47 + * @return the updated work as represented in the ORCID profile.
  48 + * @throws ORCIDException
  49 + */
  50 + public void updateWork(BigInteger putCode, Work work) throws OrcidClientException;
  51 +
  52 + /**
  53 + * Retrieves every activity summary of the ORCID profile.
  54 + *
  55 + * @return The activities summary of the ORCID profile.
  56 + * @throws ORCIDException
  57 + */
  58 + public ActivitiesSummary getActivitiesSummary() throws OrcidClientException;
  59 +
  60 + /**
  61 + * Returns the ORCID's client id
  62 + *
  63 + * @return String with cliend id.
  64 + */
  65 + public String getClientId();
  66 +
  67 +}
0 68 \ No newline at end of file
... ...
ptcrisync/src/main/java/pt/ptcris/ORCIDClientImpl.java
... ... @@ -0,0 +1,83 @@
  1 +package pt.ptcris;
  2 +
  3 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  4 +
  5 +import java.math.BigInteger;
  6 +
  7 +import org.um.dsi.gavea.orcid.client.OrcidAccessToken;
  8 +import org.um.dsi.gavea.orcid.client.OrcidOAuthClient;
  9 +import org.um.dsi.gavea.orcid.model.work.Work;
  10 +import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
  11 +
  12 +
  13 +public class ORCIDClientImpl implements ORCIDClient {
  14 +
  15 + private final OrcidAccessToken orcidToken;
  16 + private final OrcidOAuthClient orcidClient;
  17 + private final String clientId;
  18 +
  19 + public ORCIDClientImpl(String loginUri, String apiUri, String clientId, String clientSecret, String redirectUri, OrcidAccessToken orcidToken) {
  20 + this.orcidToken = orcidToken;
  21 + this.clientId = clientId;
  22 +
  23 + // Instantiate the Orcid Client
  24 + this.orcidClient = new OrcidOAuthClient(loginUri, apiUri, clientId, clientSecret, redirectUri);
  25 + }
  26 +
  27 + /**
  28 + * @see pt.ptcris.ORCIDClient#getFullWork(java.lang.Long)
  29 + */
  30 + public Work getWork(BigInteger putCode) throws OrcidClientException {
  31 + return this.orcidClient.readWork(this.orcidToken, putCode.toString());
  32 + }
  33 +
  34 + /**
  35 + * @see pt.ptcris.ORCIDClient#addWork(org.orcid.jaxb.model.record_rc2.Work)
  36 + */
  37 + public String addWork(Work work) throws OrcidClientException {
  38 + return this.orcidClient.addWork(this.orcidToken, work);
  39 + }
  40 +
  41 + /**
  42 + * @see pt.ptcris.ORCIDClient#deleteWork(java.lang.Long)
  43 + */
  44 + public void deleteWork(BigInteger putCode) throws OrcidClientException {
  45 + this.orcidClient.deleteWork(this.orcidToken, putCode.toString());
  46 +
  47 + // NOTE: according to the ORCID API, to delete a work, one must provide
  48 + // the entire list of works in the ORCID profile minus the work(s) that
  49 + // should be deleted. This means that this operation must be done in
  50 + // three steps: first, retrieve the entire set of works; second, remove
  51 + // the
  52 + // work to be deleted from the list of works; and three, send the
  53 + // updated list to the ORCID API.
  54 + }
  55 +
  56 + /**
  57 + * @see pt.ptcris.ORCIDClient#updateWork(java.lang.Long,
  58 + * org.orcid.jaxb.model.record_rc2.Work)
  59 + */
  60 + public void updateWork(BigInteger putCode, Work work) throws OrcidClientException {
  61 + this.orcidClient.updateWork(this.orcidToken, putCode.toString(), work);
  62 +
  63 + // NOTE: according to the ORCID API, to update a work, one must provide
  64 + // the entire list of works in the ORCID profile including the work(s)
  65 + // that should be updated. This means that this operation must be done
  66 + // in three steps: first, retrieve the entire set of works; second,
  67 + // replace the work to be updated with the new record in the list of
  68 + // works; and three, send the updated list to the ORCID API.
  69 + }
  70 +
  71 + /**
  72 + * @see pt.ptcris.ORCIDClient#getActivitiesSummary()
  73 + */
  74 + public ActivitiesSummary getActivitiesSummary() throws OrcidClientException {
  75 + return orcidClient.readActivitiesSummary(orcidToken);
  76 + }
  77 +
  78 + public String getClientId() {
  79 + return this.clientId;
  80 + }
  81 +
  82 +
  83 +}
... ...
ptcrisync/src/main/java/pt/ptcris/ORCIDHelper.java
... ... @@ -0,0 +1,264 @@
  1 +package pt.ptcris;
  2 +
  3 +import java.math.BigInteger;
  4 +import java.util.LinkedList;
  5 +import java.util.List;
  6 +import java.util.stream.Collectors;
  7 +import java.util.stream.Stream;
  8 +
  9 +import org.um.dsi.gavea.orcid.model.work.Work;
  10 +import org.um.dsi.gavea.orcid.model.work.WorkSummary;
  11 +import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
  12 +import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary.Works;
  13 +import org.um.dsi.gavea.orcid.model.activities.WorkGroup;
  14 +import org.um.dsi.gavea.orcid.model.common.RelationshipType;
  15 +import org.um.dsi.gavea.orcid.model.work.WorkExternalIdentifiers;
  16 +import org.um.dsi.gavea.orcid.model.work.ExternalIdentifier;
  17 +import org.apache.logging.log4j.LogManager;
  18 +import org.apache.logging.log4j.Logger;
  19 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  20 +
  21 +public class ORCIDHelper {
  22 +
  23 + public final ORCIDClient client;
  24 + private static final Logger _log = LogManager.getLogger(ORCIDHelper.class);
  25 +
  26 + public ORCIDHelper(ORCIDClient orcidClient) throws OrcidClientException {
  27 + this.client = orcidClient;
  28 + }
  29 +
  30 + /**
  31 + * Retrieves the entire set of work summaries in the ORCID profile. Merges
  32 + * each ORCID group into a single summary, following {@link #groupToWork}.
  33 + *
  34 + * @return The set of work summaries in the ORCID profile
  35 + * @throws OrcidClientException
  36 + */
  37 + public List<WorkSummary> getAllWorkSummaries() throws OrcidClientException, NullPointerException {
  38 + ActivitiesSummary activitiesSummary = client.getActivitiesSummary();
  39 + Stream<WorkGroup> workGroupList = activitiesSummary.getWorks().getGroup().stream();
  40 + Stream<WorkSummary> workSummaryList = workGroupList.map(w -> groupToWork(w));
  41 + return workSummaryList.collect(Collectors.toList());
  42 + }
  43 +
  44 + /**
  45 + * Retrieves the entire set of works in the ORCID profile whose source is
  46 + * the local CRIS service.
  47 + *
  48 + * @return The set of work summaries in the ORCID profile whose source is
  49 + * useDefault.
  50 + * @throws OrcidClientException
  51 + */
  52 + public List<WorkSummary> getSourcedWorkSummaries() throws OrcidClientException, NullPointerException {
  53 +
  54 + ActivitiesSummary activitiesSummary = client.getActivitiesSummary();
  55 + String sourceClientID = client.getClientId();
  56 + Works works = activitiesSummary.getWorks();
  57 + if (works == null) {
  58 + return new LinkedList<WorkSummary>();
  59 + }
  60 + Stream<WorkGroup> workGroupList = works.getGroup().stream();
  61 +
  62 + Stream<WorkSummary> workSummaryList = workGroupList.map(WorkGroup::getWorkSummary)
  63 + .flatMap(List::stream)
  64 + .filter(s -> s.getSource().getSourceOrcid().getUriPath().equals(sourceClientID));
  65 +
  66 + return workSummaryList.collect(Collectors.toList());
  67 + }
  68 +
  69 +
  70 + /**
  71 + * Delete all works from a specific Source
  72 + * @throws OrcidClientException
  73 + */
  74 + public void deleteAllSourcedWorks () throws OrcidClientException {
  75 + List<WorkSummary> workSummaryList = this.getSourcedWorkSummaries();
  76 +
  77 + for (WorkSummary workSummary : workSummaryList) {
  78 + client.deleteWork(workSummary.getPutCode());
  79 + }
  80 +
  81 + }
  82 +
  83 +
  84 + /**
  85 + * Retrieves the entire set of putCodes from an Activities Summary it's source independent
  86 + *
  87 + * @return a list of putCodes
  88 + */
  89 + public static List<BigInteger> getWorkSummaryPutCodes (ActivitiesSummary activitiesSummary) throws NullPointerException {
  90 + List<BigInteger> pubCodesList = new LinkedList<BigInteger>();
  91 + List<WorkSummary> workSummaryList;
  92 + BigInteger putCode;
  93 +
  94 + for (WorkGroup workGroup : activitiesSummary.getWorks().getGroup()) {
  95 + workSummaryList = workGroup.getWorkSummary();
  96 + for (WorkSummary workSummary : workSummaryList) {
  97 + putCode = workSummary.getPutCode();
  98 + pubCodesList.add(putCode);
  99 + }
  100 + }
  101 +
  102 + return pubCodesList;
  103 + }
  104 +
  105 +
  106 + /**
  107 + * @param work
  108 + * @return String with title
  109 + * @throws NullPointerException
  110 + */
  111 + public static String getWorkTitle(Work work) throws NullPointerException {
  112 + return work.getTitle().getTitle();
  113 + }
  114 +
  115 +
  116 + /**
  117 + * @param work
  118 + * @return BigInteger with putcode
  119 + * @throws NullPointerException
  120 + */
  121 + public static BigInteger getWorkPutCode(Work work) throws NullPointerException {
  122 + return work.getPutCode();
  123 + }
  124 +
  125 +
  126 + /**
  127 + * Retrieves the set of productions (from works) that share some UIDs with a
  128 + * work summary.
  129 + *
  130 + * @param summary
  131 + * The work summary to compare with the list of works.
  132 + * @param works
  133 + * The set of works to search for productions with shared UIDs.
  134 + * @return The set of works with matching UIDs.
  135 + */
  136 + public static List<Work> getWorksWithSharedUIDs(WorkSummary summary, List<Work> works) {
  137 + List<Work> matches = new LinkedList<Work>();
  138 + for (Work match : works) {
  139 + if (checkDuplicateUIDs(match.getExternalIdentifiers(), summary.getExternalIdentifiers()))
  140 + matches.add(match);
  141 + }
  142 + return matches;
  143 + }
  144 +
  145 + /**
  146 + * Tests whether two sets of external IDs have duplicates. The algorithm is
  147 + * the same as the one implemented by ORCID. Only considered duplicate if
  148 + * UIDs have the same relationship and are not "part of".
  149 + *
  150 + * @param uids1
  151 + * @param uids2
  152 + * @return
  153 + */
  154 + private static boolean checkDuplicateUIDs(WorkExternalIdentifiers uids1, WorkExternalIdentifiers uids2) {
  155 + if (uids2 != null && uids1 != null) {
  156 +
  157 + for (ExternalIdentifier uid2 : uids2.getWorkExternalIdentifier()) {
  158 + for (ExternalIdentifier uid1 : uids1.getWorkExternalIdentifier()) {
  159 +
  160 + if (sameButNotBothPartOf(uid2.getRelationship(), uid1.getRelationship()) &&
  161 + uid1.getExternalIdentifierId().equals(uid2.getExternalIdentifierId()) &&
  162 + uid1.getExternalIdentifierType().equals(uid2.getExternalIdentifierType())) {
  163 + return true;
  164 + }
  165 + }
  166 + }
  167 + }
  168 + return false;
  169 + }
  170 +
  171 + /**
  172 + * Tests whether two UIDs relationships are the same but not part of.
  173 + *
  174 + * @param r1
  175 + * @param r2
  176 + * @return
  177 + */
  178 + private static boolean sameButNotBothPartOf(RelationshipType r1, RelationshipType r2) {
  179 + if (r1 == null && r2 == null)
  180 + return true;
  181 + if (r1 != null && r1.equals(r2) && !r1.equals(RelationshipType.PART_OF))
  182 + return true;
  183 + return false;
  184 + }
  185 +
  186 + /**
  187 + * Merges a group into a work. Simply selects the first of the group and
  188 + * assigns it any extra UIDs.
  189 + *
  190 + * @param group
  191 + * The group to be merged.
  192 + * @return The resulting work summary.
  193 + */
  194 + public static WorkSummary groupToWork(WorkGroup group) {
  195 + WorkSummary aux = group.getWorkSummary().get(0);
  196 + WorkSummary dummy = new WorkSummary();
  197 + dummy.setCreatedDate(aux.getCreatedDate());
  198 + dummy.setDisplayIndex(aux.getDisplayIndex());
  199 + dummy.setExternalIdentifiers(aux.getExternalIdentifiers());
  200 + dummy.setLastModifiedDate(aux.getLastModifiedDate());
  201 + dummy.setPath(aux.getPath());
  202 + dummy.setPublicationDate(aux.getPublicationDate());
  203 + dummy.setPutCode(aux.getPutCode());
  204 + dummy.setSource(aux.getSource());
  205 + dummy.setTitle(aux.getTitle());
  206 + dummy.setType(aux.getType());
  207 + dummy.setVisibility(aux.getVisibility());
  208 + // TODO: add the other UIDs of the group
  209 + return dummy;
  210 + }
  211 +
  212 + /**
  213 + * Checks if localWork is already up to date on the information from
  214 + * remoteWork, i.e., localWork already has the same UIDs as remoteWork
  215 + *
  216 + * @param localWork
  217 + * The local work to check if it is up to date
  218 + * @param remoteWork
  219 + * The remote work to use when checking if the local work is up
  220 + * to date
  221 + * @return true if all the UIDs between the two works are the same, false
  222 + * otherwise
  223 + */
  224 + public static boolean isAlreadyUpToDate(Work localWork, Work remoteWork) {
  225 + // TODO Compare the two records to check if they are equal (when it
  226 + // comes to matching UIDs)
  227 + return false;
  228 + }
  229 +
  230 + public void deleteWork(BigInteger putCode) throws OrcidClientException {
  231 + _log.debug("[deleteWork] " + putCode);
  232 + client.deleteWork(putCode);
  233 + }
  234 +
  235 + public Work getFullWork(BigInteger putCode) throws OrcidClientException {
  236 + _log.debug("[getFullWork] " + putCode);
  237 + return client.getWork(putCode);
  238 + }
  239 +
  240 + public void updateWork(BigInteger putCode, Work work) throws OrcidClientException {
  241 + _log.debug("[updateWork] " + putCode);
  242 + client.updateWork(putCode, work);
  243 + }
  244 +
  245 + public Work addWork(Work work) throws OrcidClientException {
  246 + _log.debug("[addWork]");
  247 + //Remove any putCode if exists
  248 + work.setPutCode(null);
  249 + BigInteger putCode = new BigInteger(client.addWork(work));
  250 + work.setPutCode(putCode);
  251 + _log.debug("[addWork] " + putCode);
  252 + return work;
  253 + }
  254 +
  255 + public ActivitiesSummary getActivitiesSummary() throws OrcidClientException {
  256 + _log.debug("[getActivitiesSummary]");
  257 + return client.getActivitiesSummary();
  258 + }
  259 +
  260 + public ORCIDClient getClient () {
  261 + return this.client;
  262 + }
  263 +
  264 +}
... ...
ptcrisync/src/main/java/pt/ptcris/PTCRISync.java
... ... @@ -0,0 +1,177 @@
  1 +package pt.ptcris;
  2 +
  3 +import java.util.LinkedList;
  4 +import java.util.List;
  5 +
  6 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  7 +import org.um.dsi.gavea.orcid.model.work.Work;
  8 +import org.um.dsi.gavea.orcid.model.work.WorkSummary;
  9 +
  10 +import pt.ptcris.handlers.ProgressHandler;
  11 +import pt.ptcris.utils.UpdateRecord;
  12 +
  13 +import pt.ptcris.ORCIDHelper;
  14 +
  15 +public class PTCRISync {
  16 +
  17 + private static ORCIDHelper helper;
  18 +
  19 + /**
  20 + * Export a list of works to an ORCID profile.
  21 + *
  22 + * @param orcidClient
  23 + * The ORCID client to access to the profile.
  24 + * @param local_work
  25 + * The list of works to be exported (those marked as synced).
  26 + * @param progressHandler
  27 + * The implementation of the ProgressHandler interface
  28 + * responsible for receiving progress updates.
  29 + * @throws ORCIDException
  30 + */
  31 + public static void export(ORCIDClient orcidClient, List<Work> localWorks, ProgressHandler progressHandler)
  32 + throws OrcidClientException {
  33 +
  34 + int progress = 0;
  35 + progressHandler.setProgress(progress);
  36 + progressHandler.setCurrentStatus("ORCID_SYNC_EXPORT_STARTED");
  37 +
  38 + helper = new ORCIDHelper(orcidClient);
  39 +
  40 + List<WorkSummary> orcidWorks = helper.getSourcedWorkSummaries();
  41 + List<UpdateRecord> recordsToUpdate = new LinkedList<UpdateRecord>();
  42 +
  43 + progressHandler.setCurrentStatus("ORCID_SYNC_EXPORT_WORKS_ITERATION");
  44 + for (int counter = 0; counter != orcidWorks.size(); counter++) {
  45 + progress = (int) ((double) ((double) counter / orcidWorks.size()) * 100);
  46 + progressHandler.setProgress(progress);
  47 +
  48 + List<Work> matchingWorks = ORCIDHelper.getWorksWithSharedUIDs(orcidWorks.get(counter), localWorks);
  49 + if (matchingWorks.isEmpty()) {
  50 + helper.deleteWork(orcidWorks.get(counter).getPutCode());
  51 + } else {
  52 + for (Work localWork : matchingWorks) {
  53 + Work orcidWork = helper.getFullWork(orcidWorks.get(counter).getPutCode());
  54 + recordsToUpdate.add(new UpdateRecord(localWork, orcidWork));
  55 + localWorks.remove(localWork);
  56 + }
  57 + }
  58 + }
  59 +
  60 + progressHandler.setCurrentStatus("ORCID_SYNC_EXPORT_UPDATING_WORKS");
  61 + for (int counter = 0; counter != recordsToUpdate.size(); counter++) {
  62 + progress = (int) ((double) ((double) counter / recordsToUpdate.size()) * 100);
  63 + progressHandler.setProgress(progress);
  64 +
  65 + helper.updateWork(ORCIDHelper.getWorkPutCode(recordsToUpdate.get(counter).getRemoteWork()),
  66 + recordsToUpdate.get(counter).getLocalWork());
  67 + }
  68 +
  69 + progressHandler.setCurrentStatus("ORCID_SYNC_EXPORT_ADDING_WORKS");
  70 + for (int counter = 0; counter != localWorks.size(); counter++) {
  71 + progress = (int) ((double) ((double) counter / localWorks.size()) * 100);
  72 + progressHandler.setProgress(progress);
  73 +
  74 + helper.addWork(localWorks.get(counter));
  75 + }
  76 +
  77 + progressHandler.done();
  78 +
  79 + }
  80 +
  81 + /**
  82 + * Discover new works in an ORCID profile.
  83 + *
  84 + * @param orcidClient
  85 + * The ORCID client to access to the profile.
  86 + * @param localWorks
  87 + * The full list of works in the local profile. In fact, for each
  88 + * work only the external identifiers are needed, so the
  89 + * remaining attributes may be left null.
  90 + * @param progressHandler
  91 + * The implementation of the ProgressHandler interface
  92 + * responsible for receiving progress updates
  93 + * @return The list of new works found in the ORCID profile.
  94 + * @throws ORCIDException
  95 + */
  96 + public static List<Work> importWorks(ORCIDClient orcidClient, List<Work> localWorks,
  97 + ProgressHandler progressHandler) throws OrcidClientException {
  98 + int progress = 0;
  99 + progressHandler.setProgress(progress);
  100 + progressHandler.setCurrentStatus("ORCID_SYNC_IMPORT_WORKS_STARTED");
  101 +
  102 + List<Work> worksToImport = new LinkedList<Work>();
  103 +
  104 + helper = new ORCIDHelper(orcidClient);
  105 +
  106 + List<WorkSummary> orcidWorks = helper.getAllWorkSummaries();
  107 +
  108 + progressHandler.setCurrentStatus("ORCID_SYNC_IMPORT_WORKS_ITERATION");
  109 + for (int counter = 0; counter != orcidWorks.size(); counter++) {
  110 + progress = (int) ((double) ((double) counter / orcidWorks.size()) * 100);
  111 + progressHandler.setProgress(progress);
  112 +
  113 + List<Work> matchingWorks = ORCIDHelper.getWorksWithSharedUIDs(orcidWorks.get(counter), localWorks);
  114 + if (matchingWorks.isEmpty()) {
  115 + Work orcidWork = helper.getFullWork(orcidWorks.get(counter).getPutCode());
  116 + worksToImport.add(orcidWork);
  117 + }
  118 + }
  119 +
  120 + progressHandler.done();
  121 +
  122 + return worksToImport;
  123 + }
  124 +
  125 + /**
  126 + * Discover updates to existing works in an ORCID profile.
  127 + *
  128 + * @param orcidClient
  129 + * The ORCID client to access to the profile.
  130 + * @param localWorks
  131 + * The list of works for which we wish to discover updates (those
  132 + * marked as synced). For the moment, only new external
  133 + * identifiers will be found, so, for each work only the external
  134 + * identifiers are needed, so the remaining attributes may be
  135 + * left null. Also the putcode attribute should be used to store
  136 + * the local key of each work.
  137 + * @param progressHandler
  138 + * The implementation of the ProgressHandler interface
  139 + * responsible for receiving progress updates
  140 + * @return The list of updated works. Only the works that have changes are
  141 + * returned. Also, for each of them, only the attributes that
  142 + * changed are set. For the moment, only new external identifiers
  143 + * will be returned.
  144 + * @throws ORCIDException
  145 + */
  146 + public static List<Work> importUpdates(ORCIDClient orcidClient, List<Work> localWorks,
  147 + ProgressHandler progressHandler) throws OrcidClientException {
  148 + int progress = 0;
  149 + progressHandler.setProgress(progress);
  150 + progressHandler.setCurrentStatus("ORCID_SYNC_IMPORT_UPDATES_STARTED");
  151 +
  152 + List<Work> worksToUpdate = new LinkedList<Work>();
  153 + helper = new ORCIDHelper(orcidClient);
  154 + List<WorkSummary> orcidWorks = helper.getAllWorkSummaries();
  155 +
  156 + progressHandler.setCurrentStatus("ORCID_SYNC_IMPORT_UPDATES_ITERATION");
  157 + for (int counter = 0; counter != orcidWorks.size(); counter++) {
  158 + progress = (int) ((double) ((double) counter / orcidWorks.size()) * 100);
  159 + progressHandler.setProgress(progress);
  160 +
  161 + List<Work> matchingWorks = ORCIDHelper.getWorksWithSharedUIDs(orcidWorks.get(counter), localWorks);
  162 + if (!matchingWorks.isEmpty()) {
  163 + for (Work localWork : matchingWorks) {
  164 + Work orcidWork = helper.getFullWork(orcidWorks.get(counter).getPutCode());
  165 + if (!ORCIDHelper.isAlreadyUpToDate(localWork, orcidWork)) {
  166 + worksToUpdate.add(orcidWork);
  167 + }
  168 + }
  169 + }
  170 + }
  171 +
  172 + progressHandler.done();
  173 +
  174 + return worksToUpdate;
  175 + }
  176 +
  177 +}
... ...
ptcrisync/src/main/java/pt/ptcris/handlers/ProgressHandler.java
... ... @@ -0,0 +1,29 @@
  1 +package pt.ptcris.handlers;
  2 +
  3 +public interface ProgressHandler {
  4 + /**
  5 + * Used to set the progress of a task, by using percentual values (between 0 and 100). It can be used to start a task, where progress = 0
  6 + */
  7 + public void setProgress(int progress);
  8 +
  9 + /**
  10 + * Used to set the text of the current task
  11 + *
  12 + * @param message
  13 + * The message to be sent to the progress handler
  14 + */
  15 + public void setCurrentStatus(String message);
  16 +
  17 + /**
  18 + * Used to send an error message to the progress handler
  19 + *
  20 + * @param message
  21 + * The error message to be sent to the progress handler
  22 + */
  23 + public void sendError(String message);
  24 +
  25 + /**
  26 + * Used to set the current task as finalized
  27 + */
  28 + public void done();
  29 +}
... ...
ptcrisync/src/main/java/pt/ptcris/utils/UpdateRecord.java
... ... @@ -0,0 +1,30 @@
  1 +package pt.ptcris.utils;
  2 +
  3 +import org.um.dsi.gavea.orcid.model.work.Work;
  4 +
  5 +public class UpdateRecord {
  6 +
  7 + private Work localWork;
  8 + private Work remoteWork;
  9 +
  10 + public UpdateRecord(Work localWork, Work remoteWork) {
  11 + this.localWork = localWork;
  12 + this.remoteWork = remoteWork;
  13 + }
  14 +
  15 + public Work getLocalWork() {
  16 + return localWork;
  17 + }
  18 +
  19 + public void setLocalWork(Work localWork) {
  20 + this.localWork = localWork;
  21 + }
  22 +
  23 + public Work getRemoteWork() {
  24 + return remoteWork;
  25 + }
  26 +
  27 + public void setRemoteWork(Work remoteWork) {
  28 + this.remoteWork = remoteWork;
  29 + }
  30 +}
... ...
ptcrisync/src/pt/ptcris/ORCIDClient.java
... ... @@ -1,67 +0,0 @@
1   -package pt.ptcris;
2   -
3   -import java.math.BigInteger;
4   -
5   -import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
6   -import org.um.dsi.gavea.orcid.model.work.Work;
7   -import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
8   -
9   -public interface ORCIDClient {
10   -
11   -
12   - /**
13   - * Retrieves a full work from the ORCID profile.
14   - *
15   - * @param putCode The put-code of the work.
16   - * @return The full work.
17   - * @throws ORCIDException
18   - */
19   - public Work getWork(BigInteger putCode) throws OrcidClientException;
20   -
21   - /**
22   - * Add a work to the ORCID profile.
23   - *
24   - * @param work
25   - * The work to be added to the ORCID profile
26   - * @return the put-code in the ORCID profile of the newly created work.
27   - * @throws ORCIDException
28   - */
29   - public String addWork(Work work) throws OrcidClientException;
30   -
31   - /**
32   - * Delete a work from the ORCID profile.
33   - *
34   - * @param putCode
35   - * The put-code of the work to be deleted.
36   - * @throws ORCIDException
37   - */
38   - public void deleteWork(BigInteger putCode) throws OrcidClientException;
39   -
40   - /**
41   - * Update a work in the ORCID profile.
42   - *
43   - * @param putCode
44   - * The put-code of the work to be updated.
45   - * @param work
46   - * the new state of the work.
47   - * @return the updated work as represented in the ORCID profile.
48   - * @throws ORCIDException
49   - */
50   - public void updateWork(BigInteger putCode, Work work) throws OrcidClientException;
51   -
52   - /**
53   - * Retrieves every activity summary of the ORCID profile.
54   - *
55   - * @return The activities summary of the ORCID profile.
56   - * @throws ORCIDException
57   - */
58   - public ActivitiesSummary getActivitiesSummary() throws OrcidClientException;
59   -
60   - /**
61   - * Returns the ORCID's client id
62   - *
63   - * @return String with cliend id.
64   - */
65   - public String getClientId();
66   -
67   -}
68 0 \ No newline at end of file
ptcrisync/src/pt/ptcris/ORCIDClientImpl.java
... ... @@ -1,83 +0,0 @@
1   -package pt.ptcris;
2   -
3   -import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
4   -
5   -import java.math.BigInteger;
6   -
7   -import org.um.dsi.gavea.orcid.client.OrcidAccessToken;
8   -import org.um.dsi.gavea.orcid.client.OrcidOAuthClient;
9   -import org.um.dsi.gavea.orcid.model.work.Work;
10   -import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
11   -
12   -
13   -public class ORCIDClientImpl implements ORCIDClient {
14   -
15   - private final OrcidAccessToken orcidToken;
16   - private final OrcidOAuthClient orcidClient;
17   - private final String clientId;
18   -
19   - public ORCIDClientImpl(String loginUri, String apiUri, String clientId, String clientSecret, String redirectUri, OrcidAccessToken orcidToken) {
20   - this.orcidToken = orcidToken;
21   - this.clientId = clientId;
22   -
23   - // Instantiate the Orcid Client
24   - this.orcidClient = new OrcidOAuthClient(loginUri, apiUri, clientId, clientSecret, redirectUri);
25   - }
26   -
27   - /**
28   - * @see pt.ptcris.ORCIDClient#getFullWork(java.lang.Long)
29   - */
30   - public Work getWork(BigInteger putCode) throws OrcidClientException {
31   - return this.orcidClient.readWork(this.orcidToken, putCode.toString());
32   - }
33   -
34   - /**
35   - * @see pt.ptcris.ORCIDClient#addWork(org.orcid.jaxb.model.record_rc2.Work)
36   - */
37   - public String addWork(Work work) throws OrcidClientException {
38   - return this.orcidClient.addWork(this.orcidToken, work);
39   - }
40   -
41   - /**
42   - * @see pt.ptcris.ORCIDClient#deleteWork(java.lang.Long)
43   - */
44   - public void deleteWork(BigInteger putCode) throws OrcidClientException {
45   - this.orcidClient.deleteWork(this.orcidToken, putCode.toString());
46   -
47   - // NOTE: according to the ORCID API, to delete a work, one must provide
48   - // the entire list of works in the ORCID profile minus the work(s) that
49   - // should be deleted. This means that this operation must be done in
50   - // three steps: first, retrieve the entire set of works; second, remove
51   - // the
52   - // work to be deleted from the list of works; and three, send the
53   - // updated list to the ORCID API.
54   - }
55   -
56   - /**
57   - * @see pt.ptcris.ORCIDClient#updateWork(java.lang.Long,
58   - * org.orcid.jaxb.model.record_rc2.Work)
59   - */
60   - public void updateWork(BigInteger putCode, Work work) throws OrcidClientException {
61   - this.orcidClient.updateWork(this.orcidToken, putCode.toString(), work);
62   -
63   - // NOTE: according to the ORCID API, to update a work, one must provide
64   - // the entire list of works in the ORCID profile including the work(s)
65   - // that should be updated. This means that this operation must be done
66   - // in three steps: first, retrieve the entire set of works; second,
67   - // replace the work to be updated with the new record in the list of
68   - // works; and three, send the updated list to the ORCID API.
69   - }
70   -
71   - /**
72   - * @see pt.ptcris.ORCIDClient#getActivitiesSummary()
73   - */
74   - public ActivitiesSummary getActivitiesSummary() throws OrcidClientException {
75   - return orcidClient.readActivitiesSummary(orcidToken);
76   - }
77   -
78   - public String getClientId() {
79   - return this.clientId;
80   - }
81   -
82   -
83   -}
ptcrisync/src/pt/ptcris/ORCIDHelper.java
... ... @@ -1,264 +0,0 @@
1   -package pt.ptcris;
2   -
3   -import java.math.BigInteger;
4   -import java.util.LinkedList;
5   -import java.util.List;
6   -import java.util.stream.Collectors;
7   -import java.util.stream.Stream;
8   -
9   -import org.um.dsi.gavea.orcid.model.work.Work;
10   -import org.um.dsi.gavea.orcid.model.work.WorkSummary;
11   -import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
12   -import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary.Works;
13   -import org.um.dsi.gavea.orcid.model.activities.WorkGroup;
14   -import org.um.dsi.gavea.orcid.model.common.RelationshipType;
15   -import org.um.dsi.gavea.orcid.model.work.WorkExternalIdentifiers;
16   -import org.um.dsi.gavea.orcid.model.work.ExternalIdentifier;
17   -import org.apache.logging.log4j.LogManager;
18   -import org.apache.logging.log4j.Logger;
19   -import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
20   -
21   -public class ORCIDHelper {
22   -
23   - public final ORCIDClient client;
24   - private static final Logger _log = LogManager.getLogger(ORCIDHelper.class);
25   -
26   - public ORCIDHelper(ORCIDClient orcidClient) throws OrcidClientException {
27   - this.client = orcidClient;
28   - }
29   -
30   - /**
31   - * Retrieves the entire set of work summaries in the ORCID profile. Merges
32   - * each ORCID group into a single summary, following {@link #groupToWork}.
33   - *
34   - * @return The set of work summaries in the ORCID profile
35   - * @throws OrcidClientException
36   - */
37   - public List<WorkSummary> getAllWorkSummaries() throws OrcidClientException, NullPointerException {
38   - ActivitiesSummary activitiesSummary = client.getActivitiesSummary();
39   - Stream<WorkGroup> workGroupList = activitiesSummary.getWorks().getGroup().stream();
40   - Stream<WorkSummary> workSummaryList = workGroupList.map(w -> groupToWork(w));
41   - return workSummaryList.collect(Collectors.toList());
42   - }
43   -
44   - /**
45   - * Retrieves the entire set of works in the ORCID profile whose source is
46   - * the local CRIS service.
47   - *
48   - * @return The set of work summaries in the ORCID profile whose source is
49   - * useDefault.
50   - * @throws OrcidClientException
51   - */
52   - public List<WorkSummary> getSourcedWorkSummaries() throws OrcidClientException, NullPointerException {
53   -
54   - ActivitiesSummary activitiesSummary = client.getActivitiesSummary();
55   - String sourceClientID = client.getClientId();
56   - Works works = activitiesSummary.getWorks();
57   - if (works == null) {
58   - return new LinkedList<WorkSummary>();
59   - }
60   - Stream<WorkGroup> workGroupList = works.getGroup().stream();
61   -
62   - Stream<WorkSummary> workSummaryList = workGroupList.map(WorkGroup::getWorkSummary)
63   - .flatMap(List::stream)
64   - .filter(s -> s.getSource().getSourceOrcid().getUriPath().equals(sourceClientID));
65   -
66   - return workSummaryList.collect(Collectors.toList());
67   - }
68   -
69   -
70   - /**
71   - * Delete all works from a specific Source
72   - * @throws OrcidClientException
73   - */
74   - public void deleteAllSourcedWorks () throws OrcidClientException {
75   - List<WorkSummary> workSummaryList = this.getSourcedWorkSummaries();
76   -
77   - for (WorkSummary workSummary : workSummaryList) {
78   - client.deleteWork(workSummary.getPutCode());
79   - }
80   -
81   - }
82   -
83   -
84   - /**
85   - * Retrieves the entire set of putCodes from an Activities Summary it's source independent
86   - *
87   - * @return a list of putCodes
88   - */
89   - public static List<BigInteger> getWorkSummaryPutCodes (ActivitiesSummary activitiesSummary) throws NullPointerException {
90   - List<BigInteger> pubCodesList = new LinkedList<BigInteger>();
91   - List<WorkSummary> workSummaryList;
92   - BigInteger putCode;
93   -
94   - for (WorkGroup workGroup : activitiesSummary.getWorks().getGroup()) {
95   - workSummaryList = workGroup.getWorkSummary();
96   - for (WorkSummary workSummary : workSummaryList) {
97   - putCode = workSummary.getPutCode();
98   - pubCodesList.add(putCode);
99   - }
100   - }
101   -
102   - return pubCodesList;
103   - }
104   -
105   -
106   - /**
107   - * @param work
108   - * @return String with title
109   - * @throws NullPointerException
110   - */
111   - public static String getWorkTitle(Work work) throws NullPointerException {
112   - return work.getTitle().getTitle();
113   - }
114   -
115   -
116   - /**
117   - * @param work
118   - * @return BigInteger with putcode
119   - * @throws NullPointerException
120   - */
121   - public static BigInteger getWorkPutCode(Work work) throws NullPointerException {
122   - return work.getPutCode();
123   - }
124   -
125   -
126   - /**
127   - * Retrieves the set of productions (from works) that share some UIDs with a
128   - * work summary.
129   - *
130   - * @param summary
131   - * The work summary to compare with the list of works.
132   - * @param works
133   - * The set of works to search for productions with shared UIDs.
134   - * @return The set of works with matching UIDs.
135   - */
136   - public static List<Work> getWorksWithSharedUIDs(WorkSummary summary, List<Work> works) {
137   - List<Work> matches = new LinkedList<Work>();
138   - for (Work match : works) {
139   - if (checkDuplicateUIDs(match.getExternalIdentifiers(), summary.getExternalIdentifiers()))
140   - matches.add(match);
141   - }
142   - return matches;
143   - }
144   -
145   - /**
146   - * Tests whether two sets of external IDs have duplicates. The algorithm is
147   - * the same as the one implemented by ORCID. Only considered duplicate if
148   - * UIDs have the same relationship and are not "part of".
149   - *
150   - * @param uids1
151   - * @param uids2
152   - * @return
153   - */
154   - private static boolean checkDuplicateUIDs(WorkExternalIdentifiers uids1, WorkExternalIdentifiers uids2) {
155   - if (uids2 != null && uids1 != null) {
156   -
157   - for (ExternalIdentifier uid2 : uids2.getWorkExternalIdentifier()) {
158   - for (ExternalIdentifier uid1 : uids1.getWorkExternalIdentifier()) {
159   -
160   - if (sameButNotBothPartOf(uid2.getRelationship(), uid1.getRelationship()) &&
161   - uid1.getExternalIdentifierId().equals(uid2.getExternalIdentifierId()) &&
162   - uid1.getExternalIdentifierType().equals(uid2.getExternalIdentifierType())) {
163   - return true;
164   - }
165   - }
166   - }
167   - }
168   - return false;
169   - }
170   -
171   - /**
172   - * Tests whether two UIDs relationships are the same but not part of.
173   - *
174   - * @param r1
175   - * @param r2
176   - * @return
177   - */
178   - private static boolean sameButNotBothPartOf(RelationshipType r1, RelationshipType r2) {
179   - if (r1 == null && r2 == null)
180   - return true;
181   - if (r1 != null && r1.equals(r2) && !r1.equals(RelationshipType.PART_OF))
182   - return true;
183   - return false;
184   - }
185   -
186   - /**
187   - * Merges a group into a work. Simply selects the first of the group and
188   - * assigns it any extra UIDs.
189   - *
190   - * @param group
191   - * The group to be merged.
192   - * @return The resulting work summary.
193   - */
194   - public static WorkSummary groupToWork(WorkGroup group) {
195   - WorkSummary aux = group.getWorkSummary().get(0);
196   - WorkSummary dummy = new WorkSummary();
197   - dummy.setCreatedDate(aux.getCreatedDate());
198   - dummy.setDisplayIndex(aux.getDisplayIndex());
199   - dummy.setExternalIdentifiers(aux.getExternalIdentifiers());
200   - dummy.setLastModifiedDate(aux.getLastModifiedDate());
201   - dummy.setPath(aux.getPath());
202   - dummy.setPublicationDate(aux.getPublicationDate());
203   - dummy.setPutCode(aux.getPutCode());
204   - dummy.setSource(aux.getSource());
205   - dummy.setTitle(aux.getTitle());
206   - dummy.setType(aux.getType());
207   - dummy.setVisibility(aux.getVisibility());
208   - // TODO: add the other UIDs of the group
209   - return dummy;
210   - }
211   -
212   - /**
213   - * Checks if localWork is already up to date on the information from
214   - * remoteWork, i.e., localWork already has the same UIDs as remoteWork
215   - *
216   - * @param localWork
217   - * The local work to check if it is up to date
218   - * @param remoteWork
219   - * The remote work to use when checking if the local work is up
220   - * to date
221   - * @return true if all the UIDs between the two works are the same, false
222   - * otherwise
223   - */
224   - public static boolean isAlreadyUpToDate(Work localWork, Work remoteWork) {
225   - // TODO Compare the two records to check if they are equal (when it
226   - // comes to matching UIDs)
227   - return false;
228   - }
229   -
230   - public void deleteWork(BigInteger putCode) throws OrcidClientException {
231   - _log.debug("[deleteWork] " + putCode);
232   - client.deleteWork(putCode);
233   - }
234   -