package org.commcare.models.database.user;
import android.content.Context;
import net.sqlcipher.database.SQLiteDatabase;
import org.commcare.CommCareApplication;
import org.commcare.android.database.global.models.ApplicationRecord;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.database.user.models.FormRecordV1;
import org.commcare.android.database.user.models.FormRecordV2;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.cases.ledger.Ledger;
import org.commcare.logging.AndroidLogger;
import org.commcare.models.database.AndroidTableBuilder;
import org.commcare.models.database.ConcreteAndroidDbHelper;
import org.commcare.models.database.DbUtil;
import org.commcare.models.database.SqlStorage;
import org.commcare.modern.database.DatabaseIndexingUtils;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.storage.Persistable;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
/**
* Created by amstone326 on 3/8/17.
*/
public class UserDbUpgradeUtils {
protected static void addAppIdColumnToTable(SQLiteDatabase db) {
// Alter the FormRecord table to include an app id column
db.execSQL(DbUtil.addColumnToTable(
FormRecord.STORAGE_KEY,
FormRecord.META_APP_ID,
"TEXT"));
}
protected static void addFormNumberColumnToTable(SQLiteDatabase db) {
// Alter the FormRecord table to include an app id column
db.execSQL(DbUtil.addColumnToTable(
FormRecord.STORAGE_KEY,
FormRecord.META_SUBMISSION_ORDERING_NUMBER,
"TEXT"));
}
protected static boolean multipleInstalledAppRecords() {
SqlStorage<ApplicationRecord> storage =
CommCareApplication.instance().getGlobalStorage(ApplicationRecord.class);
int count = 0;
for (ApplicationRecord r : storage) {
if (r.getStatus() == ApplicationRecord.STATUS_INSTALLED && r.resourcesValidated()) {
count++;
}
}
return (count > 1);
}
protected static ApplicationRecord getInstalledAppRecord() {
SqlStorage<ApplicationRecord> storage =
CommCareApplication.instance().getGlobalStorage(ApplicationRecord.class);
for (Persistable p : storage) {
ApplicationRecord r = (ApplicationRecord)p;
if (r.getStatus() == ApplicationRecord.STATUS_INSTALLED && r.resourcesValidated()) {
return r;
}
}
return null;
}
protected static void deleteExistingFormRecordsAndWarnUser(Context c, SQLiteDatabase db) {
SqlStorage<FormRecordV1> formRecordStorage = new SqlStorage<>(
FormRecord.STORAGE_KEY,
FormRecordV1.class,
new ConcreteAndroidDbHelper(c, db));
SqlStorage<SessionStateDescriptor> ssdStorage = new SqlStorage<>(
SessionStateDescriptor.STORAGE_KEY,
SessionStateDescriptor.class,
new ConcreteAndroidDbHelper(c, db));
formRecordStorage.removeAll();
ssdStorage.removeAll();
String warningTitle = "Minor data loss during upgrade";
String warningMessage = "Due to the experimental state of" +
" multiple application seating, we were not able to migrate all of your app data" +
" during upgrade. Any saved, incomplete, and unsent forms on the device were deleted.";
CommCareApplication.instance().storeMessageForUserOnDispatch(warningTitle, warningMessage);
}
protected static void updateIndexes(SQLiteDatabase db) {
db.execSQL(DatabaseIndexingUtils.indexOnTableCommand("case_id_index", "AndroidCase", "case_id"));
db.execSQL(DatabaseIndexingUtils.indexOnTableCommand("case_type_index", "AndroidCase", "case_type"));
db.execSQL(DatabaseIndexingUtils.indexOnTableCommand("case_status_index", "AndroidCase", "case_status"));
}
protected static void addStockTable(SQLiteDatabase db) {
AndroidTableBuilder builder = new AndroidTableBuilder(Ledger.STORAGE_KEY);
builder.addData(new Ledger());
builder.setUnique(Ledger.INDEX_ENTITY_ID);
db.execSQL(builder.getTableCreateString());
}
protected static Set<String> getAppIdsForRecords(SqlStorage<FormRecordV2> oldFormRecords) {
Set<String> appIds = new HashSet<>();
for (FormRecordV2 formRecord : oldFormRecords) {
appIds.add(formRecord.getAppId());
}
return appIds;
}
protected static void sortRecordsByDate(Vector<Integer> ids,
SqlStorage<FormRecordV2> storage) {
final HashMap<Integer, Long> idToDateIndex =
getIdToDateMap(ids, storage);
Collections.sort(ids, new Comparator<Integer>() {
@Override
public int compare(Integer lhs, Integer rhs) {
Long lhd = idToDateIndex.get(lhs);
Long rhd = idToDateIndex.get(rhs);
if (lhd < rhd) {
return -1;
}
if (lhd > rhd) {
return 1;
}
return 0;
}
});
}
private static HashMap<Integer, Long> getIdToDateMap(Vector<Integer> ids,
SqlStorage<FormRecordV2> storage) {
HashMap<Integer, Long> idToDateIndex = new HashMap<>();
for (int id : ids) {
// Last modified for a unsent and complete forms is the formEnd
// date that was captured and locked when form entry, so it's a
// safe cannonical ordering
String dateAsString =
storage.getMetaDataFieldForRecord(id, FormRecord.META_LAST_MODIFIED);
long dateAsSeconds;
try {
dateAsSeconds = Long.valueOf(dateAsString);
} catch (NumberFormatException e) {
// Go with the next best ordering for now
Logger.log(AndroidLogger.TYPE_ERROR_ASSERTION,
"Invalid date in last modified value: " + dateAsString);
idToDateIndex.put(id, (long)id);
continue;
}
idToDateIndex.put(id, dateAsSeconds);
}
return idToDateIndex;
}
}