Initial version of basic project task management application.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@897 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2002-11-08 09:14:21 +00:00
parent ac48cced65
commit aa4c37541a
22 changed files with 1155 additions and 0 deletions
@@ -0,0 +1,42 @@
//
// $Id: Log.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue;
/**
* A placeholder class that contains a reference to the log object used by
* this package.
*/
public class Log
{
/**
* This is the log instance that will be used to log all messages for
* this package.
*/
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("twodue");
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -0,0 +1,102 @@
//
// $Id: TwoDueApp.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.StaticConnectionProvider;
import com.samskivert.servlet.JDBCTableSiteIdentifier;
import com.samskivert.servlet.SiteIdentifier;
import com.samskivert.servlet.user.UserManager;
import com.samskivert.util.ServiceUnavailableException;
import com.samskivert.velocity.Application;
import com.samskivert.twodue.data.TaskRepository;
/**
* Contains references to application-wide resources (like the database
* repository) and handles initialization and cleanup for those resources.
*/
public class TwoDueApp extends Application
{
/** Returns the connection provider in use by this application. */
public final ConnectionProvider getConnectionProvider ()
{
return _conprov;
}
/** Returns the task repository in use by the application. */
public TaskRepository getRepository ()
{
return _taskrep;
}
/** Returns the user manager in use by the application. */
public UserManager getUserManager ()
{
return _usermgr;
}
protected void willInit (ServletConfig config)
{
super.willInit(config);
try {
// create a static connection provider
_conprov = new StaticConnectionProvider(CONN_CONFIG);
// initialize the user manager
Properties props = new Properties();
props.put("login_url", "/register/login.wm?from=%R");
_usermgr = new UserManager(props, _conprov);
// initialize the task repository
_taskrep = new TaskRepository(_conprov);
Log.info("TwoDue application initialized.");
} catch (Throwable t) {
Log.warning("Error initializing application: " + t);
}
}
public void shutdown ()
{
try {
_usermgr.shutdown();
Log.info("TwoDue application shutdown.");
} catch (Throwable t) {
Log.warning("Error shutting down repository: " + t);
}
}
/** We want a special site identifier. */
protected SiteIdentifier createSiteIdentifier (ServletContext ctx)
{
try {
return new JDBCTableSiteIdentifier(_conprov);
} catch (PersistenceException pe) {
throw new ServiceUnavailableException(
"Can't access site database.", pe);
}
}
/** A reference to our connection provider. */
protected ConnectionProvider _conprov;
/** A reference to our user manager. */
protected UserManager _usermgr;
/** A reference to our task repository. */
protected TaskRepository _taskrep;
/** The path to our database configuration file. */
protected static final String CONN_CONFIG = "repository.properties";
}
@@ -0,0 +1,35 @@
//
// $Id: Task.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue.data;
import java.util.ArrayList;
import java.sql.Date;
/**
* Contains the basic data associated with a task.
*/
public class Task
{
public int taskId;
public String summary;
public String category;
public String complexity;
public int priority;
public String creator;
public Date creation;
public String owner;
public String completor;
public Date completion;
public transient ArrayList notes;
}
@@ -0,0 +1,252 @@
//
// $Id: TaskRepository.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue.data;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.JORARepository;
import com.samskivert.jdbc.jora.Cursor;
import com.samskivert.jdbc.jora.Session;
import com.samskivert.jdbc.jora.Table;
/**
* Provides access to the task table.
*/
public class TaskRepository extends JORARepository
{
/**
* The database identifier that the repository will use when fetching
* a connection from the connection provider. The value is
* <code>twodue</code> which you'll probably need to know to properly
* configure your connection provider.
*/
public static final String REPOSITORY_DB_IDENT = "twodue";
/**
* Creates the repository and opens the task database. A connection
* provider should be supplied that will be used to obtain the
* necessary database connection. The database identifier used to
* obtain our connection is documented by {@link
* #REPOSITORY_DB_IDENT}.
*
* @param provider a connection provider via which the repository will
* get its database connection.
*/
public TaskRepository (ConnectionProvider provider)
throws PersistenceException
{
super(provider, REPOSITORY_DB_IDENT);
// make sure we can get our database connection
Connection conn = provider.getConnection(REPOSITORY_DB_IDENT);
provider.releaseConnection(REPOSITORY_DB_IDENT, conn);
}
// documented inherited
protected void createTables (Session session)
{
// create our table objects
_ttable = new Table(Task.class.getName(),
"TASKS", session, "TASK_ID", true);
}
/**
* Creates a task and inserts it into the repository.
*
* @return the newly created task.
*/
public Task createTask (String summary, String category, String complexity,
int priority, String creator)
throws PersistenceException
{
// create and configure a task instance
Task task = new Task();
task.summary = summary;
task.category = category;
task.complexity = complexity;
task.priority = priority;
task.creator = creator;
createTask(task);
return task;
}
/**
* Inserts the supplied (properly populated) new task into the task
* repository.
*/
public void createTask (final Task task)
throws PersistenceException
{
task.creation = new Date(System.currentTimeMillis());
execute(new Operation() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
// insert the task into the entry table
_ttable.insert(task);
// update the taskId now that it's known
task.taskId = liaison.lastInsertedId(conn);
return null;
}
});
}
/**
* Loads up the specified task.
*/
public Task loadTask (int taskId)
throws PersistenceException
{
return (Task)execute(new Operation () {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
String query = "where COMPLETOR IS NULL AND OWNER IS NULL " +
"ORDER BY CATEGORY, PRIORITY";
Cursor tc = _ttable.select(query);
return tc.toArrayList();
}
});
}
/**
* Loads up and returns all unowned, uncompleted tasks, ordered by
* priority.
*/
public ArrayList loadTasks ()
throws PersistenceException
{
return loadTasks("where COMPLETOR IS NULL AND OWNER IS NULL " +
"ORDER BY PRIORITY DESC");
}
/**
* Loads up all owned tasks, ordered by priority.
*/
public ArrayList loadOwnedTasks ()
throws PersistenceException
{
return loadTasks("where COMPLETOR IS NULL AND OWNER IS NOT NULL " +
"ORDER BY PRIORITY DESC");
}
/**
* Loads up and returns all completed tasks, sorted by completion
* date.
*
* @param start the offset into the sorted list of completed tasks to
* start returning tasks.
* @param limit the limit to the number of tasks to be returned, or -1
* if all completed should be returned.
*/
public ArrayList loadCompletedTasks (int start, int limit)
throws PersistenceException
{
String query = "where COMPLETOR IS NOT NULL " +
"ORDER BY COMPLETION DESC LIMIT " + start;
if (limit != -1) {
query += (", " + limit);
}
return loadTasks(query);
}
/** Loads lists of tasks. */
protected ArrayList loadTasks (final String query)
throws PersistenceException
{
return (ArrayList)execute(new Operation () {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
Cursor tc = _ttable.select(query);
return tc.toArrayList();
}
});
}
/**
* Marks the specified task as completed by the specified user.
*/
public void completeTask (final int taskId, final String completor)
throws PersistenceException
{
execute(new Operation () {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
String query = "update TASKS " +
"set COMPLETOR = ?, COMPLETION = ? where TASK_ID = ?";
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(query);
stmt.setString(1, completor);
stmt.setDate(2, new Date(System.currentTimeMillis()));
stmt.setInt(3, taskId);
JDBCUtil.checkedUpdate(stmt, 1);
} finally {
JDBCUtil.close(stmt);
}
return null;
}
});
}
/**
* Marks the specified task as owned by the specified user.
*/
public void claimTask (final int taskId, final String owner)
throws PersistenceException
{
execute(new Operation () {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
String query = "update TASKS " +
"set OWNER = ? where TASK_ID = ?";
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(query);
stmt.setString(1, owner);
stmt.setInt(2, taskId);
JDBCUtil.checkedUpdate(stmt, 1);
} finally {
JDBCUtil.close(stmt);
}
return null;
}
});
}
/**
* Updates a task that was previously loaded from the repository.
*/
public void updateTask (final Task task)
throws PersistenceException
{
execute(new Operation() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
throws PersistenceException, SQLException
{
_ttable.update(task);
return null;
}
});
}
protected Table _ttable;
}
@@ -0,0 +1,38 @@
//
// $Id: UserLogic.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue.logic;
import com.samskivert.servlet.user.User;
import com.samskivert.velocity.Application;
import com.samskivert.velocity.InvocationContext;
import com.samskivert.velocity.Logic;
import com.samskivert.twodue.TwoDueApp;
/**
* A base logic class for pages that require an authenticated user.
*/
public abstract class UserLogic implements Logic
{
/**
* Logic classes should implement this method to perform their normal
* duties.
*
* @param ctx the context in which the request is being invoked.
* @param app the web application.
* @param user the user record for the authenticated user.
*/
public abstract void invoke (
InvocationContext ctx, TwoDueApp app, User user)
throws Exception;
// documentation inherited from interface
public void invoke (Application app, InvocationContext ctx)
throws Exception
{
TwoDueApp tdapp = (TwoDueApp)app;
User user = tdapp.getUserManager().requireUser(ctx.getRequest());
invoke(ctx, tdapp, user);
}
}
@@ -0,0 +1,164 @@
//
// $Id: index.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
package com.samskivert.twodue.logic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import com.samskivert.util.QuickSort;
import com.samskivert.util.StringUtil;
import com.samskivert.servlet.RedirectException;
import com.samskivert.servlet.user.User;
import com.samskivert.servlet.util.ParameterUtil;
import com.samskivert.velocity.InvocationContext;
import com.samskivert.twodue.Log;
import com.samskivert.twodue.TwoDueApp;
import com.samskivert.twodue.data.Task;
/**
* Displays a summary out outstanding and completed tasks.
*/
public class index extends UserLogic
{
public void invoke (InvocationContext ctx, TwoDueApp app, User user)
throws Exception
{
HttpServletRequest req = ctx.getRequest();
// display any message we've been asked to display
String msg = ParameterUtil.getParameter(req, "message", true);
if (!StringUtil.blank(msg)) {
ctx.put("error", msg);
}
// we use this to determine whether to show "complete" buttons for
// tasks
ctx.put("username", user.username);
// if they've submitted the form, we create a new task and stick
// it into the dataabse
if (ParameterUtil.parameterEquals(
ctx.getRequest(), "action", "create")) {
// set the creator from the username of the calling user
Task task = new Task();
task.creator = user.username;
// parse our fields
task.summary = ParameterUtil.requireParameter(
ctx.getRequest(), "summary", "index.error.missing_summary");
task.category = ParameterUtil.requireParameter(
ctx.getRequest(), "category",
"index.error.missing_category");
task.complexity = ParameterUtil.requireParameter(
ctx.getRequest(), "complexity",
"index.error.missing_complexity");
task.priority = ParameterUtil.requireIntParameter(
ctx.getRequest(), "priority",
"index.error.invalid_priority");
// insert the task into the repository
app.getRepository().createTask(task);
// flip back to this same page minus our query parameters to
// clear out the creation form
throw new RedirectException(
"index.wm?msg=index.message.task_created");
} else if (ParameterUtil.parameterEquals(
ctx.getRequest(), "action", "complete")) {
int taskId = ParameterUtil.requireIntParameter(
ctx.getRequest(), "task", "index.error.missing_taskid");
app.getRepository().completeTask(taskId, user.username);
// let the user know we updated the database
ctx.put("error", "index.message.task_completed");
} else if (ParameterUtil.parameterEquals(
ctx.getRequest(), "action", "claim")) {
int taskId = ParameterUtil.requireIntParameter(
ctx.getRequest(), "task", "index.error.missing_taskid");
app.getRepository().claimTask(taskId, user.username);
// let the user know we updated the database
ctx.put("error", "index.message.task_claimed");
}
// load up outstanding tasks and break them down by complexity
String expand = ParameterUtil.getParameter(req, "expand", false);
ArrayList tasks = app.getRepository().loadTasks();
CatList[] xtasks = categorize(tasks, expand, new Categorizer() {
public String category (Task task) {
return task.complexity;
}
});
ctx.put("xtasks", xtasks);
// load up owned tasks and break them down by owner
tasks = app.getRepository().loadOwnedTasks();
CatList[] otasks = categorize(tasks, null, new Categorizer() {
public String category (Task task) {
return task.owner;
}
});
ctx.put("otasks", otasks);
// load up recently completed tasks
tasks = app.getRepository().loadCompletedTasks(0, 6);
ctx.put("dtasks", tasks);
}
protected static class CatList
implements Comparable
{
public String name;
public ArrayList tasks;
public int pruned;
public int compareTo (Object other)
{
return name.compareTo(((CatList)other).name);
}
}
protected static interface Categorizer
{
public String category (Task task);
}
protected CatList[] categorize (
ArrayList tasks, String expand, Categorizer catter)
{
HashMap cmap = new HashMap();
int tcount = tasks.size();
for (int ii = 0; ii < tcount; ii++) {
Task task = (Task)tasks.get(ii);
String category = catter.category(task);
CatList clist = (CatList)cmap.get(category);
if (clist == null) {
clist = new CatList();
clist.name = category;
clist.tasks = new ArrayList();
cmap.put(category, clist);
}
if (expand == null || clist.tasks.size() < 2 ||
(expand.equals(category))) {
clist.tasks.add(task);
} else {
clist.pruned++;
}
}
CatList[] ctasks = new CatList[cmap.size()];
Iterator iter = cmap.values().iterator();
for (int ii = 0; iter.hasNext(); ii++) {
ctasks[ii] = (CatList)iter.next();
}
QuickSort.sort(ctasks);
return ctasks;
}
}
+72
View File
@@ -0,0 +1,72 @@
/**
* $Id: tasks-schema.sql,v 1.1 2002/11/08 09:14:21 mdb Exp $
*
* Schema for the Two Due tasks and notes table.
*/
drop table if exists TASKS;
/**
* Contains basic data for every task in the system.
*/
CREATE TABLE TASKS
(
/**
* A unique identifier for this task.
*/
TASK_ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
/**
* The summary description of this task.
*/
SUMMARY VARCHAR(255) NOT NULL,
/**
* The comma separated list of categories occupied by this task.
*/
CATEGORY VARCHAR(255) NOT NULL,
/**
* The complexity identifier for this task.
*/
COMPLEXITY VARCHAR(32) NOT NULL,
/**
* The priority of this task.
*/
PRIORITY INTEGER UNSIGNED NOT NULL,
/**
* The user to which this task is currently assigned.
*/
OWNER VARCHAR(255),
/**
* The name of the creator of this task.
*/
CREATOR VARCHAR(255) NOT NULL,
/**
* The time of creation of this task.
*/
CREATION DATE NOT NULL,
/**
* The name of the completor(s) of this task.
*/
COMPLETOR VARCHAR(255),
/**
* The time of completion of this task.
*/
COMPLETION DATE,
/**
* Defines our table keys.
*/
PRIMARY KEY (TASK_ID),
KEY (CATEGORY),
KEY (PRIORITY),
KEY (CREATION),
KEY (COMPLETION)
);