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
112 public InvocationResults updateReferencingRareFlags(String taxonCsid, String vocabularyCsid) throws Exception {
113 PoxPayloadOut taxonPayload = vocabularyCsid == null
114 ? findTaxonByCsid(taxonCsid)
115 : findTaxonByCsid(taxonCsid, vocabularyCsid);
116 String taxonRefName = getFieldValue(taxonPayload, TaxonConstants.REFNAME_SCHEMA_NAME, TaxonConstants.REFNAME_FIELD_NAME);
118 RefName.AuthorityItem item = RefName.AuthorityItem.parse(taxonRefName);
119 String vocabularyShortId = item.getParentShortIdentifier();
121 List<String> collectionObjectCsids = findReferencingCollectionObjects(TaxonomyAuthorityClient.SERVICE_NAME, vocabularyShortId, taxonCsid,
122 CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME + ":" + TAXON_FIELD_NAME_WITHOUT_PATH);
124 long numAffected = 0;
126 for (String collectionObjectCsid : collectionObjectCsids) {
127 // Filter out results where the taxon is referenced in the correct field, but isn't the primary value.
129 PoxPayloadOut collectionObjectPayload = findCollectionObjectByCsid(collectionObjectCsid);
130 String primaryTaxonRefName = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME,
131 CollectionObjectBotGardenConstants.TAXON_FIELD_NAME);
133 if (primaryTaxonRefName.equals(taxonRefName)) {
136 InvocationResults itemResults = updateRareFlag(collectionObjectPayload);
137 numAffected += itemResults.getNumAffected();
141 InvocationResults results = new InvocationResults();
142 results.setNumAffected(numAffected);
143 results.setUserNote(numFound + " referencing cataloging " + (numFound == 1 ? "record" : "records") + " found, " + numAffected + " updated");
148 public InvocationResults updateReferencingRareFlags(String taxonCsid) throws URISyntaxException, DocumentException, Exception {
149 return updateReferencingRareFlags(taxonCsid, null);
153 * Updates the rare flag of the specified collectionobject.
155 * @param collectionObjectCsid The csid of the collectionobject
159 public InvocationResults updateRareFlag(String collectionObjectCsid) throws Exception {
160 PoxPayloadOut collectionObjectPayload = findCollectionObjectByCsid(collectionObjectCsid);
162 return updateRareFlag(collectionObjectPayload);
166 * Updates the rare flag of the specified collectionobject. The rare flag is determined by looking at
167 * the taxon record that is referenced by the primary taxonomic determination of the collectionobject.
168 * If the taxon record has a conservation category that is considered rare in its primary plant attributes
169 * group, the rare flag is set to true. Otherwise, it is set to false.
171 * @param collectionObjectPayload The payload representing the collectionobject
175 public InvocationResults updateRareFlag(PoxPayloadOut collectionObjectPayload) throws Exception {
176 InvocationResults results = new InvocationResults();
178 String uri = this.getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.URI_SCHEMA_NAME,
179 CollectionObjectBotGardenConstants.URI_FIELD_NAME);
180 String[] uriParts = uri.split("\\/");
181 String collectionObjectCsid = uriParts[uriParts.length-1];
183 String workflowState = getFieldValue(collectionObjectPayload, CollectionObjectConstants.WORKFLOW_STATE_SCHEMA_NAME,
184 CollectionObjectConstants.WORKFLOW_STATE_FIELD_NAME);
186 if (workflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
187 logger.debug("skipping deleted collectionobject: " + collectionObjectCsid);
190 String taxonRefName = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.TAXON_SCHEMA_NAME,
191 CollectionObjectBotGardenConstants.TAXON_FIELD_NAME);
192 String oldIsRare = getFieldValue(collectionObjectPayload, CollectionObjectBotGardenConstants.RARE_FLAG_SCHEMA_NAME,
193 CollectionObjectBotGardenConstants.RARE_FLAG_FIELD_NAME);
195 if (oldIsRare == null) {
199 String newIsRare = "false";
201 if (StringUtils.isNotBlank(taxonRefName)) {
202 PoxPayloadOut taxonPayload = null;
205 taxonPayload = findTaxonByRefName(taxonRefName);
207 catch (WebApplicationException e) {
208 logger.error("Error finding taxon: refName=" + taxonRefName, e);
211 if (taxonPayload != null) {
212 // UCBG-369: Changing this so that it only checks the primary conservation category.
214 String conservationCategory = getFieldValue(taxonPayload, TaxonBotGardenConstants.CONSERVATION_CATEGORY_SCHEMA_NAME,
215 TaxonBotGardenConstants.CONSERVATION_CATEGORY_FIELD_NAME);
217 if (isRare(conservationCategory)) {
223 if (!newIsRare.equals(oldIsRare)) {
224 logger.debug("setting rare flag: collectionObjectCsid=" + collectionObjectCsid + " oldIsRare=" + oldIsRare +" newIsRare=" + newIsRare);
226 setRareFlag(collectionObjectCsid, newIsRare);
228 results.setNumAffected(1);
229 results.setUserNote("rare flag set to " + newIsRare);
232 logger.debug("not setting rare flag: collectionObjectCsid=" + collectionObjectCsid + " oldIsRare=" + oldIsRare +" newIsRare=" + newIsRare);
234 results.setNumAffected(0);
235 results.setUserNote("rare flag not changed");
242 public static boolean isRare(String conservationCategoryRefName) {
243 boolean isRare = false;
245 if (StringUtils.isNotEmpty(conservationCategoryRefName)) {
246 // The conservation category is non-empty, so it's rare...
249 // ...unless it's one of the non-rare ones.
251 // Check if the display name starts with a prefix that
252 // indicates that it isn't rare.
254 RefName.AuthorityItem item = RefName.AuthorityItem.parse(conservationCategoryRefName);
255 String displayName = item.getDisplayName();
257 for (String prefix : NON_RARE_CONSERVATION_CATEGORY_PREFIXES) {
258 if (displayName.startsWith(prefix)) {
269 * Updates the rare flags of the specified collectionobjects.
271 * @param collectionObjectCsids The csids of the collectionobjects
275 public InvocationResults updateRareFlags(List<String> collectionObjectCsids) throws Exception {
276 int numSubmitted = collectionObjectCsids.size();
277 long numAffected = 0;
280 for (String collectionObjectCsid : collectionObjectCsids) {
281 InvocationResults itemResults = updateRareFlag(collectionObjectCsid);
283 numAffected += itemResults.getNumAffected();
286 InvocationResults results = new InvocationResults();
287 results.setNumAffected(numAffected);
288 results.setUserNote("updated " + numAffected + " of " + numSubmitted + " cataloging records");
294 * Updates the rare flags of all collectionobjects.
299 public InvocationResults updateAllRareFlags() throws Exception {
301 long numAffected = 0;
305 List<String> csids = Collections.emptyList();
308 csids = findAllCollectionObjects(pageSize, pageNum);
309 logger.debug("pageNum=" + pageNum + " pageSize=" + pageSize + " result size=" + csids.size());
311 InvocationResults pageResults = updateRareFlags(csids);
313 numAffected += pageResults.getNumAffected();
314 numFound += csids.size();
318 while (csids.size() == pageSize);
320 InvocationResults results = new InvocationResults();
321 results.setNumAffected(numAffected);
322 results.setUserNote("updated " + numAffected + " of " + numFound + " cataloging records");
328 * Sets the rare flag of the specified collectionobject to the specified value.
330 * @param collectionObjectCsid The csid of the collectionobject
331 * @param rareFlag The value of the rare flag
332 * @throws URISyntaxException
334 private void setRareFlag(String collectionObjectCsid, String rareFlag) throws URISyntaxException {
335 String updatePayload =
336 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
337 "<document name=\"collectionobjects\">" +
338 "<ns2:collectionobjects_naturalhistory xmlns:ns2=\"http://collectionspace.org/services/collectionobject/domain/naturalhistory\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
339 getFieldXml("rare", rareFlag) +
340 "</ns2:collectionobjects_naturalhistory>" +
341 "<ns2:collectionobjects_common xmlns:ns2=\"http://collectionspace.org/services/collectionobject\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
342 "</ns2:collectionobjects_common>" +
345 NuxeoBasedResource resource = (NuxeoBasedResource) getResourceMap().get(CollectionObjectClient.SERVICE_NAME);
346 resource.update(getServiceContext(), getResourceMap(), createUriInfo(), collectionObjectCsid, updatePayload);