Project

General

Profile

Download (11.8 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.io.distribution.excelupdate;
10

    
11
import java.util.HashMap;
12
import java.util.HashSet;
13
import java.util.Iterator;
14
import java.util.Map;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import org.apache.logging.log4j.LogManager;
19
import org.apache.logging.log4j.Logger;
20
import org.springframework.stereotype.Component;
21

    
22
import eu.etaxonomy.cdm.common.CdmUtils;
23
import eu.etaxonomy.cdm.io.common.ImportResult;
24
import eu.etaxonomy.cdm.io.excel.common.ExcelImportBase;
25
import eu.etaxonomy.cdm.io.excel.common.ExcelRowBase;
26
import eu.etaxonomy.cdm.model.common.CdmBase;
27
import eu.etaxonomy.cdm.model.description.DescriptionBase;
28
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
29
import eu.etaxonomy.cdm.model.description.Distribution;
30
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
31
import eu.etaxonomy.cdm.model.description.TaxonDescription;
32
import eu.etaxonomy.cdm.model.location.NamedArea;
33
import eu.etaxonomy.cdm.model.taxon.Taxon;
34
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
35
import eu.etaxonomy.cdm.model.term.TermVocabulary;
36

    
37
/**
38
 * This Import class updates existing distributions with the new state
39
 * described in the Excel file. It requires that the data was exported
40
 * before in the defined format.
41
 *
42
 * TODO where is the export to be found?
43
 *
44
 * This class is initiated by #6524
45
 *
46
 * @author a.mueller
47
 * @since 04.04.2017
48
 */
49
@Component
50
public class ExcelDistributionUpdate
51
            extends ExcelImportBase<ExcelDistributionUpdateState, ExcelDistributionUpdateConfigurator, ExcelRowBase>{
52

    
53
    private static final long serialVersionUID = 621338661492857764L;
54
    private static final Logger logger = LogManager.getLogger(ExcelDistributionUpdate.class);
55

    
56
    private static final String AREA_MAP = "AreaMap";
57

    
58
    @Override
59
    protected void analyzeRecord(Map<String, String> record, ExcelDistributionUpdateState state) {
60
        // nothing to do
61
    }
62

    
63
    @Override
64
    protected void firstPass(ExcelDistributionUpdateState state) {
65
        Map<String, String> record = state.getOriginalRecord();
66
        String line = state.getCurrentLine() + ": ";
67
        String taxonUuid = getValue(record, "taxon_uuid");
68
        String taxonName = getValue(record, "Taxonname");
69

    
70
        if ("taxon_uuid".equals(taxonUuid)){
71
            return;
72
        }
73
        UUID uuidTaxon = UUID.fromString(taxonUuid);
74
        Taxon taxon = (Taxon)getTaxonService().find(uuidTaxon);
75
        if (taxon == null){
76
            String message = line + "Taxon for uuid not found: " +  uuidTaxon;
77
            state.getResult().addError(message);
78
        }else{
79
            try {
80
                handleAreasForTaxon(state, taxon, record, line);
81
            } catch (Exception e) {
82
                String message = line + "An unexpected error occurred when handling %s (uuid: %s)";
83
                message = String.format(message, taxonName, taxonUuid);
84
                state.getResult().addError(message);
85
                state.getResult().addException(e);
86
            }
87
        }
88
    }
89

    
90
    private void handleAreasForTaxon(ExcelDistributionUpdateState state, Taxon taxon, Map<String, String> record,
91
            String line) {
92
        ImportResult result = state.getResult();
93
        Map<NamedArea, Set<Distribution>> existingDistributions = getExistingDistributions(state, taxon, line);
94
        Map<NamedArea, Distribution> newDistributions = getNewDistributions(state, record, line);
95
        TaxonDescription newDescription = TaxonDescription.NewInstance();
96
        newDescription.addImportSource(null, null, state.getConfig().getSourceReference(), "row " + state.getCurrentLine());
97
        newDescription.setTitleCache("Updated distributions for " + getTaxonLabel(taxon), true);
98
        Set<TaxonDescription> oldReducedDescriptions = new HashSet<>();
99
        for (NamedArea area : newDistributions.keySet()){
100
            Set<Distribution> existingDistrForArea = existingDistributions.get(area);
101
            boolean hasChange = false;
102
            Distribution newDistribution = newDistributions.get(area);
103
            if (existingDistrForArea == null || existingDistrForArea.isEmpty()){
104
                if (newDistribution != null){
105
                    //new distribution exists, old distribution did not exist
106
                    hasChange = true;
107
                }
108
            }else{
109
                for (Distribution existingDistr : existingDistrForArea){
110
                    if (!isEqualDistribution(existingDistr, newDistribution)){
111
                        //distribution changed or deleted
112
                        if (state.getConfig().isCreateNewDistribution() || newDistribution == null ){
113
                            DescriptionBase<?> inDescription = existingDistr.getInDescription();
114
                            inDescription.removeElement(existingDistr);
115
                            result.addDeletedRecord(existingDistr);
116
                            hasChange = true;
117
                            oldReducedDescriptions.add(CdmBase.deproxy(inDescription, TaxonDescription.class));
118
                        }else{
119
                            existingDistr.setStatus(newDistribution.getStatus());
120
                            result.addUpdatedRecord(existingDistr);
121
                            existingDistr.addImportSource(null, null, state.getConfig().getSourceReference(), "row "+state.getCurrentLine());
122
                        }
123
                    }else{
124
    //                    addSource? => not if nothing changed
125
                    }
126
                }
127
            }
128
            if (hasChange && newDistribution != null){
129
                newDescription.addElement(newDistribution);
130
                result.addNewRecord(newDistribution);
131
            }
132
        }
133
        //add new description to taxon if any new element exists
134
        if (!newDescription.getElements().isEmpty()){
135
            taxon.addDescription(newDescription);
136
            result.addNewRecord(newDescription);
137
        }
138
        //remove old empty descriptions (oldReducedDescriptions) if really empty
139
        for (TaxonDescription desc : oldReducedDescriptions){
140
            if (desc.getElements().isEmpty()){
141
                desc.getTaxon().removeDescription(desc);
142
                result.addDeletedRecord(desc);
143
            }
144
        }
145
    }
146

    
147
    private String getTaxonLabel(Taxon taxon) {
148
        return taxon.getName() == null ? taxon.getTitleCache() : taxon.getName().getTitleCache();
149
    }
150

    
151
    private Map<NamedArea, Distribution> getNewDistributions(ExcelDistributionUpdateState state,
152
            Map<String, String> record, String line) {
153

    
154
        Map<NamedArea, Distribution> result = new HashMap<>();
155

    
156
        Set<String> keys = record.keySet();
157
        keys = removeNonAreaKeys(keys);
158
        for (String key : keys){
159
            NamedArea area = getAreaByIdInVoc(state, key, line);
160
            if (area != null){
161
                String statusStr = record.get(key);
162
                PresenceAbsenceTerm status = getStatusByStatusStr(state, statusStr, line);
163
                if (status != null){
164
                    Distribution distribution = Distribution.NewInstance(area, status);
165
                    distribution.addImportSource(null, null, state.getConfig().getSourceReference(), "row " + state.getCurrentLine());
166
                    Distribution previousDistribution = result.put(area, distribution);
167
                    if (previousDistribution != null){
168
                        String message = line + "Multiple distributions exist for same area (" + area.getTitleCache() +  ") in input source";
169
                        logger.warn(message);
170
                        state.getResult().addWarning(message);
171
                    }
172
                }else{
173
                    result.put(area, null);
174
                }
175
            }else{
176
                //??
177
            }
178
        }
179
        return result;
180
    }
181

    
182
    private PresenceAbsenceTerm getStatusByStatusStr(ExcelDistributionUpdateState state, String statusStr, String line) {
183
//        FIXME replace hardcoded;
184
        if ("A".equals(statusStr)) {
185
            return PresenceAbsenceTerm.ABSENT();
186
        }else if ("P".equals(statusStr)) {
187
            return PresenceAbsenceTerm.PRESENT();
188
        }else if ("P?".equals(statusStr)) {
189
            return PresenceAbsenceTerm.PRESENT_DOUBTFULLY();
190
        }else if (isBlank(statusStr)){
191
            return null;
192
        }else{
193
            String message = line + "Status string not recognized: " +  statusStr +". Status not imported.";
194
            logger.warn(message);
195
            state.getResult().addWarning(message);
196
        }
197

    
198
        return null;
199
    }
200

    
201
    private NamedArea getAreaByIdInVoc(ExcelDistributionUpdateState state, String id, String line) {
202
        //TODO remember in state
203
        Map<String, NamedArea> areaMap = (Map<String, NamedArea>)state.getStatusItem(AREA_MAP);
204
        if (areaMap == null){
205
            areaMap = createAreaMap(state);
206
            state.putStatusItem(AREA_MAP, areaMap);
207
        }
208
        NamedArea area = areaMap.get(id);
209
        return area;
210
    }
211

    
212
    private Map<String, NamedArea> createAreaMap(ExcelDistributionUpdateState state) {
213
        Map<String, NamedArea> result = new HashMap<>();
214
        TermVocabulary<?> voc = getVocabularyService().find(state.getConfig().getAreaVocabularyUuid());
215
        //TODO handle null
216
        for (DefinedTermBase<?> obj : voc.getTerms()){
217
            //TODO handle exception
218
            NamedArea area = CdmBase.deproxy(obj, NamedArea.class);
219
            String key = area.getIdInVocabulary();
220
            result.put(key, area);
221
        }
222
        return result;
223
    }
224

    
225
    private Set<String> removeNonAreaKeys(Set<String> keys) {
226
        Iterator<String> it = keys.iterator();
227
        while (it.hasNext()){
228
            if (it.next().matches("(Family|Taxonname|taxon_uuid)")){
229
                it.remove();
230
            }
231
        }
232
        return keys;
233
    }
234

    
235
    private boolean isEqualDistribution(Distribution existingDistribution, Distribution newDistribution) {
236
        if (existingDistribution == null || newDistribution == null){
237
            return existingDistribution == newDistribution;
238
        }
239
        if (existingDistribution.getArea().equals(newDistribution.getArea())){
240
            if (CdmUtils.nullSafeEqual(existingDistribution.getStatus(), newDistribution.getStatus())){
241
                return true;
242
            }
243
        }
244
        return false;
245
    }
246

    
247
    private Map<NamedArea, Set<Distribution>> getExistingDistributions(
248
            ExcelDistributionUpdateState state, Taxon taxon,
249
            String line) {
250
        Map<NamedArea, Set<Distribution>> result = new HashMap<>();
251
        //TODO better use service layer call to return only distributions, this might be necessary if the list of description elements is large
252
        for (TaxonDescription desc : taxon.getDescriptions()){
253
            for (DescriptionElementBase descElem : desc.getElements()){
254
                if (descElem.isInstanceOf(Distribution.class)){
255
                    Distribution distribution =  CdmBase.deproxy(descElem, Distribution.class);
256
                    NamedArea area = distribution.getArea();
257
                    Set<Distribution> set = result.get(area);
258
                    if (set == null){
259
                        set = new HashSet<>();
260
                        result.put(area, set);
261
                    }
262
                    set.add(distribution);
263
                }
264
            }
265
        }
266
        return result;
267
    }
268

    
269
    @Override
270
    protected void secondPass(ExcelDistributionUpdateState state) {
271
        // nothing to do
272
    }
273

    
274
    @Override
275
    protected boolean isIgnore(ExcelDistributionUpdateState state) {
276
        return false;
277
    }
278

    
279
    @Override
280
    protected boolean requiresNomenclaturalCode() {
281
        return false;
282
    }
283
}
(1-1/3)