/** * */ package edu.harvard.econcs.turkserver.server.mysql; import java.net.InetAddress; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.List; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.AbstractListHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.apache.commons.dbutils.handlers.ColumnListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; import edu.harvard.econcs.turkserver.SessionExpiredException; import edu.harvard.econcs.turkserver.schema.Session; import edu.harvard.econcs.turkserver.server.SessionRecord; import edu.harvard.econcs.turkserver.server.SessionRecord.SessionStatus; /** * @author mao * */ @Deprecated public class SimpleMySQLDataTracker extends SimpleDataTracker { private final String setID; private final QueryRunner qr; static final ScalarHandler defaultScalarHandler = new ScalarHandler(); static final ArrayListHandler defaultArrayListHandler = new ArrayListHandler(); static final ColumnListHandler defaultColumnListHandler = new ColumnListHandler(); static final StringListHandler defaultIdHandler = new StringListHandler("hitId"); static final StringListHandler defaultDataHandler = new StringListHandler("data"); static final AbstractListHandler<Session> sessionHandler = new AbstractListHandler<Session>() { @Override protected Session handleRow(ResultSet rs) throws SQLException { // TODO remove all this stuff return null; } }; public SimpleMySQLDataTracker(MysqlConnectionPoolDataSource ds, String setID, int simultaneousSessionLimit, int totalSetLimit) { super(simultaneousSessionLimit, totalSetLimit); this.setID = setID; this.qr = new QueryRunner(ds); } /** * Creates a new schema for turk experiments * * Session ID 40 characters * HIT ID 30 characters * Assignment ID 30 characters * Worker ID 14 characters * * username 40 characters * * @param dropOld whether to drop the old database */ public void createSchema(boolean dropOld) throws SQLException { String query; if( dropOld ) { query = "DROP TABLE IF EXISTS session, sets, worker"; debugUpdate(query); } // Workers table query = "CREATE TABLE IF NOT EXISTS worker (" + "id VARCHAR(14) NOT NULL PRIMARY KEY," + "notify ENUM('off', 'on')" + ") ENGINE = InnoDB"; debugUpdate(query); // Sets table query = "CREATE TABLE IF NOT EXISTS sets (" + "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + "name VARCHAR(24) NOT NULL UNIQUE KEY," + "descript TEXT" + ") ENGINE = InnoDB"; debugUpdate(query); // Make sure set exists already for FK constraints query = "INSERT IGNORE INTO sets (name) VALUES (?)"; debugUpdate(query, setID); // Session table - use innodb for row locking query = "CREATE TABLE IF NOT EXISTS session (" + "hitId VARCHAR(30) NOT NULL PRIMARY KEY," + "setId VARCHAR(24)," + "data VARCHAR(24)," + "assignmentId VARCHAR(30) UNIQUE," + "workerId VARCHAR(14)," + "joinTime TIMESTAMP DEFAULT 0," + "ipAddr VARCHAR(16)," + "inactivePercent DOUBLE," + "paid DECIMAL(10,2)," + "bonusPaid DECIMAL(10,2)," + "hitStatus VARCHAR(16)," + "results TEXT," + "comment TEXT," + "FOREIGN KEY(setId) REFERENCES sets(name) ON UPDATE CASCADE," + "FOREIGN KEY(workerId) REFERENCES worker(id)" + ") ENGINE = InnoDB"; debugUpdate(query); logger.info("Created new schema"); } private void debugUpdate(String query, Object... params) throws SQLException { System.out.println(query); System.out.println(qr.update(query, params) + " rows updated"); } @Override public boolean sessionExistsInDB(String hitID) throws SessionExpiredException { List<Object> results = null; try { results = qr.query("SELECT hitStatus FROM session WHERE hitId=?", defaultColumnListHandler, hitID); } catch (SQLException e) { e.printStackTrace(); } // Check if this session has a result already, if so then it's expired if( results != null && results.size() > 0) { Object obj = results.get(0); if( obj != null && obj.toString().equals("EXPIRED")) throw new SessionExpiredException(); return true; } else return false; } @Override public boolean sessionCompletedInDB(String hitID) { Session sr = getStoredSessionInfo(hitID); return (sr != null && (SessionRecord.status(sr) == SessionStatus.COMPLETED)); } @Override public List<Session> getSetSessionInfoForWorker(String workerId) { List<Session> results = null; try { // This should be quick because workerId is indexed as a foreign key results = qr.query("SELECT * FROM session WHERE workerId=? AND setId=?", sessionHandler, workerId, setID); } catch (SQLException e) { e.printStackTrace(); } return results; } @Override public List<Object[]> getWorkerAndTotalDataCounts(String workerId) { try { List<Object[]> dataObj = qr.query( "SELECT data, " + "SUM(CASE WHEN isNull(workerId) THEN 0 ELSE workerId=? END) as sum, " + "COUNT(*) as ct FROM session " + "WHERE setId=? AND data IS NOT NULL " + "GROUP BY data " + "ORDER BY ct ASC", defaultArrayListHandler, workerId, setID); return dataObj; } catch (SQLException e) { e.printStackTrace(); } return null; } @Override public Session getStoredSessionInfo(String hitID) { List<Session> result = null; try { result = qr.query("SELECT * FROM session WHERE hitId=?", sessionHandler, hitID); } catch (SQLException e) { e.printStackTrace(); } // Return the first element if one exists return (result == null || result.size() == 0 ? null : result.get(0)); } @Override public void saveHITId(String hitId) { try { qr.update("INSERT INTO session (hitId, setId) VALUES (?, ?) " + "ON DUPLICATE KEY UPDATE setId=?", hitId, setID, setID); } catch (SQLException e) { e.printStackTrace(); } } @Override public void saveAssignmentForSession(String hitID, String assignmentId, String workerId) { try { // Make sure the worker table contains this workerId first, but ignore if already exists qr.update("INSERT IGNORE INTO worker(id) VALUES (?)", workerId); qr.update("UPDATE session SET assignmentId=?, workerId=? WHERE hitId=?", assignmentId, workerId, hitID); } catch (SQLException e) { e.printStackTrace(); } } @Override public void saveIPForSession(String hitID, InetAddress remoteAddress, Date lobbyTime) { try { qr.update("UPDATE session SET ipAddr=?, joinTime=? WHERE hitId=?", remoteAddress.getHostAddress(), lobbyTime, hitID); } catch (SQLException e) { e.printStackTrace(); } } @Override public void setSessionData(String hitId, String data) { try { qr.update("UPDATE session SET data=? WHERE hitId=?", data, hitId); } catch (SQLException e) { e.printStackTrace(); } } @Override public void saveSessionLog(String hitID, String data) { try { qr.update("UPDATE session SET results=?, inactivePercent=? WHERE hitId=?", data, 0.0, hitID); } catch (SQLException e) { e.printStackTrace(); } } @Override public List<Session> expireUnusedSessions() { List<Session> expired = null; // TODO replace this with better sql try { // Get the list first expired = qr.query( "SELECT * FROM session WHERE setId=? AND results IS NULL", sessionHandler, setID); // Now update qr.update("UPDATE session SET hitStatus='EXPIRED' WHERE setId=? AND results IS NULL", setID); logger.info("Found " + expired.size() + " unused sessions"); } catch (SQLException e) { e.printStackTrace(); } return expired; } }