package com.vorsk.crossfitr.models; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; /** * DAO for "workout_session" table. * * Create a new instance and use the methods to interact with the database. * Data is returned as instances of WorkoutSessionRow where each column * is a publicly accessible property. * * @author Vivek * @since 1.0 */ public class WorkoutSessionModel extends SQLiteDAO { //// Constants // Table-specific columns public static final String COL_WORKOUT = "workout_id"; public static final String COL_SCORE = "score"; public static final String COL_SCORE_TYPE = "score_type_id"; public static final String COL_CMNT = "comments"; private Context context; /***** Constructors *****/ /** * Init SQLiteDAO with table "workout_session" * * @param ctx In the example they passed "this" from the calling class.. * I'm not really sure what this is yet. */ public WorkoutSessionModel(Context ctx) { super("workout_session", ctx); context = ctx; } /***** Private *****/ /** * Utility method to grab all the rows from a cursor * * @param cr result of a query * @return Array of entries */ private WorkoutSessionRow[] fetchWorkoutSessionRows(Cursor cr) { if (cr == null) { return null; } WorkoutSessionRow[] result = new WorkoutSessionRow[cr.getCount()]; if (result.length == 0) { cr.close(); return result; } boolean valid = cr.moveToFirst(); int ii = 0; // Grab the cursor's column indices // An error here indicates the COL constants aren't synced with the DB int ind_id = cr.getColumnIndexOrThrow(COL_ID); int ind_wid = cr.getColumnIndexOrThrow(COL_WORKOUT); int ind_score = cr.getColumnIndexOrThrow(COL_SCORE); int ind_stid = cr.getColumnIndexOrThrow(COL_SCORE_TYPE); int ind_dm = cr.getColumnIndexOrThrow(COL_MDATE); int ind_dc = cr.getColumnIndexOrThrow(COL_CDATE); int ind_cmnt = cr.getColumnIndexOrThrow(COL_CMNT); // Iterate over every row (move the cursor down the set) while (valid) { result[ii] = new WorkoutSessionRow(); fetchBaseData(cr, result[ii], ind_id, ind_dm, ind_dc); result[ii].workout_id = cr.getLong(ind_wid); result[ii].score = cr.getInt(ind_score); result[ii].score_type_id = cr.getLong(ind_stid); result[ii].comments = cr.getString(ind_cmnt); valid = cr.moveToNext(); ii ++; } cr.close(); return result; } /** * Update the workout entry to reflect the new record * * @param workout_id ID of the workout * @param score The new session's score */ private void checkUpdateRecord(long workout_id, int score) { WorkoutModel model = new WorkoutModel(context); WorkoutRow workout = model.getByID(workout_id); boolean update = false; // Check if this session is the new best record switch ((int)workout.record_type_id) { case SCORE_TIME: if (workout.record == 0 || score < workout.record) { workout.record = score; update = true; } break; case SCORE_REPS: case SCORE_WEIGHT: if (workout.record == 0 || score > workout.record) { workout.record = score; update = true; } break; } // If so, update the workout if (update) { model.edit(workout); } } /***** Public *****/ /** * Inserts a new entry into the workout table * * @param row Add this entry to the DB * @return ID of newly added entry, -1 on failure */ public long insert(WorkoutSessionRow row) { checkUpdateRecord(row.workout_id, row.score); // Insert this entry return super.insert(row.toContentValues()); } /** * Inserts a new entry into the workout table, defaults record to 0 * * @param workout ID of the workout performed this session * @param score The entry's score (time, reps, etc) or this.NOT_SCORED * @param score_type Type of the score (this.SCORE_TIME, etc) or * this.SCORE_NONE if no score is recorded * @return ID of newly added entry, -1 on failure */ public long insert(long workout, long score, long score_type) { Integer isc = (score == NOT_SCORED) ? null : (int)score; Long ist = (score_type == SCORE_NONE) ? null : score_type; if (isc != null) { checkUpdateRecord(workout, isc); } ContentValues cv = new ContentValues(); cv.put(COL_WORKOUT, workout); cv.put(COL_SCORE, isc); cv.put(COL_SCORE_TYPE, ist); return super.insert(cv); } /** * Change the comment of a session * * @param id ID of the comment to edit * @param comment New comment; overwrites existing comment * @return 1 on success, -1 on failure, 0 if invalid ID */ public int editComment(long id, String comment) { if (comment == null) comment = ""; ContentValues cv = new ContentValues(); cv.put(COL_CMNT, comment); return super.update(cv, COL_ID + " = " + id); } /** * Removes all sessions of a particular workout * * @param id ID of the workout whose history to remove * @return Number of sessions removed */ public int deleteWorkoutHistory(long id) { return super.delete(COL_WORKOUT + " = " + id); } /** * Remove a previously created session * * Currently used by ResultsActivity is you don't want to save. This * should be cleaned up so this method can be removed. * * @param id session_id to delete * @return result of deletion, -1 on failure */ public int delete(long id) { WorkoutModel model = new WorkoutModel(context); WorkoutSessionRow session = getByID(id); if (session == null) { return 0; } WorkoutRow workout = model.getByID(session.workout_id); int result = super.delete(COL_ID + " = " + id); if (workout.record == session.score) { model.calculateRecord(workout._id, workout.record_type_id); } return result; } /** * Fetch an entry via the ID * * @param id * @return Associated entry or NULL on failure */ public WorkoutSessionRow getByID(long id) { Cursor cr = selectByID(id); if (cr.getCount() > 1) { return null; // TODO: Throw exception } WorkoutSessionRow[] rows = fetchWorkoutSessionRows(cr); return (rows.length == 0) ? null : rows[0]; } /** * Fetch all workouts within a given time period * * @param mintime Beginning time of interval (unix timestamp) * @param maxtime End time of interval (unix timestamp) * @return Sessions within the time period; NULL on failure */ public WorkoutSessionRow[] getByTime(long mintime, long maxtime) { String sql = "SELECT * FROM " + DB_TABLE + " WHERE " + COL_CDATE + "> ? AND " + COL_CDATE + "< ?"; Cursor cr = db.rawQuery(sql, new String[] { Long.toString(mintime), Long.toString(maxtime) }); return fetchWorkoutSessionRows(cr); } /** * Fetch all workouts within a given time period of a given type * * @param mintime Beginning time of interval (unix timestamp) * @param maxtime End time of interval (unix timestamp) * @param type Workout type; use constants (TYPE_GIRL, etc) */ public WorkoutSessionRow[] getByTime(long mintime, long maxtime, int type) { String sql = "SELECT * FROM " + DB_TABLE + " ws WHERE " + COL_CDATE + "> ? AND " + COL_CDATE + "< ? AND " + "(SELECT " + WorkoutModel.COL_WK_TYPE + " FROM workout WHERE " + COL_ID + "=ws." + COL_WORKOUT + ") = ?"; Cursor cr = db.rawQuery(sql, new String[] { String.valueOf(mintime), String.valueOf(maxtime), String.valueOf(type) }); return fetchWorkoutSessionRows(cr); } /** * Fetch all workout sessions by type * * @param type Workout type; use constants (TYPE_GIRL, etc) * @return Sessions of that workout type; null on failure */ public WorkoutSessionRow[] getByType(int type) { String sql = "SELECT * FROM " + DB_TABLE + " ws WHERE " + "(SELECT " + WorkoutModel.COL_WK_TYPE + " FROM workout WHERE " + COL_ID + "=ws." + COL_WORKOUT + ") = ?"; Cursor cr = db.rawQuery(sql, new String[] { String.valueOf(type) }); return fetchWorkoutSessionRows(cr); } /** * Fetch sessions of a particular workout * * @param id ID of the workout; obtain from WorkoutModel * @return Sessions of that workout; null on failure */ public WorkoutSessionRow[] getByWorkout(long id) { String col[] = { COL_WORKOUT }; String val[] = { String.valueOf(id) }; Cursor cr = select(col, val); return fetchWorkoutSessionRows(cr); } /** * Gets the total number of sessions performed * * @return Total sessions */ public int getTotal() { return selectCount(null, null); } /** * Get the most recent session * * @type Workout type, or NULL to search all sessions * @return Most recently created session; NULL on failure */ public WorkoutSessionRow getMostRecent(Integer type) { String[] col = (type == null) ? null : new String[1]; String[] val = (type == null) ? null : new String[1]; if (type != null) { col[0] = WorkoutModel.COL_WK_TYPE; val[0] = type.toString(); } String order = COL_CDATE + " DESC"; Cursor cr = select(col, val, order, 1); WorkoutSessionRow[] rows = fetchWorkoutSessionRows(cr); if (rows == null || rows.length < 1) { return null; } return rows[0]; } /** * Gets an aggregate value of the scores for a workout * * @param id Workout ID to calculate the scores * @param agg Type of aggregation (MIN, MAX, COUNT) * @return Calculated value */ public int getWorkoutAggScore(long id, String agg) { if (agg != "MIN" && agg != "MAX" && agg != "COUNT") return -1; String sql = "SELECT " + agg + "(" + COL_SCORE + ") as agg FROM " + DB_TABLE + " WHERE " + COL_WORKOUT + "=" + id; Cursor cr = db.rawQuery(sql, null); if (cr == null || !cr.moveToFirst()) { return -1; } int result = cr.getInt(cr.getColumnIndexOrThrow("agg")); cr.close(); return result; } }