mirror of https://github.com/tasks/tasks
Partial-work commit of tag migration and improvements to the data model.
parent
08bdc33e6b
commit
f29da76fac
@ -0,0 +1,178 @@
|
|||||||
|
package com.todoroo.astrid.tags;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
|
|
||||||
|
import com.thoughtworks.sql.Criterion;
|
||||||
|
import com.thoughtworks.sql.Order;
|
||||||
|
import com.thoughtworks.sql.Query;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.data.Property.CountProperty;
|
||||||
|
import com.todoroo.andlib.service.Autowired;
|
||||||
|
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||||
|
import com.todoroo.astrid.api.AstridApiConstants;
|
||||||
|
import com.todoroo.astrid.api.AstridContentProvider.AstridTask;
|
||||||
|
import com.todoroo.astrid.dao.Database;
|
||||||
|
import com.todoroo.astrid.dao.MetadataDao;
|
||||||
|
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
||||||
|
import com.todoroo.astrid.model.Metadata;
|
||||||
|
import com.todoroo.astrid.service.MetadataService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides operations for working with tags
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public class DataService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata key for tag data
|
||||||
|
*/
|
||||||
|
public static final String KEY = "tags-tag";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Database database;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataDao metadataDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataService metadataService;
|
||||||
|
|
||||||
|
public DataService(@SuppressWarnings("unused") Context context) {
|
||||||
|
DependencyInjectionService.getInstance().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property for retrieving count of aggregated rows
|
||||||
|
*/
|
||||||
|
private static final CountProperty COUNT = new CountProperty();
|
||||||
|
|
||||||
|
public static final Order GROUPED_TAGS_BY_ALPHA = Order.asc(Metadata.VALUE);
|
||||||
|
public static final Order GROUPED_TAGS_BY_SIZE = Order.desc(COUNT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for returning a tag/task count pair
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Tag {
|
||||||
|
String tag;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all tags ordered by given clause
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* @return empty array if no tags, otherwise array
|
||||||
|
*/
|
||||||
|
public Tag[] getGroupedTags(Order order) {
|
||||||
|
TodorooCursor<Metadata> cursor = metadataService.fetchWithCount(
|
||||||
|
COUNT, MetadataCriteria.withKey(KEY), order, true);
|
||||||
|
try {
|
||||||
|
Tag[] array = new Tag[cursor.getCount()];
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
cursor.moveToNext();
|
||||||
|
array[i] = new Tag();
|
||||||
|
array[i].tag = cursor.get(Metadata.VALUE);
|
||||||
|
array[i].count = cursor.get(COUNT);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return tags on the given task
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* @return empty array if no tags, otherwise array
|
||||||
|
*/
|
||||||
|
public TodorooCursor<Metadata> getTags(long taskId) {
|
||||||
|
Query query = Query.select(Metadata.VALUE).where(Criterion.and(MetadataCriteria.withKey(KEY),
|
||||||
|
MetadataCriteria.byTask(taskId)));
|
||||||
|
return metadataDao.query(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return tags as a comma-separated list of strings
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* @return empty string if no tags, otherwise string
|
||||||
|
*/
|
||||||
|
public String getTagsAsString(long taskId) {
|
||||||
|
StringBuilder tagBuilder = new StringBuilder();
|
||||||
|
String[] tags = getTags(taskId);
|
||||||
|
for (int i = 0; i < tags.length; i++) {
|
||||||
|
tagBuilder.append(tags[i]);
|
||||||
|
if (i < tags.length - 1)
|
||||||
|
tagBuilder.append(", ");
|
||||||
|
}
|
||||||
|
return tagBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String query = String.format("INNER JOIN %s ON %s = "
|
||||||
|
+ "%s WHERE %s = 0 AND %s = '%s' AND %s = ",
|
||||||
|
AstridApiConstants.METADATA_TABLE,
|
||||||
|
AstridTask.ID, Metadata.TASK,
|
||||||
|
AstridTask.COMPLETION_DATE,
|
||||||
|
Metadata.KEY, KEY,
|
||||||
|
Metadata.VALUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return SQL selector query for getting tasks with a given tag
|
||||||
|
*
|
||||||
|
* @param tag
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getQuery(String tag) {
|
||||||
|
return query + String.format("%s", DatabaseUtils.sqlEscapeString(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String newTaskSql = String.format(
|
||||||
|
"INSERT INTO %s (%s, %s, %s) " + "VALUES ($ID,'%s',",
|
||||||
|
AstridApiConstants.METADATA_TABLE,
|
||||||
|
Metadata.TASK.name,
|
||||||
|
Metadata.KEY.name,
|
||||||
|
Metadata.VALUE.name,
|
||||||
|
KEY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return SQL new task creator query
|
||||||
|
* @param tag
|
||||||
|
*/
|
||||||
|
public String getNewTaskSql(String tag) {
|
||||||
|
return newTaskSql + String.format("%s)", DatabaseUtils.sqlEscapeString(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the given array of tags into the database
|
||||||
|
* @param taskId
|
||||||
|
* @param tags
|
||||||
|
*/
|
||||||
|
public void synchronizeTags(long taskId, ArrayList<String> tags) {
|
||||||
|
metadataDao.deleteWhere(database, MetadataSql.byTask(taskId) + " AND " +
|
||||||
|
MetadataSql.withKey(KEY));
|
||||||
|
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.setValue(Metadata.KEY, KEY);
|
||||||
|
metadata.setValue(Metadata.TASK, taskId);
|
||||||
|
for(String tag : tags) {
|
||||||
|
metadata.setValue(Metadata.VALUE, tag.trim());
|
||||||
|
metadataDao.save(database, metadata);
|
||||||
|
metadata.clearValue(Metadata.ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Todoroo Inc
|
||||||
|
* All Rights Reserved
|
||||||
|
* http://www.todoroo.com
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.tagsold;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.AbstractModel;
|
||||||
|
import com.todoroo.andlib.data.Property;
|
||||||
|
import com.todoroo.andlib.data.Table;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.data.Property.LongProperty;
|
||||||
|
import com.todoroo.andlib.data.Property.StringProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Model which represents a task users need to accomplish.
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public class Tag extends AbstractModel {
|
||||||
|
|
||||||
|
// --- table
|
||||||
|
|
||||||
|
public static final Table TABLE = new Table("tags", Tag.class);
|
||||||
|
|
||||||
|
// --- properties
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
public static final LongProperty ID = new LongProperty(
|
||||||
|
TABLE, ID_PROPERTY_NAME);
|
||||||
|
|
||||||
|
/** Name of Task */
|
||||||
|
public static final StringProperty NAME = new StringProperty(
|
||||||
|
TABLE, "name");
|
||||||
|
|
||||||
|
/** List of all properties for this model */
|
||||||
|
public static final Property<?>[] PROPERTIES = generateProperties(Tag.class);
|
||||||
|
|
||||||
|
// --- defaults
|
||||||
|
|
||||||
|
/** Default values container */
|
||||||
|
private static final ContentValues defaultValues = new ContentValues();
|
||||||
|
|
||||||
|
static {
|
||||||
|
defaultValues.put(NAME.name, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentValues getDefaultValues() {
|
||||||
|
return defaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- data access boilerplate
|
||||||
|
|
||||||
|
public Tag() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag(TodorooCursor<Tag> cursor, Property<?>[] properties) {
|
||||||
|
this();
|
||||||
|
readPropertiesFromCursor(cursor, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFromCursor(TodorooCursor<Tag> cursor, Property<?>[] properties) {
|
||||||
|
super.readPropertiesFromCursor(cursor, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return getIdHelper(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* ASTRID: Android's Simple Task Recording Dashboard
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Tim Su
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.tagsold;
|
||||||
|
|
||||||
|
import android.database.SQLException;
|
||||||
|
|
||||||
|
import com.thoughtworks.sql.Join;
|
||||||
|
import com.thoughtworks.sql.Query;
|
||||||
|
import com.todoroo.andlib.data.GenericDao;
|
||||||
|
import com.todoroo.andlib.data.Property;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.astrid.model.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for tags plugin
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TagService {
|
||||||
|
|
||||||
|
private GenericDao<Tag> tagDao;
|
||||||
|
private GenericDao<TagToTaskMapping> tagToTaskDao;
|
||||||
|
|
||||||
|
public TagService() {
|
||||||
|
TagsDatabase tagDatabase = new TagsDatabase();
|
||||||
|
tagDao = new GenericDao<Tag>(Tag.class, tagDatabase);
|
||||||
|
tagToTaskDao = new GenericDao<TagToTaskMapping>(TagToTaskMapping.class,
|
||||||
|
tagDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- tag batch operations
|
||||||
|
|
||||||
|
/** Get a list of all tags */
|
||||||
|
public TodorooCursor<Tag> getAllTags(Property<?>... properties) {
|
||||||
|
return tagDao.query(Query.select(properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a list of tag identifiers for the given task */
|
||||||
|
public TodorooCursor<Tag> getTaskTags(Task task, Property<?>... properties) throws SQLException {
|
||||||
|
Query query = Query.select(properties).join(Join.inner(TagToTaskMapping.TABLE,
|
||||||
|
Tag.ID.eq(TagToTaskMapping.TAG))).where(TagToTaskMapping.TASK.eq(task.getId()));
|
||||||
|
return tagDao.query(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Todoroo Inc
|
||||||
|
* All Rights Reserved
|
||||||
|
* http://www.todoroo.com
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.tagsold;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.AbstractModel;
|
||||||
|
import com.todoroo.andlib.data.Property;
|
||||||
|
import com.todoroo.andlib.data.Table;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.data.Property.LongProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Model which represents a task users need to accomplish.
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public class TagToTaskMapping extends AbstractModel {
|
||||||
|
|
||||||
|
// --- table
|
||||||
|
|
||||||
|
public static final Table TABLE = new Table("tagTaskMap", TagToTaskMapping.class);
|
||||||
|
|
||||||
|
// --- properties
|
||||||
|
|
||||||
|
/** Tag */
|
||||||
|
public static final LongProperty TAG = new LongProperty(
|
||||||
|
TABLE, "tag");
|
||||||
|
|
||||||
|
/** Task */
|
||||||
|
public static final LongProperty TASK = new LongProperty(
|
||||||
|
TABLE, "task");
|
||||||
|
|
||||||
|
/** List of all properties for this model */
|
||||||
|
public static final Property<?>[] PROPERTIES = generateProperties(TagToTaskMapping.class);
|
||||||
|
|
||||||
|
// --- defaults
|
||||||
|
|
||||||
|
/** Default values container */
|
||||||
|
private static final ContentValues defaultValues = new ContentValues();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentValues getDefaultValues() {
|
||||||
|
return defaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- data access boilerplate
|
||||||
|
|
||||||
|
public TagToTaskMapping() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagToTaskMapping(TodorooCursor<TagToTaskMapping> cursor, Property<?>[] properties) {
|
||||||
|
this();
|
||||||
|
readPropertiesFromCursor(cursor, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFromCursor(TodorooCursor<TagToTaskMapping> cursor, Property<?>[] properties) {
|
||||||
|
super.readPropertiesFromCursor(cursor, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Todoroo Inc
|
||||||
|
* All Rights Reserved
|
||||||
|
* http://www.todoroo.com
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.tagsold;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.AbstractDatabase;
|
||||||
|
import com.todoroo.andlib.data.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database wrapper
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public class TagsDatabase extends AbstractDatabase {
|
||||||
|
|
||||||
|
// --- constants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database version number. This variable must be updated when database
|
||||||
|
* tables are updated, as it determines whether a database needs updating.
|
||||||
|
*/
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database name (must be unique)
|
||||||
|
*/
|
||||||
|
private static final String NAME = "tags";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of table/ If you're adding a new table, add it to this list and
|
||||||
|
* also make sure that our SQLite helper does the right thing.
|
||||||
|
*/
|
||||||
|
public static final Table[] TABLES = new Table[] {
|
||||||
|
Tag.TABLE,
|
||||||
|
TagToTaskMapping.TABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- implementation
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getVersion() {
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Table[] getTables() {
|
||||||
|
return TABLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreateTables() {
|
||||||
|
StringBuilder sql = new StringBuilder();
|
||||||
|
sql.append("CREATE INDEX IF NOT EXISTS tm_tag ON ").
|
||||||
|
append(TagToTaskMapping.TABLE).append('(').
|
||||||
|
append(TagToTaskMapping.TAG.name).
|
||||||
|
append(')');
|
||||||
|
database.execSQL(sql.toString());
|
||||||
|
|
||||||
|
sql.setLength(0);
|
||||||
|
sql.append("CREATE INDEX IF NOT EXISTS tm_task ON ").
|
||||||
|
append(TagToTaskMapping.TABLE).append('(').
|
||||||
|
append(TagToTaskMapping.TASK.name).
|
||||||
|
append(')');
|
||||||
|
database.execSQL(sql.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onUpgrade(int oldVersion, int newVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue