Project

General

Profile

Download (18.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
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.
8
*/
9
package eu.etaxonomy.cdm.dataInserter;
10

    
11
import java.io.IOException;
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.EnumSet;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20
import java.util.UUID;
21

    
22
import org.apache.log4j.Logger;
23
import org.joda.time.DateTime;
24
import org.joda.time.Partial;
25
import org.joda.time.format.DateTimeFormatter;
26
import org.springframework.context.event.ContextRefreshedEvent;
27
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
28
import org.springframework.security.core.GrantedAuthority;
29
import org.springframework.transaction.TransactionStatus;
30
import org.springframework.transaction.annotation.Transactional;
31

    
32
import com.fasterxml.jackson.core.JsonParseException;
33
import com.fasterxml.jackson.databind.JsonMappingException;
34
import com.fasterxml.jackson.databind.ObjectMapper;
35

    
36
import eu.etaxonomy.cdm.api.application.AbstractDataInserter;
37
import eu.etaxonomy.cdm.api.application.CdmRepository;
38
import eu.etaxonomy.cdm.api.service.pager.Pager;
39
import eu.etaxonomy.cdm.model.agent.AgentBase;
40
import eu.etaxonomy.cdm.model.agent.Institution;
41
import eu.etaxonomy.cdm.model.common.ExtensionType;
42
import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;
43
import eu.etaxonomy.cdm.model.common.Group;
44
import eu.etaxonomy.cdm.model.common.TimePeriod;
45
import eu.etaxonomy.cdm.model.name.Registration;
46
import eu.etaxonomy.cdm.model.name.RegistrationStatus;
47
import eu.etaxonomy.cdm.model.name.TaxonName;
48
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
49
import eu.etaxonomy.cdm.model.reference.Reference;
50
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
51
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
52
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionClass;
53
import eu.etaxonomy.cdm.persistence.hibernate.permission.Role;
54
import eu.etaxonomy.cdm.persistence.query.MatchMode;
55
import eu.etaxonomy.cdm.vaadin.model.registration.KindOfUnitTerms;
56
import eu.etaxonomy.cdm.vaadin.security.RolesAndPermissions;
57

    
58
/**
59
 *
60
 * Can create missing registrations for names which have Extensions of the Type <code>IAPTRegdata.json</code>.
61
 * See https://dev.e-taxonomy.eu/redmine/issues/6621 for further details.
62
 * This feature can be activated by by supplying one of the following jvm command line arguments:
63
 * <ul>
64
 * <li><code>-DregistrationCreate=iapt</code>: create all iapt Registrations if missing</li>
65
 * <li><code>-DregistrationWipeout=iapt</code>: remove all iapt Registrations</li>
66
 * <li><code>-DregistrationWipeout=all</code>: remove all Registrations</li>
67
 * </ul>
68
 * The <code>-DregistrationWipeout</code> commands are executed before the <code>-DregistrationCreate</code> and will not change the name and type designations.
69
 *
70
 * @author a.kohlbecker
71
 * @since May 9, 2017
72
 *
73
 */
74
public class RegistrationRequiredDataInserter extends AbstractDataInserter {
75

    
76
    protected static final String PARAM_NAME_CREATE = "registrationCreate";
77

    
78
    protected static final String PARAM_NAME_WIPEOUT = "registrationWipeout";
79

    
80
    protected static final UUID GROUP_SUBMITTER_UUID = UUID.fromString("c468c6a7-b96c-4206-849d-5a825f806d3e");
81

    
82
    protected static final UUID GROUP_CURATOR_UUID = UUID.fromString("135210d3-3db7-4a81-ab36-240444637d45");
83

    
84
    private static final EnumSet<CRUD> CREATE_READ = EnumSet.of(CRUD.CREATE, CRUD.READ);
85
    private static final EnumSet<CRUD> CREATE_READ_UPDATE_DELETE = EnumSet.of(CRUD.CREATE, CRUD.READ, CRUD.UPDATE, CRUD.DELETE);
86

    
87
    private static final Logger logger = Logger.getLogger(RegistrationRequiredDataInserter.class);
88

    
89
    private ExtensionType extensionTypeIAPTRegData;
90

    
91
    Map<String, Institution> instituteMap = new HashMap<>();
92

    
93
    public static boolean commandsExecuted = false;
94

    
95
    private CdmRepository repo;
96

    
97
    private boolean hasRun = false;
98

    
99
    public void setCdmRepository(CdmRepository repo){
100
      this.repo = repo;
101
    }
102

    
103

    
104
 // ==================== Registration creation ======================= //
105

    
106
    /**
107
     * {@inheritDoc}
108
     */
109
    @Override
110
    public void onApplicationEvent(ContextRefreshedEvent event) {
111

    
112
        if(hasRun){
113
            return;
114
        }
115

    
116
        runAsAuthentication(Role.ROLE_ADMIN);
117

    
118
        insertRequiredData();
119
        executeSuppliedCommands();
120

    
121
        restoreAuthentication();
122

    
123
        hasRun = true;
124
    }
125

    
126
    /**
127
     *
128
     */
129
    @Transactional
130
    private void insertRequiredData() {
131

    
132
        TransactionStatus txStatus = repo.startTransaction(false);
133

    
134
        Role roleCuration = RolesAndPermissions.ROLE_CURATION;
135
        if(repo.getGrantedAuthorityService().find(roleCuration.getUuid()) == null){
136
            repo.getGrantedAuthorityService().saveOrUpdate(roleCuration.asNewGrantedAuthority());
137
        }
138

    
139
        Group groupCurator = repo.getGroupService().load(GROUP_CURATOR_UUID, Arrays.asList("grantedAuthorities"));
140
        if(groupCurator == null){
141
            groupCurator = Group.NewInstance();
142
            groupCurator.setUuid(GROUP_CURATOR_UUID);
143
            groupCurator.setName("Curator");
144
        }
145
        assureGroupHas(groupCurator, new CdmAuthority(CdmPermissionClass.REGISTRATION, CREATE_READ_UPDATE_DELETE).toString());
146
        repo.getGroupService().saveOrUpdate(groupCurator);
147

    
148
        Group groupSubmitter = repo.getGroupService().load(GROUP_SUBMITTER_UUID, Arrays.asList("grantedAuthorities"));
149
        if(groupSubmitter == null){
150
            groupSubmitter = Group.NewInstance();
151
            groupSubmitter.setUuid(GROUP_SUBMITTER_UUID);
152
            groupSubmitter.setName("Submitter");
153
        }
154
        assureGroupHas(groupSubmitter, new CdmAuthority(CdmPermissionClass.TAXONNAME, CREATE_READ).toString());
155
        assureGroupHas(groupSubmitter, new CdmAuthority(CdmPermissionClass.TEAMORPERSONBASE, CREATE_READ).toString());
156
        assureGroupHas(groupSubmitter, new CdmAuthority(CdmPermissionClass.REGISTRATION, CREATE_READ).toString());
157
        assureGroupHas(groupSubmitter, new CdmAuthority(CdmPermissionClass.SPECIMENOROBSERVATIONBASE, CREATE_READ).toString());
158
        repo.getGroupService().saveOrUpdate(groupSubmitter);
159

    
160
        if(repo.getTermService().find(KindOfUnitTerms.SPECIMEN().getUuid()) == null){
161
            repo.getTermService().save(KindOfUnitTerms.SPECIMEN());
162
        }
163
        if(repo.getTermService().find(KindOfUnitTerms.PUBLISHED_IMAGE().getUuid()) == null){
164
            repo.getTermService().save(KindOfUnitTerms.PUBLISHED_IMAGE());
165
        }
166
        if(repo.getTermService().find(KindOfUnitTerms.UNPUBLISHED_IMAGE().getUuid()) == null){
167
            repo.getTermService().save(KindOfUnitTerms.UNPUBLISHED_IMAGE());
168
        }
169
        if(repo.getTermService().find(KindOfUnitTerms.CULTURE_METABOLIC_INACTIVE().getUuid()) == null){
170
            repo.getTermService().save(KindOfUnitTerms.CULTURE_METABOLIC_INACTIVE());
171
        }
172

    
173
        // --- remove after release 4.12.0 ------------------------------------------------------
174
        // delete old DerivationEventTypes terms which are no longer used, see #7059
175
        // UUID_PUBLISHED_IMAGE = UUID.fromString("b8cba359-4202-4741-8ed8-4f17ae94b3e3");
176
        // UUID UUID_UNPUBLISHED_IMAGE = UUID.fromString("6cd5681f-0918-4ed6-89a8-bda1480dc890");
177
        // UUID UUID_CULTURE_METABOLIC_INACTIVE = UUID.fromString("eaf1c853-ba8d-4c40-aa0a-56beac96b0d2");
178
        for(UUID uuid : new UUID[]{
179
                UUID.fromString("b8cba359-4202-4741-8ed8-4f17ae94b3e3"),
180
                UUID.fromString("6cd5681f-0918-4ed6-89a8-bda1480dc890"),
181
                UUID.fromString("eaf1c853-ba8d-4c40-aa0a-56beac96b0d2")}){
182
            if(repo.getTermService().find(uuid) != null){
183
                repo.getTermService().delete(uuid);
184
            }
185
        }
186
        // --------------------------------------------------------------------------------------
187
        txStatus.flush();
188
        repo.commitTransaction(txStatus);
189

    
190
    }
191

    
192
    private void assureGroupHas(Group group, String authorityString){
193
        boolean authorityExists = false;
194

    
195
        for(GrantedAuthority ga : group.getGrantedAuthorities()){
196
            if((authorityExists = ga.getAuthority().equals(authorityString)) == true){
197
                break;
198
            }
199
        }
200
        if(!authorityExists){
201
            group.addGrantedAuthority(findGrantedAuthority(authorityString));
202
        }
203
    }
204

    
205
    private GrantedAuthorityImpl findGrantedAuthority(String authorityString){
206
        GrantedAuthorityImpl ga = null;
207
        try{
208
            ga = repo.getGrantedAuthorityService().findAuthorityString(authorityString);
209
        } catch (AuthenticationCredentialsNotFoundException e){
210
            e.printStackTrace();
211
        }
212
        if(ga == null){
213
            ga = GrantedAuthorityImpl.NewInstance(authorityString);
214
            repo.getGrantedAuthorityService().save(ga);
215
        }
216
        return ga;
217
    }
218

    
219
    /**
220
     *
221
     */
222

    
223
    private void executeSuppliedCommands() {
224

    
225
        if(commandsExecuted){
226
            // do not run twice
227
            // a second run could take place during initialization of the web context
228
            return;
229
        }
230
        commandsExecuted  = true;
231

    
232
        String wipeoutCmd = System.getProperty(PARAM_NAME_WIPEOUT);
233
        String createCmd = System.getProperty(PARAM_NAME_CREATE);
234

    
235
        // ============ DELETE
236
        if(wipeoutCmd != null && wipeoutCmd.matches("iapt|all")){
237

    
238
            boolean onlyIapt = wipeoutCmd.equals("iapt");
239
            List<UUID> deleteCandidates = new ArrayList<UUID>();
240

    
241
            TransactionStatus tx = repo.startTransaction(true);
242
            List<Registration> allRegs = repo.getRegistrationService().list(null, null, null, null, null);
243
            for(Registration reg : allRegs){
244
                if(onlyIapt){
245
                    try {
246
                        @SuppressWarnings("unchecked")
247
                        Set<String> extensions = reg.getName().getExtensions(getExtensionTypeIAPTRegData());
248
                        deleteCandidates.add(reg.getUuid());
249
                    } catch(NullPointerException e){
250
                        // IGNORE
251
                    }
252
                } else {
253
                    deleteCandidates.add(reg.getUuid());
254
                }
255
            }
256
            repo.commitTransaction(tx);
257
            repo.getRegistrationService().delete(deleteCandidates);
258
        }
259

    
260
        // ============ CREATE
261
        int pageIndex = 0;
262
        if(createCmd != null && createCmd.equals("iapt")){
263

    
264
            DateTimeFormatter dateFormat1 = org.joda.time.format.DateTimeFormat.forPattern("dd.MM.yy").withPivotYear(1950);
265
            DateTimeFormatter dateFormat2 = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").withPivotYear(1950);
266

    
267
            TransactionStatus tx = repo.startTransaction(false);
268
            while(true) {
269
                Pager<TaxonName> pager = repo.getNameService().page(null, 1000, pageIndex, null, null);
270
                if(pager.getRecords().isEmpty()){
271
                    break;
272
                }
273
                List<Registration> newRegs = new ArrayList<>(pager.getRecords().size());
274
                for(TaxonName name : pager.getRecords()){
275

    
276
                    Set<String> extensionValues = name.getExtensions(getExtensionTypeIAPTRegData());
277

    
278
                    // there is for sure only one
279
                    if(extensionValues.isEmpty()){
280
                        continue;
281
                    }
282
                    String iaptJson = extensionValues.iterator().next();
283
                    try {
284

    
285
                        IAPTRegData iaptData = new ObjectMapper().readValue(iaptJson, IAPTRegData.class);
286

    
287
                        if(iaptData.getRegId() == null){
288
                            continue;
289
                        }
290

    
291
                        DateTime regDate = null;
292
                        if(iaptData.getDate() != null){
293
                            DateTimeFormatter dateFormat;
294
                            if(iaptData.getDate().matches("\\d{4}-\\d{2}-\\d{2}")){
295
                                dateFormat = dateFormat2;
296
                            } else {
297
                                dateFormat = dateFormat1;
298
                            }
299
                            try {
300
                                regDate = dateFormat.parseDateTime(iaptData.getDate());
301
                                regDate.getYear();
302
                            } catch (Exception e) {
303
                                logger.error("Error parsing date : " + iaptData.getDate(), e);
304
                                continue;
305
                            }
306
                        }
307

    
308
                        Registration reg = Registration.NewInstance();
309
                        reg.setStatus(RegistrationStatus.PUBLISHED);
310
                        reg.setIdentifier("http://phycobank.org/" + iaptData.getRegId());
311
                        reg.setSpecificIdentifier(iaptData.getRegId().toString());
312
                        reg.setInstitution(getInstitution(iaptData.getOffice()));
313

    
314
                        boolean isPhycobankID = false; // Integer.valueOf(reg.getSpecificIdentifier()) >= 100000;
315

    
316
                        Partial youngestDate = null;
317
                        Reference youngestPub = null;
318

    
319
                        // find youngest publication
320

    
321
                        // NOTE:
322
                        // data imported from IAPT does not have typedesignation citations and sometimes no nomref
323

    
324
                        if(isPhycobankID){
325
                            youngestPub = (Reference) name.getNomenclaturalReference();
326
                            youngestDate = partial(youngestPub.getDatePublished());
327

    
328
                            if(name.getTypeDesignations() != null && !name.getTypeDesignations().isEmpty()){
329
                                for(TypeDesignationBase td : name.getTypeDesignations()){
330
                                    if(td.getCitation() == null){
331
                                        continue;
332
                                    }
333
                                    Partial pubdate = partial(td.getCitation().getDatePublished());
334
                                        if(youngestDate.compareTo(pubdate) < 0){
335
                                            youngestDate = pubdate;
336
                                            youngestPub = td.getCitation();
337
                                        }
338
                                }
339
                            }
340
                        }
341

    
342
                        if((isPhycobankID && youngestPub == name.getNomenclaturalReference()) || !isPhycobankID) {
343
                            reg.setName(name);
344
                        } else {
345
                            logger.debug("skipping name published in older referece");
346
                        }
347
                        if(name.getTypeDesignations() != null && !name.getTypeDesignations().isEmpty()){
348
                            // do not add the collection directly to avoid "Found shared references to a collection" problem
349
                            HashSet<TypeDesignationBase> typeDesignations = new HashSet<>(name.getTypeDesignations().size());
350
                            for(TypeDesignationBase td : name.getTypeDesignations()){
351
                                if(td.getCitation() == null && isPhycobankID){
352
                                    logger.error("Missing TypeDesignation Citation in Phycobank data");
353
                                    continue;
354
                                }
355
                                if((isPhycobankID && youngestPub == td.getCitation()) || !isPhycobankID){
356
                                    typeDesignations.add(td);
357
                                } else {
358
                                    logger.debug("skipping typedesignation published in older reference");
359
                                }
360
                            }
361
                            reg.setTypeDesignations(typeDesignations);
362
                        }
363
                        reg.setRegistrationDate(regDate);
364
                        logger.debug("IAPT Registration for " + name.getTitleCache());
365
                        newRegs.add(reg);
366

    
367
                    } catch (JsonParseException e) {
368
                        logger.error("Error parsing IAPTRegData from extension", e);
369
                    } catch (JsonMappingException e) {
370
                        logger.error("Error mapping json from extension to IAPTRegData", e);
371
                    } catch (IOException e) {
372
                        logger.error(e);
373
                    }
374

    
375
                }
376
                repo.getRegistrationService().save(newRegs);
377
                tx.flush();
378
                logger.debug("Registrations saved");
379
                pageIndex++;
380
            }
381
            repo.commitTransaction(tx);
382
        }
383
    }
384

    
385

    
386
    /**
387
     * @param datePublished
388
     * @return
389
     */
390
    private Partial partial(TimePeriod datePublished) {
391
        if(datePublished != null){
392
            if(datePublished.getEnd() != null){
393
                return datePublished.getEnd();
394
            } else {
395
                return datePublished.getStart();
396
            }
397
        }
398
        return null;
399
    }
400

    
401

    
402
    /**
403
     * @param office
404
     * @return
405
     */
406
    private Institution getInstitution(String office) {
407
        Institution institution;
408
        if(instituteMap.containsKey(office)){
409
            institution = instituteMap.get(office);
410
        } else {
411

    
412
            Pager<AgentBase> pager = repo.getAgentService().findByTitle(Institution.class, office, MatchMode.EXACT, null, null, null, null, null);
413
            if(!pager.getRecords().isEmpty()){
414
                institution =  (Institution) pager.getRecords().get(0);
415
            } else {
416
                Institution institute = (Institution) repo.getAgentService().save(Institution.NewNamedInstance(office));
417
                institution = institute;
418
            }
419
            instituteMap.put(office, institution);
420
        }
421
        return institution;
422
    }
423

    
424

    
425
    private ExtensionType getExtensionTypeIAPTRegData() {
426
        if(extensionTypeIAPTRegData == null){
427
            extensionTypeIAPTRegData = (ExtensionType) repo.getTermService().load(UUID.fromString("9be1bfe3-6ba0-4560-af15-86971ab96e09"));
428
        }
429
        return extensionTypeIAPTRegData;
430
    }
431

    
432

    
433

    
434
}
(2-2/2)