2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.dataInserter
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Arrays
;
13 import java
.util
.EnumSet
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
18 import java
.util
.UUID
;
20 import org
.apache
.log4j
.Level
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.hibernate
.Session
;
23 import org
.springframework
.context
.event
.ContextRefreshedEvent
;
24 import org
.springframework
.security
.authentication
.AuthenticationCredentialsNotFoundException
;
25 import org
.springframework
.security
.core
.GrantedAuthority
;
26 import org
.springframework
.transaction
.TransactionStatus
;
27 import org
.springframework
.transaction
.annotation
.Transactional
;
29 import eu
.etaxonomy
.cdm
.api
.application
.AbstractDataInserter
;
30 import eu
.etaxonomy
.cdm
.api
.application
.CdmRepository
;
31 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
32 import eu
.etaxonomy
.cdm
.model
.agent
.Institution
;
33 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
34 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTermBase
;
35 import eu
.etaxonomy
.cdm
.model
.common
.GrantedAuthorityImpl
;
36 import eu
.etaxonomy
.cdm
.model
.common
.Group
;
37 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
38 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
39 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
40 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatusType
;
41 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
42 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
45 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.taxonGraph
.AbstractHibernateTaxonGraphProcessor
;
46 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxonGraph
.TaxonGraphException
;
47 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CRUD
;
48 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CdmAuthority
;
49 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CdmPermissionClass
;
50 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.Role
;
51 import eu
.etaxonomy
.cdm
.vaadin
.model
.registration
.KindOfUnitTerms
;
52 import eu
.etaxonomy
.cdm
.vaadin
.model
.registration
.RegistrationMarkerTypes
;
53 import eu
.etaxonomy
.cdm
.vaadin
.permission
.RolesAndPermissions
;
56 // * Can create missing registrations for names which have Extensions of the Type <code>IAPTRegdata.json</code>.
57 //* See https://dev.e-taxonomy.eu/redmine/issues/6621 for further details.
58 //* This feature can be activated by by supplying one of the following jvm command line arguments:
60 //* <li><code>-DregistrationCreate=iapt</code>: create all iapt Registrations if missing</li>
61 //* <li><code>-DregistrationWipeout=iapt</code>: remove all iapt Registrations</li>
62 //* <li><code>-DregistrationWipeout=all</code>: remove all Registrations</li>
64 //* The <code>-DregistrationWipeout</code> commands are executed before the <code>-DregistrationCreate</code> and will not change the name and type designations.
67 * This feature can be activated by by supplying one of the following jvm command line arguments:
69 * <li><code>-DtaxonGraphCreate=true</code>: create taxon graph relations for all names below genus level</li>
72 * @author a.kohlbecker
76 public class RegistrationRequiredDataInserter
extends AbstractDataInserter
{
78 // protected static final String PARAM_NAME_CREATE = "registrationCreate";
80 // protected static final String PARAM_NAME_WIPEOUT = "registrationWipeout";
82 protected static final String TAXON_GRAPH_CREATE
= "taxonGraphCreate";
84 protected static final UUID GROUP_SUBMITTER_UUID
= UUID
.fromString("c468c6a7-b96c-4206-849d-5a825f806d3e");
86 protected static final UUID GROUP_CURATOR_UUID
= UUID
.fromString("135210d3-3db7-4a81-ab36-240444637d45");
88 private static final EnumSet
<CRUD
> CREATE_READ
= EnumSet
.of(CRUD
.CREATE
, CRUD
.READ
);
89 private static final EnumSet
<CRUD
> CREATE_READ_UPDATE_DELETE
= EnumSet
.of(CRUD
.CREATE
, CRUD
.READ
, CRUD
.UPDATE
, CRUD
.DELETE
);
91 private static final Logger logger
= Logger
.getLogger(RegistrationRequiredDataInserter
.class);
93 // private ExtensionType extensionTypeIAPTRegData;
95 Map
<String
, Institution
> instituteMap
= new HashMap
<>();
97 public static boolean commandsExecuted
= false;
99 private CdmRepository repo
;
101 private boolean hasRun
= false;
103 public void setCdmRepository(CdmRepository repo
){
111 public void onApplicationEvent(ContextRefreshedEvent event
) {
117 runAsAuthentication(Role
.ROLE_ADMIN
);
119 insertRequiredData();
120 executeSuppliedCommands();
122 restoreAuthentication();
131 private void insertRequiredData() {
133 TransactionStatus txStatus
= repo
.startTransaction(false);
135 Role roleCuration
= RolesAndPermissions
.ROLE_CURATION
;
136 if(repo
.getGrantedAuthorityService().find(roleCuration
.getUuid()) == null){
137 repo
.getGrantedAuthorityService().saveOrUpdate(roleCuration
.asNewGrantedAuthority());
140 Group groupCurator
= repo
.getGroupService().load(GROUP_CURATOR_UUID
, Arrays
.asList("grantedAuthorities"));
141 if(groupCurator
== null){
142 groupCurator
= Group
.NewInstance();
143 groupCurator
.setUuid(GROUP_CURATOR_UUID
);
144 groupCurator
.setName("Curator");
146 assureGroupHas(groupCurator
, new CdmAuthority(CdmPermissionClass
.REGISTRATION
, CREATE_READ_UPDATE_DELETE
).toString());
147 repo
.getGroupService().saveOrUpdate(groupCurator
);
149 Group groupSubmitter
= repo
.getGroupService().load(GROUP_SUBMITTER_UUID
, Arrays
.asList("grantedAuthorities"));
150 if(groupSubmitter
== null){
151 groupSubmitter
= Group
.NewInstance();
152 groupSubmitter
.setUuid(GROUP_SUBMITTER_UUID
);
153 groupSubmitter
.setName("Submitter");
155 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.TAXONNAME
, CREATE_READ
).toString());
156 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.TEAMORPERSONBASE
, CREATE_READ
).toString());
157 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.REGISTRATION
, CREATE_READ
).toString());
158 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.REFERENCE
, CREATE_READ
).toString());
159 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.SPECIMENOROBSERVATIONBASE
, CREATE_READ
).toString());
160 assureGroupHas(groupSubmitter
, new CdmAuthority(CdmPermissionClass
.COLLECTION
, CREATE_READ
).toString());
161 repo
.getGroupService().saveOrUpdate(groupSubmitter
);
163 TermVocabulary
<DefinedTerm
> kindOfUnitVocabulary
= repo
.getVocabularyService().find(KindOfUnitTerms
.KIND_OF_UNIT_VOCABULARY().getUuid());
164 if(repo
.getVocabularyService().find(KindOfUnitTerms
.KIND_OF_UNIT_VOCABULARY().getUuid()) == null){
165 kindOfUnitVocabulary
= repo
.getVocabularyService().save(KindOfUnitTerms
.KIND_OF_UNIT_VOCABULARY());
168 DefinedTermBase kouSpecimen
= repo
.getTermService().find(KindOfUnitTerms
.SPECIMEN().getUuid());
169 DefinedTermBase kouImage
= repo
.getTermService().find(KindOfUnitTerms
.PUBLISHED_IMAGE().getUuid());
170 DefinedTermBase kouUnpublishedImage
= repo
.getTermService().find(KindOfUnitTerms
.UNPUBLISHED_IMAGE().getUuid());
171 DefinedTermBase kouCulture
= repo
.getTermService().find(KindOfUnitTerms
.CULTURE_METABOLIC_INACTIVE().getUuid());
173 if(kouSpecimen
== null){
174 kouSpecimen
= repo
.getTermService().save(KindOfUnitTerms
.SPECIMEN());
176 if(kouImage
== null){
177 kouImage
= repo
.getTermService().save(KindOfUnitTerms
.PUBLISHED_IMAGE());
179 if(kouUnpublishedImage
== null){
180 kouUnpublishedImage
= repo
.getTermService().save(KindOfUnitTerms
.UNPUBLISHED_IMAGE());
182 if(kouCulture
== null){
183 kouCulture
= repo
.getTermService().save(KindOfUnitTerms
.CULTURE_METABOLIC_INACTIVE());
186 Set
<DefinedTerm
> termInVocab
= kindOfUnitVocabulary
.getTerms();
187 List
<DefinedTermBase
> kouTerms
= Arrays
.asList(kouCulture
, kouImage
, kouSpecimen
, kouUnpublishedImage
);
189 for(DefinedTermBase t
: kouTerms
){
190 if(!termInVocab
.contains(t
)){
191 kindOfUnitVocabulary
.addTerm((DefinedTerm
)t
);
195 DefinedTermBase incorrectName
= repo
.getTermService().find(RegistrationMarkerTypes
.INCORRECT_NAME().getUuid());
196 if(incorrectName
== null){
197 incorrectName
= repo
.getTermService().save(RegistrationMarkerTypes
.INCORRECT_NAME());
198 MarkerType
.COMPLETE().getVocabulary().addTerm((MarkerType
) incorrectName
);
201 repo
.commitTransaction(txStatus
);
205 private void assureGroupHas(Group group
, String authorityString
){
206 boolean authorityExists
= false;
208 for(GrantedAuthority ga
: group
.getGrantedAuthorities()){
209 if((authorityExists
= ga
.getAuthority().equals(authorityString
)) == true){
213 if(!authorityExists
){
214 group
.addGrantedAuthority(findGrantedAuthority(authorityString
));
218 private GrantedAuthorityImpl
findGrantedAuthority(String authorityString
){
219 GrantedAuthorityImpl ga
= null;
221 ga
= repo
.getGrantedAuthorityService().findAuthorityString(authorityString
);
222 } catch (AuthenticationCredentialsNotFoundException e
){
226 ga
= GrantedAuthorityImpl
.NewInstance(authorityString
);
227 repo
.getGrantedAuthorityService().save(ga
);
235 private void executeSuppliedCommands() {
237 if(commandsExecuted
){
239 // a second run could take place during initialization of the web context
242 commandsExecuted
= true;
244 String taxonGraphCreate
= System
.getProperty(TAXON_GRAPH_CREATE
);
246 if(taxonGraphCreate
!= null){
247 AbstractHibernateTaxonGraphProcessor processor
= new AbstractHibernateTaxonGraphProcessor() {
250 public Session
getSession() {
251 return repo
.getSession();
254 logger
.setLevel(Level
.DEBUG
);
255 int chunksize
= 1000;
257 TransactionStatus tx
;
258 Pager
<Taxon
> taxonPage
;
259 List
<TaxonBase
> taxa
= new ArrayList
<>();
260 logger
.debug("======= fixing sec refrences =========");
262 tx
= repo
.startTransaction(false);
263 taxonPage
= repo
.getTaxonService().page(Taxon
.class, chunksize
, pageIndex
++, null, null);
264 if(taxonPage
.getRecords().size() == 0){
265 repo
.commitTransaction(tx
);
268 for(Taxon taxon
: taxonPage
.getRecords()){
269 taxon
.setSec(processor
.secReference());
270 repo
.getTaxonService().saveOrUpdate(taxon
);
272 repo
.commitTransaction(tx
);
275 logger
.debug("======= creating taxon graph =========");
277 Pager
<TaxonName
> page
;
279 tx
= repo
.startTransaction(false);
280 page
= repo
.getNameService().page(null, chunksize
, pageIndex
++, null, null);
281 if(page
.getRecords().size() == 0){
282 repo
.commitTransaction(tx
);
285 logger
.debug(TAXON_GRAPH_CREATE
+ ": chunk " + pageIndex
+ "/" + Math
.ceil(page
.getCount() / chunksize
));
286 taxa
= new ArrayList
<>();
288 for(TaxonName name
: page
.getRecords()){
289 if(name
.getRank() != null && name
.getRank().isLower(Rank
.GENUS())){
290 NomenclaturalStatusType illegitimType
= findILegitimateStatusType(name
);
291 if(illegitimType
== null){
294 logger
.debug("Processing name: " + name
.getTitleCache() + " [" + name
.getRank().getLabel() + "]");
295 taxon
= processor
.assureSingleTaxon(name
);
296 processor
.updateEdges(taxon
);
298 } catch (TaxonGraphException e
) {
299 logger
.error(e
.getMessage());
302 logger
.debug("Skipping illegitimate name: " + name
.getTitleCache() + " " + illegitimType
.getLabel() + " [" + name
.getRank().getLabel() + "]");
305 logger
.debug("Skipping name: " + name
.getTitleCache() + " [" + (name
.getRank() != null ? name
.getRank().getLabel() : "NULL") + "]");
308 repo
.getTaxonService().saveOrUpdate(taxa
);
309 repo
.commitTransaction(tx
);
313 // String wipeoutCmd = System.getProperty(PARAM_NAME_WIPEOUT);
314 // String createCmd = System.getProperty(PARAM_NAME_CREATE);
316 // // ============ DELETE
317 // if(wipeoutCmd != null && wipeoutCmd.matches("iapt|all")){
319 // boolean onlyIapt = wipeoutCmd.equals("iapt");
320 // Set<UUID> deleteCandidates = new HashSet<UUID>();
322 // TransactionStatus tx = repo.startTransaction(true);
323 // List<Registration> allRegs = repo.getRegistrationService().list(null, null, null, null, null);
324 // for(Registration reg : allRegs){
327 // @SuppressWarnings("unchecked")
328 // Set<String> extensions = reg.getName().getExtensions(getExtensionTypeIAPTRegData());
329 // if(reg.getUuid() != null){
330 // deleteCandidates.add(reg.getUuid());
332 // } catch(NullPointerException e){
336 // if(reg.getUuid() != null){
337 // deleteCandidates.add(reg.getUuid());
341 // repo.commitTransaction(tx);
342 // if(!deleteCandidates.isEmpty()){
344 // repo.getRegistrationService().delete(deleteCandidates);
345 // } catch (Exception e) {
346 // // MySQLIntegrityConstraintViolationException happens here every second run !!!
352 // // ============ CREATE
353 // int pageIndex = 0;
354 // if(createCmd != null && createCmd.equals("iapt")){
356 // DateTimeFormatter dateFormat1 = org.joda.time.format.DateTimeFormat.forPattern("dd.MM.yy").withPivotYear(1950);
357 // DateTimeFormatter dateFormat2 = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").withPivotYear(1950);
359 // TransactionStatus tx = repo.startTransaction(false);
361 // Pager<TaxonName> pager = repo.getNameService().page(null, 1000, pageIndex, null, null);
362 // if(pager.getRecords().isEmpty()){
365 // List<Registration> newRegs = new ArrayList<>(pager.getRecords().size());
366 // for(TaxonName name : pager.getRecords()){
370 // Set<String> extensionValues = name.getExtensions(getExtensionTypeIAPTRegData());
372 // // there is for sure only one
373 // if(extensionValues.isEmpty()){
377 // logger.debug("IAPT Registration for " + name.getTitleCache() + " ...");
379 // String iaptJson = extensionValues.iterator().next();
382 // IAPTRegData iaptData = new ObjectMapper().readValue(iaptJson, IAPTRegData.class);
384 // if(iaptData.getRegId() == null){
388 // DateTime regDate = null;
389 // if(iaptData.getDate() != null){
390 // DateTimeFormatter dateFormat;
391 // if(iaptData.getDate().matches("\\d{4}-\\d{2}-\\d{2}")){
392 // dateFormat = dateFormat2;
394 // dateFormat = dateFormat1;
397 // regDate = dateFormat.parseDateTime(iaptData.getDate());
398 // regDate.getYear();
399 // } catch (Exception e) {
400 // logger.error("Error parsing date : " + iaptData.getDate(), e);
405 // Registration reg = Registration.NewInstance();
406 // reg.setStatus(RegistrationStatus.PUBLISHED);
407 // reg.setIdentifier("http://phycobank.org/" + iaptData.getRegId());
408 // reg.setSpecificIdentifier(iaptData.getRegId().toString());
409 // reg.setInstitution(getInstitution(iaptData.getOffice()));
411 // boolean isPhycobankID = Integer.valueOf(reg.getSpecificIdentifier()) >= 100000;
413 // Partial youngestDate = null;
414 // Reference youngestPub = null;
416 // // find youngest publication
419 // // data imported from IAPT does not have typedesignation citations and sometimes no nomref
421 // if(isPhycobankID){
422 // youngestPub = name.getNomenclaturalReference();
423 // youngestDate = partial(youngestPub.getDatePublished());
425 // if(name.getTypeDesignations() != null && !name.getTypeDesignations().isEmpty()){
426 // for(TypeDesignationBase<?> td : name.getTypeDesignations()){
427 // if(td.getCitation() == null){
430 // Partial pubdate = partial(td.getCitation().getDatePublished());
431 // if(pubdate != null){
434 // if(youngestDate== null || earlierThanOther(youngestDate, pubdate)){
435 // youngestDate = pubdate;
436 // youngestPub = td.getCitation();
438 // } catch (Exception e) {
439 // logger.error("Error comparing " + youngestDate + " with" + pubdate , e);
446 // if((isPhycobankID && youngestPub == name.getNomenclaturalReference()) || !isPhycobankID) {
447 // reg.setName(name);
449 // logger.debug("skipping name published in older referece");
451 // if(name.getTypeDesignations() != null && !name.getTypeDesignations().isEmpty()){
452 // // do not add the collection directly to avoid "Found shared references to a collection" problem
453 // Set<TypeDesignationBase> typeDesignations = new HashSet<>(name.getTypeDesignations().size());
454 // for(TypeDesignationBase<?> td : name.getTypeDesignations()){
455 // if(td.getCitation() == null && isPhycobankID){
456 // logger.error("Missing TypeDesignation Citation in Phycobank data");
459 // if((isPhycobankID && youngestPub == td.getCitation()) || !isPhycobankID){
460 // typeDesignations.add(td);
462 // logger.debug("skipping typedesignation published in older reference");
465 // reg.setTypeDesignations(typeDesignations);
467 // reg.setRegistrationDate(regDate);
470 // } catch (JsonParseException e) {
471 // logger.error("Error parsing IAPTRegData from extension", e);
472 // } catch (JsonMappingException e) {
473 // logger.error("Error mapping json from extension to IAPTRegData", e);
474 // } catch (IOException e) {
479 // repo.getRegistrationService().save(newRegs);
481 // logger.debug("Registrations saved");
484 // repo.commitTransaction(tx);
489 private NomenclaturalStatusType
findILegitimateStatusType(TaxonName name
){
490 for(NomenclaturalStatus status
: name
.getStatus()){
491 if(status
.getType() != null && !status
.getType().isLegitimateType()){
492 return status
.getType();
500 // * @param youngestDate
504 // private boolean earlierThanOther(Partial basePartial, Partial other) {
506 // if(basePartial == null || basePartial.getValues().length == 0){
509 // if(other == null || other.getValues().length == 0){
512 // if(basePartial.size() == other.size()) {
513 // return basePartial.compareTo(other) < 0;
515 // basePartial = basePartial.without(DateTimeFieldType.dayOfMonth());
516 // other = other.without(DateTimeFieldType.dayOfMonth());
517 // if(basePartial.size() == other.size()) {
518 // return basePartial.compareTo(other) < 0;
520 // basePartial = basePartial.without(DateTimeFieldType.monthOfYear());
521 // other = other.without(DateTimeFieldType.monthOfYear());
522 // return basePartial.compareTo(other) < 0;
528 // * @param datePublished
531 // private Partial partial(TimePeriod datePublished) {
532 // if(datePublished != null){
533 // if(datePublished.getEnd() != null){
534 // return datePublished.getEnd();
536 // return datePublished.getStart();
547 // private Institution getInstitution(String office) {
548 // Institution institution;
549 // if(instituteMap.containsKey(office)){
550 // institution = instituteMap.get(office);
553 // Pager<Institution> pager = repo.getAgentService().findByTitleWithRestrictions(Institution.class, office, MatchMode.EXACT, null, null, null, null, null);
554 // ) if(!pager.getRecords().isEmpty()){
555 // institution = pager.getRecords().get(0);
557 // Institution institute = (Institution) repo.getAgentService().save(Institution.NewNamedInstance(office));
558 // institution = institute;
560 // instituteMap.put(office, institution);
562 // return institution;
566 // private ExtensionType getExtensionTypeIAPTRegData() {
567 // if(extensionTypeIAPTRegData == null){
568 // extensionTypeIAPTRegData = (ExtensionType) repo.getTermService().load(UUID.fromString("9be1bfe3-6ba0-4560-af15-86971ab96e09"));
570 // return extensionTypeIAPTRegData;