package org.activityinfo.server.command.handler;
/*
* #%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.api.services.storage.Storage;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import org.activityinfo.model.legacy.KeyGenerator;
import org.activityinfo.legacy.shared.command.Month;
import org.activityinfo.legacy.shared.command.UpdateMonthlyReports;
import org.activityinfo.legacy.shared.command.result.CommandResult;
import org.activityinfo.legacy.shared.command.result.VoidResult;
import org.activityinfo.legacy.shared.exception.CommandException;
import org.activityinfo.legacy.shared.exception.IllegalAccessCommandException;
import org.activityinfo.legacy.shared.model.IndicatorDTO;
import org.activityinfo.server.database.hibernate.entity.*;
import org.activityinfo.server.event.sitehistory.ChangeType;
import org.activityinfo.server.event.sitehistory.SiteHistoryProcessor;
import javax.persistence.EntityManager;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import static org.activityinfo.legacy.shared.model.IndicatorDTO.getPropertyName;
/**
* @author Alex Bertram
* @see org.activityinfo.legacy.shared.command.UpdateMonthlyReports
*/
public class UpdateMonthlyReportsHandler implements CommandHandler<UpdateMonthlyReports> {
private final EntityManager em;
private final KeyGenerator keyGenerator;
private final SiteHistoryProcessor siteHistoryProcessor;
private final PermissionOracle permissionOracle;
@Inject
public UpdateMonthlyReportsHandler(EntityManager em,
KeyGenerator keyGenerator,
SiteHistoryProcessor siteHistoryProcessor) {
this.em = em;
this.keyGenerator = keyGenerator;
this.siteHistoryProcessor = siteHistoryProcessor;
this.permissionOracle = new PermissionOracle(em);
}
@Override
public CommandResult execute(UpdateMonthlyReports cmd, User user) throws CommandException {
Site site = em.find(Site.class, cmd.getSiteId());
if (site == null) {
throw new CommandException(cmd, "site " + cmd.getSiteId() + " not found for user " + user.getEmail());
}
if (!permissionOracle.isEditAllowed(site, user)) {
throw new IllegalAccessCommandException("Not authorized to modify sites");
}
Map<Month, ReportingPeriod> periods = Maps.newHashMap();
Map<String, Object> siteHistoryChangeMap = createChangeMap();
for (ReportingPeriod period : site.getReportingPeriods()) {
periods.put(HandlerUtil.monthFromRange(period.getDate1(), period.getDate2()), period);
}
// update tables in consistent order to avoid deadlocks
// First create any new reporting periods needed
for (UpdateMonthlyReports.Change change : cmd.getChanges()) {
if (!periods.containsKey(change.getMonth())) {
ReportingPeriod period = new ReportingPeriod(site);
period.setId(keyGenerator.generateInt());
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, change.getMonth().getYear());
calendar.set(Calendar.MONTH, change.getMonth().getMonth() - 1);
calendar.set(Calendar.DATE, 1);
period.setDate1(calendar.getTime());
calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
period.setDate2(calendar.getTime());
em.persist(period);
periods.put(change.getMonth(), period);
}
}
// Now update indicator values
for (UpdateMonthlyReports.Change change : cmd.getChanges()) {
updateIndicatorValue(em,
periods.get(change.getMonth()),
change.getIndicatorId(),
change.getValue(), false);
siteHistoryChangeMap.put(getPropertyName(change.getIndicatorId(), change.getMonth()), change.getValue());
}
// finally update the timestamp on the site entity so changes get picked up
// by the synchro mechanism
site.setDateEdited(new Date());
siteHistoryProcessor.persistHistory(site, user, ChangeType.UPDATE, siteHistoryChangeMap);
return new VoidResult();
}
public void updateIndicatorValue(EntityManager em,
ReportingPeriod period,
int indicatorId,
Double value,
boolean creating) {
if (value == null && !creating) {
int rowsAffected = em.createQuery(
"delete from IndicatorValue v where v.indicator.id = ?1 and v.reportingPeriod.id = ?2")
.setParameter(1, indicatorId)
.setParameter(2, period.getId())
.executeUpdate();
assert rowsAffected <= 1 : "whoops, deleted too many";
} else if (value != null) {
int rowsAffected = 0;
if (!creating) {
rowsAffected = em.createQuery("update IndicatorValue v set v.value = ?1 where " +
"v.indicator.id = ?2 and " +
"v.reportingPeriod.id = ?3")
.setParameter(1, value)
.setParameter(2, indicatorId)
.setParameter(3, period.getId())
.executeUpdate();
}
if (rowsAffected == 0) {
IndicatorValue iValue = new IndicatorValue(period,
em.getReference(Indicator.class, indicatorId),
value);
em.persist(iValue);
}
}
}
private Map<String, Object> createChangeMap() {
return Maps.newTreeMap(new Comparator<String>() {
@Override
// comparing eg. I345M2009-7, first part as string, part after the dash as number
public int compare(String o1, String o2) {
int result = o1.substring(0, o1.indexOf('-')).compareToIgnoreCase(o2.substring(0, o2.indexOf('-')));
if (result == 0) {
String m1 = o1.substring(o1.indexOf('-') + 1);
String m2 = o2.substring(o2.indexOf('-') + 1);
result = Integer.valueOf(m1).compareTo(Integer.valueOf(m2));
}
return result;
}
});
}
}