package org.activityinfo.legacy.shared.impl;
/*
* #%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.bedatadriven.rebar.sql.client.SqlResultCallback;
import com.bedatadriven.rebar.sql.client.SqlResultSet;
import com.bedatadriven.rebar.sql.client.SqlResultSetRow;
import com.bedatadriven.rebar.sql.client.SqlTransaction;
import com.bedatadriven.rebar.sql.client.query.SqlQuery;
import com.bedatadriven.rebar.sql.client.util.RowHandler;
import com.google.common.base.Functions;
import com.google.common.collect.Lists;
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.activityinfo.legacy.shared.Log;
import org.activityinfo.legacy.shared.command.GetSchema;
import org.activityinfo.legacy.shared.model.*;
import org.activityinfo.legacy.shared.reports.util.mapping.Extents;
import org.activityinfo.promise.Promise;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GetSchemaHandler implements CommandHandlerAsync<GetSchema, SchemaDTO> {
@Override
public void execute(GetSchema command, ExecutionContext context, AsyncCallback<SchemaDTO> callback) {
new SchemaBuilder().build(context, callback);
}
private class SchemaBuilder {
private final List<UserDatabaseDTO> databaseList = new ArrayList<UserDatabaseDTO>();
private final List<CountryDTO> countryList = new ArrayList<CountryDTO>();
private final Map<Integer, UserDatabaseDTO> databaseMap = new HashMap<Integer, UserDatabaseDTO>();
private final Map<Integer, CountryDTO> countries = new HashMap<Integer, CountryDTO>();
private final Map<Integer, PartnerDTO> partners = new HashMap<Integer, PartnerDTO>();
private final Map<Integer, ActivityDTO> activities = new HashMap<>();
private final Map<Integer, AttributeGroupDTO> attributeGroups = new HashMap<Integer, AttributeGroupDTO>();
private final Map<Integer, ProjectDTO> projects = new HashMap<Integer, ProjectDTO>();
private final Map<Integer, LocationTypeDTO> locationTypes = new HashMap<>();
private SqlTransaction tx;
private ExecutionContext context;
public Promise<Void> loadCountries() {
return execute(SqlQuery.select()
.appendColumn("CountryId", "id")
.appendColumn("Name", "name")
.appendColumn("X1", "x1")
.appendColumn("y1", "y1")
.appendColumn("x2", "x2")
.appendColumn("y2", "y2")
.from("country"),
new RowHandler() {
@Override
public void handleRow(SqlResultSetRow rs) {
CountryDTO country = new CountryDTO();
country.setId(rs.getInt("id"));
country.setName(rs.getString("name"));
Extents bounds = new Extents(rs.getDouble("y1"),
rs.getDouble("y2"),
rs.getDouble("x1"),
rs.getDouble("x2"));
country.setBounds(bounds);
countries.put(country.getId(), country);
countryList.add(country);
}
});
}
public Promise<Void> loadLocationTypes() {
SqlQuery query = SqlQuery.select("locationTypeId",
"name",
"boundAdminLevelId",
"countryId",
"workflowId",
"databaseId").from("locationtype");
if (context.isRemote()) {
query.where("DateDeleted IS NULL");
}
return execute(query, new RowHandler() {
@Override
public void handleRow(SqlResultSetRow row) {
LocationTypeDTO type = new LocationTypeDTO();
type.setId(row.getInt("locationTypeId"));
type.setName(row.getString("name"));
type.setWorkflowId(row.getString("workflowId"));
if (!row.isNull("databaseId")) {
type.setDatabaseId(row.getInt("databaseId"));
}
if (!row.isNull("boundAdminLevelId")) {
type.setBoundAdminLevelId(row.getInt("boundAdminLevelId"));
}
int countryId = row.getInt("countryId");
CountryDTO country = countries.get(countryId);
country.getLocationTypes().add(type);
type.setAdminLevels(levelsForLocationType(country, type));
type.setCountryBounds(country.getBounds());
locationTypes.put(type.getId(), type);
}
});
}
private List<AdminLevelDTO> levelsForLocationType(CountryDTO country, LocationTypeDTO type) {
if (type.isAdminLevel()) {
// if this activity is bound to an administrative
// level, then we need only as far down as this goes
return country.getAdminLevelAncestors(type.getBoundAdminLevelId());
} else if (type.isNationwide()) {
return Lists.newArrayList();
} else {
// all admin levels
return country.getAdminLevels();
}
}
public Promise<Void> loadAdminLevels() {
return execute(SqlQuery.select("adminLevelId", "name", "parentId", "countryId")
.from("adminlevel")
.whereTrue("deleted=0"),
new RowHandler() {
@Override
public void handleRow(SqlResultSetRow row) {
AdminLevelDTO level = new AdminLevelDTO();
level.setId(row.getInt("adminLevelId"));
level.setName(row.getString("name"));
level.setCountryId(row.getInt("countryId"));
if (!row.isNull("parentId")) {
level.setParentLevelId(row.getInt("parentId"));
}
countries.get(level.getCountryId()).getAdminLevels().add(level);
}
});
}
public Promise<Void> loadDatabases() {
final Promise<Void> promise = new Promise<>();
SqlQuery query = SqlQuery.select("d.DatabaseId")
.appendColumn("d.Name")
.appendColumn("d.FullName")
.appendColumn("d.OwnerUserId")
.appendColumn("d.CountryId")
.appendColumn("o.Name", "OwnerName")
.appendColumn("o.Email", "OwnerEmail")
.appendColumn("p.AllowViewAll", "allowViewAll")
.appendColumn("p.AllowEdit", "allowEdit")
.appendColumn("p.AllowEditAll", "allowEditAll")
.appendColumn("p.AllowManageUsers", "allowManageUsers")
.appendColumn("p.AllowManageAllUsers", "allowManageAllUsers")
.appendColumn("p.AllowDesign", "allowDesign")
.appendColumn("p.PartnerId", "partnerId")
.from("userdatabase d")
.leftJoin(SqlQuery.selectAll()
.from("userpermission")
.where("userpermission.UserId")
.equalTo(context.getUser().getId()), "p")
.on("p.DatabaseId = d.DatabaseId")
.leftJoin("userlogin o")
.on("d.OwnerUserId = o.UserId")
.where("d.DateDeleted")
.isNull()
.orderBy("d.Name");
// this is quite hackesh. we ultimately need to split up GetSchema()
// into
// GetDatabases() and GetDatabaseSchema() so that the client has
// more fine-grained
// control over which databases are visible, which will be important
// as the number
// of public databases grow
if (context.getUser().isAnonymous()) {
query.whereTrue("(d.DatabaseId in (select pa.DatabaseId from activity pa where pa.published>0))");
} else {
query.whereTrue("(o.userId = ? or p.AllowView = 1)").appendParameter(context.getUser().getId());
}
query.execute(tx, new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
for (SqlResultSetRow row : results.getRows()) {
UserDatabaseDTO db = new UserDatabaseDTO();
db.setId(row.getInt("DatabaseId"));
db.setName(row.getString("Name"));
db.setFullName(row.getString("FullName"));
db.setAmOwner(row.getInt("OwnerUserId") == context.getUser().getId());
db.setCountry(countries.get(row.getInt("CountryId")));
db.setOwnerName(row.getString("OwnerName"));
db.setOwnerEmail(row.getString("OwnerEmail"));
if (db.getAmOwner()) {
db.setViewAllAllowed(true);
db.setEditAllowed(true);
db.setEditAllAllowed(true);
db.setManageUsersAllowed(true);
db.setManageAllUsersAllowed(true);
db.setDesignAllowed(true);
} else if (row.isNull("allowViewAll")) {
// when other users see public databases
// they will not have a UserPermission record
db.setViewAllAllowed(true);
db.setEditAllowed(false);
db.setEditAllAllowed(false);
db.setManageUsersAllowed(false);
db.setManageAllUsersAllowed(false);
db.setDesignAllowed(false);
} else {
db.setViewAllAllowed(row.getBoolean("allowViewAll"));
db.setEditAllowed(row.getBoolean("allowEdit"));
db.setEditAllAllowed(row.getBoolean("allowEditAll"));
db.setManageUsersAllowed(row.getBoolean("allowManageUsers"));
db.setManageAllUsersAllowed(row.getBoolean("allowManageAllUsers"));
db.setDesignAllowed(row.getBoolean("allowDesign"));
db.setMyPartnerId(row.getInt("partnerId"));
}
// todo fix query !!! sometimes it returns duplicates
if (!databaseMap.containsKey(db.getId())) {
databaseMap.put(db.getId(), db);
databaseList.add(db);
} else {
continue;
}
}
if (databaseMap.isEmpty()) {
promise.resolve(null);
} else {
Promise.waitAll(
joinPartnersToDatabases(),
loadProjects(),
loadActivities(),
// loadIndicators(),
// loadAttributeGroups(),
// loadAttributes(),
// joinAttributesToActivities(),
loadLockedPeriods())
.then(promise);
}
}
});
return promise;
}
protected Promise<Void> loadProjects() {
final Promise<Void> promise = new Promise<>();
SqlQuery.select("name", "projectId", "description", "databaseId")
.from("project")
.where("databaseId").in(databaseMap.keySet())
.where("dateDeleted").isNull()
.execute(tx, new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
for (SqlResultSetRow row : results.getRows()) {
ProjectDTO project = new ProjectDTO();
project.setName(row.getString("name"));
project.setId(row.getInt("projectId"));
project.setDescription(row.getString("description"));
int databaseId = row.getInt("databaseId");
UserDatabaseDTO database = databaseMap.get(databaseId);
database.getProjects().add(project);
project.setUserDatabase(database);
projects.put(project.getId(), project);
}
promise.resolve(null);
}
});
return promise;
}
protected Promise<Void> loadLockedPeriods() {
final Promise<Void> promise = new Promise<>();
SqlQuery.select("fromDate",
"toDate",
"enabled",
"name",
"lockedPeriodId",
"userDatabaseId",
"activityId",
"projectId").from("lockedperiod")
.execute(tx, new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
for (SqlResultSetRow row : results.getRows()) {
LockedPeriodDTO lockedPeriod = new LockedPeriodDTO();
lockedPeriod.setFromDate(row.getDate("fromDate"));
lockedPeriod.setToDate(row.getDate("toDate"));
lockedPeriod.setEnabled(row.getBoolean("enabled"));
lockedPeriod.setName(row.getString("name"));
lockedPeriod.setId(row.getInt("lockedPeriodId"));
boolean parentFound = false;
if (!row.isNull("activityId")) {
Integer activityId = row.getInt("activityId");
ActivityDTO activity = activities.get(activityId);
if (activity != null) { // activities can be
// deleted...
activity.getLockedPeriods().add(lockedPeriod);
lockedPeriod.setParent(activity);
}
parentFound = true;
}
if (!row.isNull("userDatabaseId")) {
Integer databaseId = row.getInt("userDatabaseId");
UserDatabaseDTO database = databaseMap.get(databaseId);
if (database != null) { // databases can be
// deleted
database.getLockedPeriods().add(lockedPeriod);
lockedPeriod.setParent(database);
}
parentFound = true;
}
if (!row.isNull("projectId")) {
Integer projectId = row.getInt("projectId");
ProjectDTO project = projects.get(projectId);
if (project != null) {
project.getLockedPeriods().add(lockedPeriod);
lockedPeriod.setParent(project);
parentFound = true;
}
}
if (!parentFound) {
Log.debug(
"Orphan lockedPeriod: No parent (UserDatabase/Activity/Project) found for" +
" LockedPeriod with Id=" +
lockedPeriod.getId());
}
}
promise.resolve(null);
}
});
return promise;
}
private Promise<Void> joinPartnersToDatabases() {
SqlQuery query = SqlQuery.select("d.databaseId", "d.partnerId", "p.name", "p.fullName")
.from(Tables.PARTNER_IN_DATABASE, "d")
.leftJoin(Tables.PARTNER, "p")
.on("d.PartnerId = p.PartnerId")
.orderBy("p.name");
// Only allow results that are visible to this user if we are on the
// server,
// otherwise permissions have already been taken into account during
// synchronization
if (context.isRemote()) {
query.where("d.databaseId").in(databaseMap.keySet());
}
return execute(query, new RowHandler() {
@Override
public void handleRow(SqlResultSetRow row) {
int partnerId = row.getInt("partnerId");
PartnerDTO partner = partners.get(partnerId);
if (partner == null) {
partner = new PartnerDTO();
partner.setId(partnerId);
partner.setName(row.getString("name"));
partner.setFullName(row.getString("fullName"));
partners.put(partnerId, partner);
}
UserDatabaseDTO db = databaseMap.get(row.getInt("databaseId"));
if (db != null) { // databases can be deleted
db.getPartners().add(partner);
}
}
});
}
public Promise<Void> loadActivities() {
SqlQuery query = SqlQuery.select("activityId",
"name",
"category",
"locationTypeId",
"reportingFrequency",
"databaseId",
"classicView",
"published").from("activity").orderBy("SortOrder");
if (context.isRemote()) {
query.where("DateDeleted IS NULL");
query.where("DatabaseId").in(databaseMap.keySet());
}
return execute(query, new RowHandler() {
@Override
public void handleRow(SqlResultSetRow row) {
ActivityDTO activity = new ActivityDTO();
activity.setId(row.getInt("activityId"));
activity.setName(row.getString("name"));
activity.setCategory(row.getString("category"));
activity.setReportingFrequency(row.getInt("reportingFrequency"));
activity.setPublished(row.getInt("published"));
activity.setClassicView(row.getBoolean("classicView"));
int databaseId = row.getInt("databaseId");
UserDatabaseDTO database = databaseMap.get(databaseId);
activity.setDatabase(database);
database.getActivities().add(activity);
int locationTypeId = row.getInt("locationTypeId");
LocationTypeDTO locationType = locationTypes.get(locationTypeId);
if(locationType == null) {
throw new IllegalStateException("No location type for " + locationTypeId);
}
activity.setLocationType(locationType);
activity.set("locationTypeId", locationType.getId());
activities.put(activity.getId(), activity);
}
});
}
//
// public Promise<Void> loadIndicators() {
// SqlQuery query = SqlQuery.select("indicatorId",
// "name",
// "type",
// "expression",
// "skipExpression",
// "nameInExpression",
// "calculatedAutomatically",
// "category",
// "listHeader",
// "description",
// "aggregation",
// "units",
// "activityId",
// "sortOrder",
// "mandatory").from("indicator").orderBy("SortOrder");
//
// if (context.isRemote()) {
// query.where("DateDeleted IS NULL");
// query.where("activityId")
// .in(SqlQuery.select("ActivityId").from("activity").where("databaseId").in(databaseMap.keySet()));
//
// }
//
// return execute(query, new RowHandler() {
//
// @Override
// public void handleRow(SqlResultSetRow rs) {
// IndicatorDTO indicator = new IndicatorDTO();
// indicator.setId(rs.getInt("indicatorId"));
// indicator.setName(rs.getString("name"));
// indicator.setTypeId(rs.getString("type"));
// indicator.setExpression(rs.getString("expression"));
// indicator.setSkipExpression(rs.getString("skipExpression"));
// indicator.setNameInExpression(rs.getString("nameInExpression"));
// indicator.setCalculatedAutomatically(rs.getBoolean("calculatedAutomatically"));
// indicator.setCategory(rs.getString("category"));
// indicator.setListHeader(rs.getString("listHeader"));
// indicator.setDescription(rs.getString("description"));
// indicator.setAggregation(rs.getInt("aggregation"));
// indicator.setUnits(rs.getString("units"));
// indicator.setMandatory(rs.getBoolean("mandatory"));
// indicator.setSortOrder(rs.getInt("sortOrder"));
//
// int activityId = rs.getInt("activityId");
// ActivityFormDTO activity = activities.get(activityId);
// if (activity != null) { // it may have been deleted
// activity.getIndicators().add(indicator);
// }
// }
// });
// }
//
// public Promise<Void> loadAttributeGroups() {
// SqlQuery query = SqlQuery.select()
// .appendColumn("AttributeGroupId", "id")
// .appendColumn("Name", "name")
// .appendColumn("multipleAllowed")
// .appendColumn("mandatory")
// .appendColumn("defaultValue")
// .appendColumn("workflow")
// .appendColumn("sortOrder")
// .from("attributegroup")
// .orderBy("SortOrder");
//
// if (context.isRemote()) {
// query.where("DateDeleted IS NULL");
// query.where("AttributeGroupId")
// .in(SqlQuery.select("AttributeGroupId")
// .from("attributegroupinactivity")
// .where("ActivityId")
// .in(SqlQuery.select("ActivityId")
// .from("activity")
// .where("databaseId")
// .in(databaseMap.keySet())));
//
// }
//
// return execute(query, new RowHandler() {
//
// @Override
// public void handleRow(SqlResultSetRow rs) {
//
// AttributeGroupDTO group = new AttributeGroupDTO();
// group.setId(rs.getInt("id"));
// group.setName(rs.getString("name"));
// group.setMultipleAllowed(rs.getBoolean("multipleAllowed"));
// group.setMandatory(rs.getBoolean("mandatory"));
// group.setSortOrder(rs.getInt("sortOrder"));
// if (!rs.isNull("defaultValue")) { // if null it throws NPE
// group.setDefaultValue(rs.getInt("defaultValue"));
// }
// group.setWorkflow(rs.getBoolean("workflow"));
//
// attributeGroups.put(group.getId(), group);
// }
// });
// }
//
// public Promise<Void> loadAttributes() {
// SqlQuery query = SqlQuery.select("attributeId", "name", "attributeGroupId")
// .from("attribute")
// .orderBy("SortOrder");
//
// if (context.isRemote()) {
// query.where("DateDeleted IS NULL");
// query.where("AttributeGroupId")
// .in(SqlQuery.select("AttributeGroupId")
// .from("attributegroupinactivity")
// .where("ActivityId")
// .in(SqlQuery.select("ActivityId")
// .from("activity")
// .where("databaseId")
// .in(databaseMap.keySet())));
//
// }
//
// return execute(query, new RowHandler() {
//
// @Override
// public void handleRow(SqlResultSetRow row) {
//
// AttributeDTO attribute = new AttributeDTO();
// attribute.setId(row.getInt("attributeId"));
// attribute.setName(row.getString("name"));
//
// int groupId = row.getInt("attributeGroupId");
// AttributeGroupDTO group = attributeGroups.get(groupId);
// if (group != null) {
// group.getAttributes().add(attribute);
// }
// }
// });
// }
//
// public Promise<Void> joinAttributesToActivities() {
// SqlQuery query = SqlQuery.select("J.activityId", "J.attributeGroupId")
// .from("attributegroupinactivity J " +
// "INNER JOIN attributegroup G ON (J.attributeGroupId = G.attributeGroupId)")
// .orderBy("G.SortOrder")
// .where("G.dateDeleted")
// .isNull();
//
// if (context.isRemote()) {
// query.where("ActivityId")
// .in(SqlQuery.select("ActivityId").from("activity").where("databaseId").in(databaseMap.keySet()));
//
// }
//
// return execute(query, new RowHandler() {
// @Override
// public void handleRow(SqlResultSetRow row) {
//
// int groupId = row.getInt("attributeGroupId");
//
// ActivityFormDTO activity = activities.get(row.getInt("activityId"));
// if (activity != null) { // it may have been deleted
// activity.getAttributeGroups().add(attributeGroups.get(groupId));
// }
//
// }
// });
// }
private Promise<Void> execute(SqlQuery query, final RowHandler rowHandler) {
final Promise<Void> promise = new Promise<>();
query.execute(tx, new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
rowHandler.onSuccess(tx, results);
promise.resolve(null);
}
});
return promise;
}
public void build(ExecutionContext context, final AsyncCallback<SchemaDTO> callback) {
this.context = context;
this.tx = context.getTransaction();
List<Promise<Void>> tasks = Lists.newArrayList();
tasks.add(loadCountries());
tasks.add(loadAdminLevels());
tasks.add(loadLocationTypes());
tasks.add(loadDatabases());
SchemaDTO schemaDTO = new SchemaDTO();
schemaDTO.setCountries(countryList);
schemaDTO.setDatabases(databaseList);
Promise.waitAll(tasks)
.then(Functions.constant(schemaDTO))
.then(callback);
}
}
}