/* * Copyright (c) 2014 - 2015 Ngewi Fet <ngewif@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gnucash.android.db.adapter; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteStatement; import android.support.annotation.NonNull; import android.util.Log; import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.model.Recurrence; import org.gnucash.android.model.ScheduledAction; import java.util.ArrayList; import java.util.List; import static org.gnucash.android.db.DatabaseSchema.ScheduledActionEntry; /** * Database adapter for fetching/saving/modifying scheduled events * * @author Ngewi Fet <ngewif@gmail.com> */ public class ScheduledActionDbAdapter extends DatabaseAdapter<ScheduledAction> { private RecurrenceDbAdapter mRecurrenceDbAdapter; public ScheduledActionDbAdapter(SQLiteDatabase db, RecurrenceDbAdapter recurrenceDbAdapter){ super(db, ScheduledActionEntry.TABLE_NAME, new String[]{ ScheduledActionEntry.COLUMN_ACTION_UID , ScheduledActionEntry.COLUMN_TYPE , ScheduledActionEntry.COLUMN_START_TIME , ScheduledActionEntry.COLUMN_END_TIME , ScheduledActionEntry.COLUMN_LAST_RUN , ScheduledActionEntry.COLUMN_ENABLED , ScheduledActionEntry.COLUMN_CREATED_AT , ScheduledActionEntry.COLUMN_TAG , ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY , ScheduledActionEntry.COLUMN_RECURRENCE_UID , ScheduledActionEntry.COLUMN_AUTO_CREATE , ScheduledActionEntry.COLUMN_AUTO_NOTIFY , ScheduledActionEntry.COLUMN_ADVANCE_CREATION , ScheduledActionEntry.COLUMN_ADVANCE_NOTIFY , ScheduledActionEntry.COLUMN_TEMPLATE_ACCT_UID , ScheduledActionEntry.COLUMN_EXECUTION_COUNT }); mRecurrenceDbAdapter = recurrenceDbAdapter; LOG_TAG = "ScheduledActionDbAdapter"; } /** * Returns application-wide instance of database adapter * @return ScheduledEventDbAdapter instance */ public static ScheduledActionDbAdapter getInstance(){ return GnuCashApplication.getScheduledEventDbAdapter(); } @Override public void addRecord(@NonNull ScheduledAction scheduledAction, UpdateMethod updateMethod) { mRecurrenceDbAdapter.addRecord(scheduledAction.getRecurrence(), updateMethod); super.addRecord(scheduledAction, updateMethod); } @Override public long bulkAddRecords(@NonNull List<ScheduledAction> scheduledActions, UpdateMethod updateMethod) { List<Recurrence> recurrenceList = new ArrayList<>(scheduledActions.size()); for (ScheduledAction scheduledAction : scheduledActions) { recurrenceList.add(scheduledAction.getRecurrence()); } //first add the recurrences, they have no dependencies (foreign key constraints) long nRecurrences = mRecurrenceDbAdapter.bulkAddRecords(recurrenceList, updateMethod); Log.d(LOG_TAG, String.format("Added %d recurrences for scheduled actions", nRecurrences)); return super.bulkAddRecords(scheduledActions, updateMethod); } /** * Updates only the recurrence attributes of the scheduled action. * The recurrence attributes are the period, start time, end time and/or total frequency. * All other properties of a scheduled event are only used for internal database tracking and are * not central to the recurrence schedule. * <p><b>The GUID of the scheduled action should already exist in the database</b></p> * @param scheduledAction Scheduled action * @return Database record ID of the edited scheduled action */ public long updateRecurrenceAttributes(ScheduledAction scheduledAction){ //since we are updating, first fetch the existing recurrence UID and set it to the object //so that it will be updated and not a new one created RecurrenceDbAdapter recurrenceDbAdapter = new RecurrenceDbAdapter(mDb); String recurrenceUID = recurrenceDbAdapter.getAttribute(scheduledAction.getUID(), ScheduledActionEntry.COLUMN_RECURRENCE_UID); Recurrence recurrence = scheduledAction.getRecurrence(); recurrence.setUID(recurrenceUID); recurrenceDbAdapter.addRecord(recurrence, UpdateMethod.update); ContentValues contentValues = new ContentValues(); extractBaseModelAttributes(contentValues, scheduledAction); contentValues.put(ScheduledActionEntry.COLUMN_START_TIME, scheduledAction.getStartTime()); contentValues.put(ScheduledActionEntry.COLUMN_END_TIME, scheduledAction.getEndTime()); contentValues.put(ScheduledActionEntry.COLUMN_TAG, scheduledAction.getTag()); contentValues.put(ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY, scheduledAction.getTotalPlannedExecutionCount()); Log.d(LOG_TAG, "Updating scheduled event recurrence attributes"); String where = ScheduledActionEntry.COLUMN_UID + "=?"; String[] whereArgs = new String[]{scheduledAction.getUID()}; return mDb.update(ScheduledActionEntry.TABLE_NAME, contentValues, where, whereArgs); } @Override protected @NonNull SQLiteStatement setBindings(@NonNull SQLiteStatement stmt, @NonNull final ScheduledAction schedxAction) { stmt.clearBindings(); stmt.bindString(1, schedxAction.getActionUID()); stmt.bindString(2, schedxAction.getActionType().name()); stmt.bindLong(3, schedxAction.getStartTime()); stmt.bindLong(4, schedxAction.getEndTime()); stmt.bindLong(5, schedxAction.getLastRunTime()); stmt.bindLong(6, schedxAction.isEnabled() ? 1 : 0); stmt.bindString(7, schedxAction.getCreatedTimestamp().toString()); if (schedxAction.getTag() == null) stmt.bindNull(8); else stmt.bindString(8, schedxAction.getTag()); stmt.bindString(9, Integer.toString(schedxAction.getTotalPlannedExecutionCount())); stmt.bindString(10, schedxAction.getRecurrence().getUID()); stmt.bindLong(11, schedxAction.shouldAutoCreate() ? 1 : 0); stmt.bindLong(12, schedxAction.shouldAutoNotify() ? 1 : 0); stmt.bindLong(13, schedxAction.getAdvanceCreateDays()); stmt.bindLong(14, schedxAction.getAdvanceNotifyDays()); stmt.bindString(15, schedxAction.getTemplateAccountUID()); stmt.bindString(16, Integer.toString(schedxAction.getExecutionCount())); stmt.bindString(17, schedxAction.getUID()); return stmt; } /** * Builds a {@link org.gnucash.android.model.ScheduledAction} instance from a row to cursor in the database. * The cursor should be already pointing to the right entry in the data set. It will not be modified in any way * @param cursor Cursor pointing to data set * @return ScheduledEvent object instance */ @Override public ScheduledAction buildModelInstance(@NonNull final Cursor cursor){ String actionUid = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ACTION_UID)); long startTime = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_START_TIME)); long endTime = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_END_TIME)); long lastRun = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_LAST_RUN)); String typeString = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TYPE)); String tag = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TAG)); boolean enabled = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ENABLED)) > 0; int numOccurrences = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY)); int execCount = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_EXECUTION_COUNT)); int autoCreate = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_AUTO_CREATE)); int autoNotify = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_AUTO_NOTIFY)); int advanceCreate = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ADVANCE_CREATION)); int advanceNotify = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ADVANCE_NOTIFY)); String recurrenceUID = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_RECURRENCE_UID)); String templateActUID = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TEMPLATE_ACCT_UID)); ScheduledAction event = new ScheduledAction(ScheduledAction.ActionType.valueOf(typeString)); populateBaseModelAttributes(cursor, event); event.setStartTime(startTime); event.setEndTime(endTime); event.setActionUID(actionUid); event.setLastRun(lastRun); event.setTag(tag); event.setEnabled(enabled); event.setTotalPlannedExecutionCount(numOccurrences); event.setExecutionCount(execCount); event.setAutoCreate(autoCreate == 1); event.setAutoNotify(autoNotify == 1); event.setAdvanceCreateDays(advanceCreate); event.setAdvanceNotifyDays(advanceNotify); //TODO: optimize by doing overriding fetchRecord(String) and join the two tables event.setRecurrence(mRecurrenceDbAdapter.getRecord(recurrenceUID)); event.setTemplateAccountUID(templateActUID); return event; } /** * Returns all {@link org.gnucash.android.model.ScheduledAction}s from the database with the specified action UID. * Note that the parameter is not of the the scheduled action record, but from the action table * @param actionUID GUID of the event itself * @return List of ScheduledEvents */ public List<ScheduledAction> getScheduledActionsWithUID(@NonNull String actionUID){ Cursor cursor = mDb.query(ScheduledActionEntry.TABLE_NAME, null, ScheduledActionEntry.COLUMN_ACTION_UID + "= ?", new String[]{actionUID}, null, null, null); List<ScheduledAction> scheduledActions = new ArrayList<>(); try { while (cursor.moveToNext()) { scheduledActions.add(buildModelInstance(cursor)); } } finally { cursor.close(); } return scheduledActions; } /** * Returns all enabled scheduled actions in the database * @return List of enabled scheduled actions */ public List<ScheduledAction> getAllEnabledScheduledActions(){ Cursor cursor = mDb.query(mTableName, null, ScheduledActionEntry.COLUMN_ENABLED + "=1", null, null, null, null); List<ScheduledAction> scheduledActions = new ArrayList<>(); while (cursor.moveToNext()){ scheduledActions.add(buildModelInstance(cursor)); } return scheduledActions; } /** * Returns the number of instances of the action which have been created from this scheduled action * @param scheduledActionUID GUID of scheduled action * @return Number of transactions created from scheduled action */ public long getActionInstanceCount(String scheduledActionUID) { String sql = "SELECT COUNT(*) FROM " + DatabaseSchema.TransactionEntry.TABLE_NAME + " WHERE " + DatabaseSchema.TransactionEntry.COLUMN_SCHEDX_ACTION_UID + "=?"; SQLiteStatement statement = mDb.compileStatement(sql); statement.bindString(1, scheduledActionUID); return statement.simpleQueryForLong(); } }