Worked on migrating tasks table. And unit tests.

pull/14/head
Tim Su 15 years ago
parent 54f5ff5eb8
commit 3a48555cc7

@ -106,47 +106,34 @@ public class AstridContentProvider {
// --- columns
/** long: Task id */
public static final String ID = AstridApiConstants.TASK_TABLE + "._id";
public static final String ID = "_id";
/** String: name of Task */
public static final String TITLE = AstridApiConstants.TASK_TABLE
+ ".title";
/**
* int: Task Urgency setting (see <code>Task.URGENCY_*</code> for
* possible values)
*/
public static final String URGENCY = AstridApiConstants.TASK_TABLE
+ ".urgency";
/**
* int: Task Importance setting (see <code>Task.IMPORTANCE_*</code> for
* possible values)
*/
public static final String IMPORTANCE = AstridApiConstants.TASK_TABLE
+ ".importance";
public static final String TITLE = "title";
/** int: Task Urgency setting (see <code>Task.URGENCY_*</code>) */
public static final String URGENCY = "urgency";
/** int: Task Importance setting (see <code>Task.IMPORTANCE_*</code>) */
public static final String IMPORTANCE = "importance";
/** int: unixtime Task is due, 0 if not set */
public static final String DUE_DATE = AstridApiConstants.TASK_TABLE
+ ".dueDate";
public static final String DUE_DATE = "dueDate";
/** int: unixtime Task should be hidden until, 0 if not set */
public static final String HIDDEN_UNTIL = AstridApiConstants.TASK_TABLE
+ ".hiddenUntil";
public static final String HIDDEN_UNTIL = "hiddenUntil";
/** int: unixtime Task was created */
public static final String CREATION_DATE = AstridApiConstants.TASK_TABLE
+ ".creationDate";
public static final String CREATION_DATE = "creationDate";
/** int: unixtime Task was completed, 0 if task not completed */
public static final String COMPLETION_DATE = AstridApiConstants.TASK_TABLE
+ ".completionDate";
public static final String COMPLETION_DATE = "completionDate";
/** int: unixtime Task was deleted, 0 if task not deleted */
public static final String DELETION_DATE = AstridApiConstants.TASK_TABLE
+ ".deletionDate";
public static final String DELETION_DATE = "deletionDate";
public static final String MODIFICATION_DATE = null;
/** int: unixtime Task was modified */
public static final String MODIFICATION_DATE = "modificationDate";
// --- urgency settings

@ -35,11 +35,11 @@ abstract public class AbstractController {
public static final String KEY_ROWID = "_id";
// database and table names
protected static final String TASK_TABLE_NAME = "tasks";
protected static final String TAG_TABLE_NAME = "tags";
protected static final String TAG_TASK_MAP_NAME = "tagTaskMap";
protected static final String ALERT_TABLE_NAME = "alerts";
protected static final String SYNC_TABLE_NAME = "sync";
public static final String TASK_TABLE_NAME = "tasks";
public static final String TAG_TABLE_NAME = "tags";
public static final String TAG_TASK_MAP_NAME = "tagTaskMap";
public static final String ALERT_TABLE_NAME = "alerts";
public static final String SYNC_TABLE_NAME = "sync";
abstract public void open();
abstract public void close();

@ -76,7 +76,7 @@ public final class Database extends AbstractDatabase {
* Default implementation of Astrid database helper
*/
@SuppressWarnings("nls")
public static class AstridSQLiteOpenHelper extends SQLiteOpenHelper {
private static class AstridSQLiteOpenHelper extends SQLiteOpenHelper {
public AstridSQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) {
@ -98,6 +98,7 @@ public final class Database extends AbstractDatabase {
for(Property<?> property : table.getProperties()) {
if(AbstractModel.ID_PROPERTY.equals(property.name))
continue;
Log.e("haha", table.name + "'s " + property.name);
sql.append(',').append(property.accept(sqlVisitor, null));
}
sql.append(')');

@ -45,16 +45,7 @@ public class Metadata extends AbstractModel {
TABLE, "value");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = new Property<?>[] {
ID,
TASK,
KEY,
VALUE,
};
static {
TABLE.setProperties(PROPERTIES);
}
public static final Property<?>[] PROPERTIES = generateProperties(Metadata.class);
// --- defaults

@ -8,6 +8,8 @@ package com.todoroo.astrid.model;
import android.content.ContentValues;
import com.timsu.astrid.data.enums.RepeatInterval;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
@ -73,22 +75,47 @@ public class Task extends AbstractModel {
public static final IntegerProperty DELETION_DATE = new IntegerProperty(
TABLE, AstridTask.DELETION_DATE);
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = new Property<?>[] {
ID,
TITLE,
URGENCY,
IMPORTANCE,
DUE_DATE,
HIDDEN_UNTIL,
CREATION_DATE,
COMPLETION_DATE,
DELETION_DATE
};
// --- for migration purposes from astrid 2 (eventually we will want to
// move these into the metadata table and treat them as plug-ins
static {
TABLE.setProperties(PROPERTIES);
}
public static final StringProperty NOTES = new StringProperty(
TABLE, "notes");
public static final IntegerProperty ESTIMATED_SECONDS = new IntegerProperty(
TABLE, "estimatedSeconds");
public static final IntegerProperty ELAPSED_SECONDS = new IntegerProperty(
TABLE, "elapsedSeconds");
public static final IntegerProperty TIMER_START = new IntegerProperty(
TABLE, "timerStart");
public static final IntegerProperty PREFERRED_DUE_DATE = new IntegerProperty(
TABLE, "preferredDueDate");
public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(
TABLE, "postponeCount");
public static final IntegerProperty NOTIFICATIONS = new IntegerProperty(
TABLE, "notifications");
public static final IntegerProperty NOTIFICATION_FLAGS = new IntegerProperty(
TABLE, "notificationFlags");
public static final IntegerProperty LAST_NOTIFIED = new IntegerProperty(
TABLE, "lastNotified");
public static final IntegerProperty REPEAT = new IntegerProperty(
TABLE, "repeat");
public static final IntegerProperty FLAGS = new IntegerProperty(
TABLE, "flags");
public static final StringProperty CALENDAR_URI = new StringProperty(
TABLE, "calendarUri");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(Task.class);
// --- urgency flags
@ -197,4 +224,23 @@ public class Task extends AbstractModel {
return getValue(DUE_DATE) > 0;
}
// --- data access methods for migration properties
/** Number of bits to shift repeat value by */
public static final int REPEAT_VALUE_OFFSET = 3;
/**
* @return RepeatInfo corresponding to
*/
public RepeatInfo getRepeatInfo() {
int repeat = getValue(REPEAT);
if(repeat == 0)
return null;
int value = repeat >> REPEAT_VALUE_OFFSET;
RepeatInterval interval = RepeatInterval.values()
[repeat - (value << REPEAT_VALUE_OFFSET)];
return new RepeatInfo(interval, value);
}
}

@ -0,0 +1,208 @@
package com.todoroo.astrid.service;
import java.util.HashMap;
import java.util.Map.Entry;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.task.AbstractTaskModel;
import com.todoroo.andlib.data.AbstractDao;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Task;
public final class UpgradeService {
@Autowired
private Database database;
@Autowired
private TaskDao taskDao;
/**
* Perform upgrade from one version to the next. Needs to be called
* on the UI thread so it can display a progress bar and then
* show users a change log.
*
* @param from
* @param to
*/
public void performUpgrade(int from, int to) {
if(from == to)
return;
if(from < 150) {
upgrade2To3();
}
}
// --- database upgrade logic
/**
* Upgrade helper class that reads a database
*/
private class Astrid2UpgradeHelper extends SQLiteOpenHelper {
private String name;
public Astrid2UpgradeHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
this.name = name;
}
@Override
@SuppressWarnings("nls")
public void onCreate(SQLiteDatabase db) {
// create empty table with nothing in it
String sql = "CREATE TABLE IF NOT EXISTS " + name + " (" +
AbstractModel.ID_PROPERTY + " INTEGER);";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do nothing
}
}
/**
* Perform the upgrade from Astrid 2 to Astrid 3
*/
private void upgrade2To3() {
Context context = ContextManager.getContext();
// --- upgrade tasks table
HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>();
propertyMap.put(AbstractController.KEY_ROWID, Task.ID);
propertyMap.put(AbstractTaskModel.NAME, Task.TITLE);
propertyMap.put(AbstractTaskModel.NOTES, Task.TITLE);
// don't update progress percentage, we don't use this anymore
propertyMap.put(AbstractTaskModel.IMPORTANCE, Task.IMPORTANCE);
propertyMap.put(AbstractTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS);
propertyMap.put(AbstractTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
propertyMap.put(AbstractTaskModel.TIMER_START, Task.TIMER_START);
propertyMap.put(AbstractTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
propertyMap.put(AbstractTaskModel.PREFERRED_DUE_DATE, Task.PREFERRED_DUE_DATE);
propertyMap.put(AbstractTaskModel.HIDDEN_UNTIL, Task.HIDDEN_UNTIL);
propertyMap.put(AbstractTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.NOTIFICATIONS);
propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.NOTIFICATION_FLAGS);
propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.LAST_NOTIFIED);
propertyMap.put(AbstractTaskModel.REPEAT, Task.REPEAT);
propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(AbstractTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
propertyMap.put(AbstractTaskModel.FLAGS, Task.FLAGS);
upgradeTasksTable(context, AbstractController.TASK_TABLE_NAME,
propertyMap, new Task(), taskDao);
// --- upgrade tags table
}
protected static class UpgradeVisitorContainer {
public int columnIndex;
public Cursor cursor;
public AbstractModel model;
}
/**
* Visitor that reads from a visitor container and writes to the model
* @author Tim Su <tim@todoroo.com>
*
*/
protected class ColumnUpgradeVisitor implements PropertyVisitor<Void, UpgradeVisitorContainer> {
@Override
public Void visitDouble(Property<Double> property, UpgradeVisitorContainer data) {
double value = data.cursor.getDouble(data.columnIndex);
data.model.setValue(property, value);
return null;
}
@Override
public Void visitInteger(Property<Integer> property, UpgradeVisitorContainer data) {
int value;
// convert long date -> integer
if(property == Task.COMPLETION_DATE ||
property == Task.CREATION_DATE ||
property == Task.DELETION_DATE ||
property == Task.DUE_DATE ||
property == Task.HIDDEN_UNTIL ||
property == Task.LAST_NOTIFIED ||
property == Task.MODIFICATION_DATE ||
property == Task.PREFERRED_DUE_DATE)
value = (int) (data.cursor.getLong(data.columnIndex) / 1000L);
else
value = data.cursor.getInt(data.columnIndex);
data.model.setValue(property, value);
return null;
}
@Override
public Void visitLong(Property<Long> property, UpgradeVisitorContainer data) {
long value = data.cursor.getLong(data.columnIndex);
data.model.setValue(property, value);
return null;
}
@Override
public Void visitString(Property<String> property, UpgradeVisitorContainer data) {
String value = data.cursor.getString(data.columnIndex);
data.model.setValue(property, value);
return null;
}
}
/**
* Helper that reads entries from legacy database and row-by-row
* creates new models and saves them.
*
* @param context
* @param legacyTable
* @param propertyMap
* @param model
* @param dao
*/
@SuppressWarnings("nls")
private <TYPE extends AbstractModel> void upgradeTasksTable(Context context, String legacyTable,
HashMap<String, Property<?>> propertyMap, TYPE model,
AbstractDao<TYPE> dao) {
SQLiteDatabase upgradeDb = new Astrid2UpgradeHelper(context, legacyTable,
null, 0).getReadableDatabase();
Cursor cursor = upgradeDb.rawQuery("SELECT * FROM " + legacyTable, null);
UpgradeVisitorContainer container = new UpgradeVisitorContainer();
container.cursor = cursor;
container.model = model;
ColumnUpgradeVisitor visitor = new ColumnUpgradeVisitor();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
model.clear();
for(Entry<String, Property<?>> entry : propertyMap.entrySet()) {
container.columnIndex = cursor.getColumnIndex(entry.getKey());
entry.getValue().accept(visitor, container);
}
dao.save(database, container.model);
}
upgradeDb.close();
context.deleteDatabase(legacyTable);
}
}

@ -20,6 +20,6 @@
"adb shell am instrument -w com.xxx.xxx.tests/android.test.InstrumentationTestRunner"
-->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.todoroo.astrid"
android:targetPackage="com.timsu.astrid"
android:label="Tests for Astrid"/>
</manifest>

@ -22,11 +22,11 @@ public class DatabaseTestCase extends TodorooTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
// create new test database
database.clear();
database.clear();
database.openForWriting();
super.setUp();
}
@Override

@ -25,6 +25,8 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
}
public static void assertDatesEqual(Date old, int newDate) {
if(old == null)
assertEquals(0, newDate);
assertEquals(old.getTime() / 1000L, newDate);
}
@ -44,10 +46,12 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
TaskModelForEdit guti = new com.todoroo.astrid.legacy.data.task.TaskModelForEdit();
guti.setName("franklin gutierrez");
guti.setPreferredDueDate(new Date(System.currentTimeMillis() + 5000000L));
guti.setImportance(Importance.LEVEL_4);
guti.setHiddenUntil(new Date());
guti.setRepeat(new RepeatInfo(RepeatInterval.DAYS, 10));
guti.setElapsedSeconds(500);
taskController.saveTask(guti, false);
Date createdDate = new Date();
// assert created
assertEquals(2, taskController.getAllTaskIdentifiers());
@ -62,12 +66,30 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// verify that data exists in our new table
TodorooCursor<Task> tasks = taskDao.query(database, Query.select(Task.PROPERTIES));
assertEquals(2, tasks.getCount());
tasks.moveToFirst();
Task task = new Task(tasks, Task.PROPERTIES);
assertEquals(griffey.getName(), task.getValue(Task.TITLE));
assertDatesEqual(griffey.getDefiniteDueDate(), task.getValue(Task.DUE_DATE));
assertEquals((Integer)Task.IMPORTANCE_SHOULD_DO, task.getValue(Task.IMPORTANCE));
assertEquals(griffey.getEstimatedSeconds(), task.getValue(Task.ESTIMATED_SECONDS));
assertEquals(griffey.getNotes(), task.getValue(Task.NOTES));
assertEquals((Integer)0, task.getValue(Task.LAST_NOTIFIED));
assertEquals((Integer)0, task.getValue(Task.HIDDEN_UNTIL));
tasks.moveToNext();
task = new Task(tasks, Task.PROPERTIES);
assertEquals(guti.getName(), task.getValue(Task.TITLE));
assertDatesEqual(guti.getDefiniteDueDate(), task.getValue(Task.DUE_DATE));
assertDatesEqual(guti.getPreferredDueDate(), task.getValue(Task.DUE_DATE));
assertDatesEqual(guti.getHiddenUntil(), task.getValue(Task.HIDDEN_UNTIL));
assertEquals((Integer)Task.IMPORTANCE_DO_OR_DIE, task.getValue(Task.IMPORTANCE));
assertEquals(guti.getRepeat().getValue(), task.getRepeatInfo().getValue());
assertEquals(guti.getRepeat().getInterval().ordinal(), task.getRepeatInfo().getInterval().ordinal());
assertEquals(guti.getElapsedSeconds(), task.getValue(Task.ELAPSED_SECONDS));
assertEquals(createdDate, task.getValue(Task.CREATION_DATE));
assertEquals(createdDate, task.getValue(Task.MODIFICATION_DATE));
}

Loading…
Cancel
Save