package org.activityinfo.core.shared.importing.strategy;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.activityinfo.core.client.InstanceQuery;
import org.activityinfo.core.client.ResourceLocator;
import org.activityinfo.model.resource.ResourceId;
import org.activityinfo.core.shared.Pair;
import org.activityinfo.core.shared.Projection;
import org.activityinfo.core.shared.criteria.ClassCriteria;
import org.activityinfo.core.shared.criteria.FormClassSet;
import org.activityinfo.model.form.FormInstance;
import org.activityinfo.model.formTree.FieldPath;
import org.activityinfo.model.formTree.FormTree;
import org.activityinfo.core.shared.importing.source.SourceRow;
import org.activityinfo.core.shared.importing.validation.ValidationResult;
import org.activityinfo.promise.Promise;
import org.activityinfo.model.legacy.CuidAdapter;
import java.util.List;
import java.util.Map;
/**
* @author yuriyz on 5/19/14.
*/
public class HierarchyClassImporter implements FieldImporter {
private final FormTree.Node rootField;
private final Map<FieldPath, Integer> referenceFields;
private final List<ColumnAccessor> sourceColumns;
private final List<FieldImporterColumn> fieldImporterColumns;
private final Map<ResourceId, InstanceScoreSource> scoreSources = Maps.newHashMap();
public HierarchyClassImporter(FormTree.Node rootField,
List<ColumnAccessor> sourceColumns,
Map<FieldPath, Integer> referenceFields,
List<FieldImporterColumn> fieldImporterColumns) {
this.rootField = rootField;
this.sourceColumns = sourceColumns;
this.referenceFields = referenceFields;
this.fieldImporterColumns = fieldImporterColumns;
}
@Override
public Promise<Void> prepare(ResourceLocator locator, List<? extends SourceRow> batch) {
final List<Promise<Void>> promises = Lists.newArrayList();
for (final ResourceId range : FormClassSet.of(rootField.getRange()).getElements()) {
InstanceQuery query = new InstanceQuery(Lists.newArrayList(referenceFields.keySet()), new ClassCriteria(range));
final Promise<List<Projection>> promise = locator.query(query);
promise.then(new Function<List<Projection>, Void>() {
@Override
public Void apply(List<Projection> projections) {
scoreSources.put(range, new InstanceScoreSourceBuilder(referenceFields, sourceColumns).build(projections));
return null;
}
});
}
return Promise.waitAll(promises);
}
private FieldImporterColumn getImportedColumn(ColumnAccessor columnAccessor) {
for (FieldImporterColumn c : fieldImporterColumns) {
if (c.getAccessor().equals(columnAccessor)) {
return c;
}
}
return null; // bug?
}
@Override
public void validateInstance(SourceRow row, List<ValidationResult> results) {
for (int i = 0; i < sourceColumns.size(); i++) {
ColumnAccessor columnAccessor = sourceColumns.get(i);
if (!columnAccessor.isMissing(row)) {
FieldImporterColumn importedColumn = getImportedColumn(columnAccessor);
ResourceId targetSiteId = ResourceId.valueOf(importedColumn.getTarget().getSite().asString());
if (targetSiteId.getDomain() == CuidAdapter.ADMIN_LEVEL_DOMAIN) {
final int levelId = CuidAdapter.getBlock(targetSiteId, 0);
// todo : recreation of admin level cuid seems to be error prone, check later !
ResourceId range = CuidAdapter.adminLevelFormClass(levelId);
InstanceScoreSource scoreSource = scoreSources.get(range);
InstanceScorer instanceScorer = new InstanceScorer(scoreSource);
final InstanceScorer.Score score = instanceScorer.score(row);
final int bestMatchIndex = score.getBestMatchIndex();
if (score.getImported()[i] == null) {
results.add(ValidationResult.MISSING);
} else if (bestMatchIndex == -1) {
results.add(ValidationResult.error("No match"));
} else {
String matched = scoreSource.getReferenceValues().get(bestMatchIndex)[i];
final ValidationResult converted = ValidationResult.converted(matched, score.getBestScores()[i]);
converted.setRangeWithInstanceId(new Pair<>(range, scoreSource.getReferenceInstanceIds().get(bestMatchIndex)));
results.add(converted);
}
} else {
throw new UnsupportedOperationException("Not supported");
}
} else {
results.add(ValidationResult.MISSING);
}
}
}
@Override
public boolean updateInstance(SourceRow row, FormInstance instance) {
final List<ValidationResult> validationResults = Lists.newArrayList();
validateInstance(row, validationResults);
final Map<ResourceId, ResourceId> toSave = Maps.newHashMap();
for (ValidationResult result : validationResults) {
if (result.shouldPersist() && result.getRangeWithInstanceId() != null) {
ResourceId range = result.getRangeWithInstanceId().getA();
ResourceId value = toSave.get(range);
if (value == null) {
toSave.put(range, result.getRangeWithInstanceId().getB());
}
}
}
if (!toSave.isEmpty()) {
instance.set(rootField.getFieldId(), Sets.newHashSet(toSave.values()));
return true;
}
return false;
}
@Override
public List<FieldImporterColumn> getColumns() {
return fieldImporterColumns;
}
}