Skip to content

Commit

Permalink
Merge pull request codinguser#680 from rivaldi8/bugfix-641-exports-we…
Browse files Browse the repository at this point in the history
…ekday-ignored

Bugfix 641: Weekday is ignored in scheduled exports
  • Loading branch information
codinguser authored Apr 27, 2017
2 parents 8087978 + 0a5af15 commit 893132a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 8 deletions.
40 changes: 39 additions & 1 deletion app/src/main/java/org/gnucash/android/model/ScheduledAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
Expand Down Expand Up @@ -224,7 +225,7 @@ private long computeNextScheduledExecutionTimeStartingAt(long startTime) {
nextScheduledExecution = nextScheduledExecution.plusDays(multiplier);
break;
case WEEK:
nextScheduledExecution = nextScheduledExecution.plusWeeks(multiplier);
nextScheduledExecution = computeNextWeeklyExecutionStartingAt(nextScheduledExecution);
break;
case MONTH:
nextScheduledExecution = nextScheduledExecution.plusMonths(multiplier);
Expand All @@ -236,6 +237,43 @@ private long computeNextScheduledExecutionTimeStartingAt(long startTime) {
return nextScheduledExecution.toDate().getTime();
}

/**
* Computes the next time that this weekly scheduled action is supposed to be
* executed starting at startTime.
*
* @param startTime LocalDateTime to use as start to compute the next schedule.
*
* @return Next run time as a LocalDateTime
*/
@NonNull
private LocalDateTime computeNextWeeklyExecutionStartingAt(LocalDateTime startTime) {
// Look into the week of startTime for another scheduled weekday
for (int weekDay : mRecurrence.getByDays() ) {
int jodaWeekDay = convertCalendarWeekdayToJoda(weekDay);
LocalDateTime candidateNextDueTime = startTime.withDayOfWeek(jodaWeekDay);
if (candidateNextDueTime.isAfter(startTime))
return candidateNextDueTime;
}

// Return the first scheduled weekday from the next due week
int firstScheduledWeekday = convertCalendarWeekdayToJoda(mRecurrence.getByDays().get(0));
return startTime.plusWeeks(mRecurrence.getMultiplier())
.withDayOfWeek(firstScheduledWeekday);
}

/**
* Converts a java.util.Calendar weekday constant to the
* org.joda.time.DateTimeConstants equivalent.
*
* @param calendarWeekday weekday constant from java.util.Calendar
* @return weekday constant equivalent from org.joda.time.DateTimeConstants
*/
private int convertCalendarWeekdayToJoda(int calendarWeekday) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, calendarWeekday);
return LocalDateTime.fromCalendarFields(cal).getDayOfWeek();
}

/**
* Set time of last execution of the scheduled action
* @param nextRun Timestamp in milliseconds since Epoch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
import org.junit.Test;

import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;


/**
* Test scheduled actions
*/
Expand Down Expand Up @@ -130,6 +134,48 @@ public void testComputingTimeOfLastSchedule(){

}

/**
* Weekly actions scheduled to run on multiple weekdays should be due
* in each of them in the same week.
*
* For an action scheduled on Mondays and Thursdays, we test that, if
* the last run was on Monday, the next should be due on the Thursday
* of the same week instead of the following week.
*/
@Test
public void multiWeekdayWeeklyActions_shouldBeDueOnEachWeekdaySet() {
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setByDays(Arrays.asList(Calendar.MONDAY, Calendar.THURSDAY));
scheduledAction.setRecurrence(recurrence);
scheduledAction.setStartTime(new DateTime(2016, 6, 6, 9, 0).getMillis());
scheduledAction.setLastRun(new DateTime(2017, 4, 17, 9, 0).getMillis()); // Monday

long expectedNextDueDate = new DateTime(2017, 4, 20, 9, 0).getMillis(); // Thursday
assertThat(scheduledAction.computeNextTimeBasedScheduledExecutionTime())
.isEqualTo(expectedNextDueDate);
}

/**
* Weekly actions scheduled with multiplier should skip intermediate
* weeks and be due in the specified weekday.
*/
@Test
public void weeklyActionsWithMultiplier_shouldBeDueOnTheWeekdaySet() {
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(2);
recurrence.setByDays(Collections.singletonList(Calendar.WEDNESDAY));
scheduledAction.setRecurrence(recurrence);
scheduledAction.setStartTime(new DateTime(2016, 6, 6, 9, 0).getMillis());
scheduledAction.setLastRun(new DateTime(2017, 4, 12, 9, 0).getMillis()); // Wednesday

// Wednesday, 2 weeks after the last run
long expectedNextDueDate = new DateTime(2017, 4, 26, 9, 0).getMillis();
assertThat(scheduledAction.computeNextTimeBasedScheduledExecutionTime())
.isEqualTo(expectedNextDueDate);
}

private long getTimeInMillis(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, month, day);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.gnucash.android.util.BookUtils;
import org.gnucash.android.util.TimestampHelper;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.LocalDateTime;
import org.joda.time.Weeks;
import org.junit.After;
Expand All @@ -63,6 +64,8 @@
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -199,8 +202,10 @@ public void missedScheduledTransactions_shouldBeGenerated(){

scheduledAction.setActionUID(mActionUID);

int multiplier = 2;
scheduledAction.setRecurrence(PeriodType.WEEK, multiplier);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(2);
recurrence.setByDays(Collections.singletonList(Calendar.MONDAY));
scheduledAction.setRecurrence(recurrence);
ScheduledActionDbAdapter.getInstance().addRecord(scheduledAction, DatabaseAdapter.UpdateMethod.insert);

TransactionsDbAdapter transactionsDbAdapter = TransactionsDbAdapter.getInstance();
Expand Down Expand Up @@ -249,7 +254,10 @@ public void scheduledTransactionsWithEndTimeInPast_shouldBeExecuted(){
scheduledAction.setStartTime(startTime.getMillis());
scheduledAction.setActionUID(mActionUID);

scheduledAction.setRecurrence(PeriodType.WEEK, 2);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(2);
recurrence.setByDays(Collections.singletonList(Calendar.MONDAY));
scheduledAction.setRecurrence(recurrence);
scheduledAction.setEndTime(new DateTime(2016, 8, 8, 9, 0).getMillis());
ScheduledActionDbAdapter.getInstance().addRecord(scheduledAction, DatabaseAdapter.UpdateMethod.insert);

Expand Down Expand Up @@ -345,11 +353,15 @@ public void scheduledBackups_shouldRunOnlyOnce(){
@Test
public void scheduledBackups_shouldNotRunBeforeNextScheduledExecution(){
ScheduledAction scheduledBackup = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
scheduledBackup.setStartTime(LocalDateTime.now().minusDays(2).toDate().getTime());
scheduledBackup.setStartTime(
LocalDateTime.now().withDayOfWeek(DateTimeConstants.WEDNESDAY).toDate().getTime());
scheduledBackup.setLastRun(scheduledBackup.getStartTime());
long previousLastRun = scheduledBackup.getLastRunTime();
scheduledBackup.setExecutionCount(1);
scheduledBackup.setRecurrence(PeriodType.WEEK, 1);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(1);
recurrence.setByDays(Collections.singletonList(Calendar.MONDAY));
scheduledBackup.setRecurrence(recurrence);

ExportParams backupParams = new ExportParams(ExportFormat.XML);
backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD);
Expand Down Expand Up @@ -380,7 +392,10 @@ public void scheduledBackups_shouldNotIncludeTransactionsPreviousToTheLastRun()
scheduledBackup.setLastRun(LocalDateTime.now().minusDays(8).toDate().getTime());
long previousLastRun = scheduledBackup.getLastRunTime();
scheduledBackup.setExecutionCount(1);
scheduledBackup.setRecurrence(PeriodType.WEEK, 1);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(1);
recurrence.setByDays(Collections.singletonList(Calendar.WEDNESDAY));
scheduledBackup.setRecurrence(recurrence);
ExportParams backupParams = new ExportParams(ExportFormat.QIF);
backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD);
backupParams.setExportStartTime(new Timestamp(scheduledBackup.getStartTime()));
Expand Down Expand Up @@ -438,7 +453,10 @@ public void scheduledBackups_shouldIncludeTransactionsAfterTheLastRun() {
scheduledBackup.setLastRun(LocalDateTime.now().minusDays(8).toDate().getTime());
long previousLastRun = scheduledBackup.getLastRunTime();
scheduledBackup.setExecutionCount(1);
scheduledBackup.setRecurrence(PeriodType.WEEK, 1);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(1);
recurrence.setByDays(Collections.singletonList(Calendar.FRIDAY));
scheduledBackup.setRecurrence(recurrence);
ExportParams backupParams = new ExportParams(ExportFormat.QIF);
backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD);
backupParams.setExportStartTime(new Timestamp(scheduledBackup.getStartTime()));
Expand Down

0 comments on commit 893132a

Please sign in to comment.