1 package org.collectionspace.services.batch.nuxeo;
3 import java.net.URISyntaxException;
4 import java.util.Arrays;
5 import java.util.Collections;
8 import javax.ws.rs.WebApplicationException;
10 import org.apache.commons.lang.StringUtils;
11 import org.collectionspace.services.client.CollectionObjectClient;
12 import org.collectionspace.services.client.PoxPayloadOut;
13 import org.collectionspace.services.client.TaxonomyAuthorityClient;
14 import org.collectionspace.services.client.workflow.WorkflowClient;
15 import org.collectionspace.services.collectionobject.nuxeo.CollectionObjectBotGardenConstants;
16 import org.collectionspace.services.collectionobject.nuxeo.CollectionObjectConstants;
17 import org.collectionspace.services.common.NuxeoBasedResource;
18 import org.collectionspace.services.common.api.RefName;
19 import org.collectionspace.services.common.invocable.InvocationContext.ListCSIDs;
20 import org.collectionspace.services.common.invocable.InvocationResults;
21 import org.collectionspace.services.taxonomy.nuxeo.TaxonBotGardenConstants;
22 import org.collectionspace.services.taxonomy.nuxeo.TaxonConstants;
23 import org.dom4j.DocumentException;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 public class UpdateRareFlagBatchJob extends AbstractBatchJob {
28 final Logger logger = LoggerFactory.getLogger(UpdateRareFlagBatchJob.class);
30 // All conservation categories are considered rare, except for ones that start with the following prefixes.
31 public static final List<String> NON_RARE_CONSERVATION_CATEGORY_PREFIXES = Arrays.asList("none", "DD ", "LC ", "LR (lc) ");
33 private static final String[] TAXON_FIELD_NAME_PARTS = CollectionObjectBotGardenConstants.TAXON_FIELD_NAME.split("\\/");
34 private static final String TAXON_FIELD_NAME_WITHOUT_PATH = TAXON_FIELD_NAME_PARTS[TAXON_FIELD_NAME_PARTS.length - 1];
36 public UpdateRareFlagBatchJob() {
37 this.setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST, INVOCATION_MODE_NO_CONTEXT));
42 setCompletionStatus(STATUS_MIN_PROGRESS);
45 String mode = getInvocationContext().getMode();
47 if (mode.equals(INVOCATION_MODE_SINGLE)) {
49 * In a single document context, the single csid must specify a collectionobject or a
50 * taxonomy record. If it's a collectionobject, the rare flag for the specified
51 * collectionobject will be updated. If it's a taxonomy record, the rare flag will be
52 * updated for each collectionobject with a primary determination that refers to the
53 * specified taxonomy record.
56 String csid = getInvocationContext().getSingleCSID();
58 if (StringUtils.isEmpty(csid)) {
59 throw new Exception("Missing context csid");
62 String docType = getInvocationContext().getDocType();
64 if (docType.equals(CollectionObjectConstants.NUXEO_DOCTYPE)) {
65 setResults(updateRareFlag(csid));
67 else if (docType.equals(TaxonConstants.NUXEO_DOCTYPE)) {
68 setResults(updateReferencingRareFlags(csid));
71 throw new Exception("Unsupported document type: " + docType);
74 else if (mode.equals(INVOCATION_MODE_LIST)) {
76 * In a list context, the csids must specify collectionobjects. The rare flag for
77 * each collectionobject will be updated.
79 ListCSIDs csids = getInvocationContext().getListCSIDs();
81 setResults(updateRareFlags(csids.getCsid()));
83 else if (mode.equals(INVOCATION_MODE_NO_CONTEXT)) {
85 * If there is no context, the rare flag will be updated for all (non-deleted)
89 setResults(updateAllRareFlags());
92 throw new Exception("Unsupported invocation mode: " + mode);
95 setCompletionStatus(STATUS_COMPLETE);
98 setCompletionStatus(STATUS_ERROR);
99 setErrorInfo(new InvocationError(INT_ERROR_STATUS, e.getMessage()));
104 * Updates the rare flags of collectionobjects that refer to the specified taxon record.
105 * A collectionobject is considered to refer to the taxon record if the refname of its
106 * primary taxonomic identification is the refname of the taxon record.
108 * @param taxonCsid The csid of the taxon record
110 * @throws URISyntaxException
111 * @throws DocumentException
113 public InvocationResults updateReferencingRareFlags(String taxonCsid) throws URISyntaxException, DocumentException {
114 PoxPayloadOut taxonPayload = findTaxonByCsid(taxonCsid);
115 String taxonRefName = getFieldValue(taxonPayload, TaxonConstants.REFNAME_SCHEMA_NAME, TaxonConstants.REFNAME_FIELD_NAME);
117 RefName.AuthorityItem item = RefName.AuthorityItem.parse(taxonRefName);
118 String vocabularyShortId = item.getParentShortIdentifier();
120 List<String> collectionObjectCsids = findReferencingCollectionObjects(TaxonomyAuthorityClient.SERVICE_NAME, vocabularyShortId, taxonCsid,
121 CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME + ":" + TAXON_FIELD_NAME_WITHOUT_PATH);
123 long numAffected = 0;
125 for (String collectionObjectCsid : collectionObjectCsids) {
126 // Filter out results where the taxon is referenced in the correct field, but isn't the primary value.
128 PoxPayloadOut collectionObjectPayload = findCollectionObjectByCsid(collectionObjectCsid);
129 String primaryTaxonRefName = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME,
130 CollectionObjectBotGardenConstants.TAXON_FIELD_NAME);
132 if (primaryTaxonRefName.equals(taxonRefName)) {
135 InvocationResults itemResults = updateRareFlag(collectionObjectPayload);
136 numAffected += itemResults.getNumAffected();
140 InvocationResults results = new InvocationResults();
141 results.setNumAffected(numAffected);
142 results.setUserNote(numFound + " referencing cataloging " + (numFound == 1 ? "record" : "records") + " found, " + numAffected + " updated");
148 * Updates the rare flag of the specified collectionobject.
150 * @param collectionObjectCsid The csid of the collectionobject
152 * @throws URISyntaxException
153 * @throws DocumentException
155 public InvocationResults updateRareFlag(String collectionObjectCsid) throws URISyntaxException, DocumentException {
156 PoxPayloadOut collectionObjectPayload = findCollectionObjectByCsid(collectionObjectCsid);
158 return updateRareFlag(collectionObjectPayload);
162 * Updates the rare flag of the specified collectionobject. The rare flag is determined by looking at
163 * the taxon record that is referenced by the primary taxonomic determination of the collectionobject.
164 * If the taxon record has a conservation category that is considered rare in its primary plant attributes
165 * group, the rare flag is set to true. Otherwise, it is set to false.
167 * @param collectionObjectPayload The payload representing the collectionobject
169 * @throws URISyntaxException
170 * @throws DocumentException
172 public InvocationResults updateRareFlag(PoxPayloadOut collectionObjectPayload) throws URISyntaxException, DocumentException {
173 InvocationResults results = new InvocationResults();
175 String uri = this.getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.URI_SCHEMA_NAME,
176 CollectionObjectBotGardenConstants.URI_FIELD_NAME);
177 String[] uriParts = uri.split("\\/");
178 String collectionObjectCsid = uriParts[uriParts.length-1];
180 String workflowState = getFieldValue(collectionObjectPayload, CollectionObjectConstants.WORKFLOW_STATE_SCHEMA_NAME,
181 CollectionObjectConstants.WORKFLOW_STATE_FIELD_NAME);
183 if (workflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
184 logger.debug("skipping deleted collectionobject: " + collectionObjectCsid);
187 String taxonRefName = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME,
188 CollectionObjectBotGardenConstants.TAXON_FIELD_NAME);
189 String oldIsRare = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.RARE_FLAG_SCHEMA_NAME,
190 CollectionObjectBotGardenConstants.RARE_FLAG_FIELD_NAME);
192 if (oldIsRare == null) {
196 String newIsRare = "false";
198 if (StringUtils.isNotBlank(taxonRefName)) {
199 PoxPayloadOut taxonPayload = null;
202 taxonPayload = findTaxonByRefName(taxonRefName);
204 catch (WebApplicationException e) {
205 logger.error("Error finding taxon: refName=" + taxonRefName, e);
208 if (taxonPayload != null) {
209 // UCBG-369: Changing this so that it only checks the primary conservation category.
211 String conservationCategory = getFieldValue(taxonPayload, TaxonBotGardenConstants.CONSERVATION_CATEGORY_SCHEMA_NAME,
212 TaxonBotGardenConstants.CONSERVATION_CATEGORY_FIELD_NAME);
214 if (isRare(conservationCategory)) {
220 if (!newIsRare.equals(oldIsRare)) {
221 logger.debug("setting rare flag: collectionObjectCsid=" + collectionObjectCsid + " oldIsRare=" + oldIsRare +" newIsRare=" + newIsRare);
223 setRareFlag(collectionObjectCsid, newIsRare);
225 results.setNumAffected(1);
226 results.setUserNote("rare flag set to " + newIsRare);
229 logger.debug("not setting rare flag: collectionObjectCsid=" + collectionObjectCsid + " oldIsRare=" + oldIsRare +" newIsRare=" + newIsRare);
231 results.setNumAffected(0);
232 results.setUserNote("rare flag not changed");
239 public static boolean isRare(String conservationCategoryRefName) {
240 boolean isRare = false;
242 if (StringUtils.isNotEmpty(conservationCategoryRefName)) {
243 // The conservation category is non-empty, so it's rare...
246 // ...unless it's one of the non-rare ones.
248 // Check if the display name starts with a prefix that
249 // indicates that it isn't rare.
251 RefName.AuthorityItem item = RefName.AuthorityItem.parse(conservationCategoryRefName);
252 String displayName = item.getDisplayName();
254 for (String prefix : NON_RARE_CONSERVATION_CATEGORY_PREFIXES) {
255 if (displayName.startsWith(prefix)) {
266 * Updates the rare flags of the specified collectionobjects.
268 * @param collectionObjectCsids The csids of the collectionobjects
270 * @throws URISyntaxException
271 * @throws DocumentException
273 public InvocationResults updateRareFlags(List<String> collectionObjectCsids) throws URISyntaxException, DocumentException {
274 int numSubmitted = collectionObjectCsids.size();
275 long numAffected = 0;
278 for (String collectionObjectCsid : collectionObjectCsids) {
279 InvocationResults itemResults = updateRareFlag(collectionObjectCsid);
281 numAffected += itemResults.getNumAffected();
284 InvocationResults results = new InvocationResults();
285 results.setNumAffected(numAffected);
286 results.setUserNote("updated " + numAffected + " of " + numSubmitted + " cataloging records");
292 * Updates the rare flags of all collectionobjects.
295 * @throws URISyntaxException
296 * @throws DocumentException
298 public InvocationResults updateAllRareFlags() throws URISyntaxException, DocumentException {
300 long numAffected = 0;
304 List<String> csids = Collections.emptyList();
307 csids = findAllCollectionObjects(pageSize, pageNum);
308 logger.debug("pageNum=" + pageNum + " pageSize=" + pageSize + " result size=" + csids.size());
310 InvocationResults pageResults = updateRareFlags(csids);
312 numAffected += pageResults.getNumAffected();
313 numFound += csids.size();
317 while (csids.size() == pageSize);
319 InvocationResults results = new InvocationResults();
320 results.setNumAffected(numAffected);
321 results.setUserNote("updated " + numAffected + " of " + numFound + " cataloging records");
327 * Sets the rare flag of the specified collectionobject to the specified value.
329 * @param collectionObjectCsid The csid of the collectionobject
330 * @param rareFlag The value of the rare flag
331 * @throws URISyntaxException
333 private void setRareFlag(String collectionObjectCsid, String rareFlag) throws URISyntaxException {
334 String updatePayload =
335 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
336 "<document name=\"collectionobjects\">" +
337 "<ns2:collectionobjects_naturalhistory xmlns:ns2=\"http://collectionspace.org/services/collectionobject/domain/naturalhistory\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
338 getFieldXml("rare", rareFlag) +
339 "</ns2:collectionobjects_naturalhistory>" +
340 "<ns2:collectionobjects_common xmlns:ns2=\"http://collectionspace.org/services/collectionobject\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
341 "</ns2:collectionobjects_common>" +
344 NuxeoBasedResource resource = (NuxeoBasedResource) getResourceMap().get(CollectionObjectClient.SERVICE_NAME);
345 resource.update(getResourceMap(), createUriInfo(), collectionObjectCsid, updatePayload);