Merge pull request #173 from JotraN/dev

Add exporting scores and checkmarks files with multiple habits (#68).
pull/175/head
Alinson S. Xavier 9 years ago committed by GitHub
commit dc5d7930a6

@ -75,6 +75,8 @@ public class HabitsCSVExporterTest extends BaseAndroidTest
assertPathExists("001 Wake up early/Scores.csv");
assertPathExists("002 Meditate/Checkmarks.csv");
assertPathExists("002 Meditate/Scores.csv");
assertPathExists("Checkmarks.csv");
assertPathExists("Scores.csv");
}
private void assertAbsolutePathExists(String s)

@ -101,6 +101,30 @@ public class SQLiteScoreListTest extends BaseAndroidTest
assertThat(records.get(0).timestamp, equalTo(today));
}
@Test
public void testGetByInterval()
{
long from = today - 10 * day;
long to = today - 3 * day;
List<Score> list = scores.getByInterval(from, to);
assertThat(list.size(), equalTo(8));
assertThat(list.get(0).getTimestamp(), equalTo(today - 3 * day));
assertThat(list.get(3).getTimestamp(), equalTo(today - 6 * day));
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testGetByInterval_withLongInterval()
{
long from = today - 200 * day;
long to = today;
List<Score> list = scores.getByInterval(from, to);
assertThat(list.size(), equalTo(201));
}
private List<ScoreRecord> getAllRecords()
{
return new Select()
@ -109,5 +133,4 @@ public class SQLiteScoreListTest extends BaseAndroidTest
.orderBy("timestamp desc")
.execute();
}
}

@ -41,6 +41,10 @@ public class HabitsCSVExporter
private List<String> generateFilenames;
private String exportDirName;
/**
* Delimiter used in a CSV file.
*/
private final String DELIMITER = ",";
@NonNull
private final HabitList allHabits;
@ -102,16 +106,6 @@ public class HabitsCSVExporter
return s.substring(0, Math.min(s.length(), 100));
}
private void writeCheckmarks(String habitDirName, CheckmarkList checkmarks)
throws IOException
{
String filename = habitDirName + "Checkmarks.csv";
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
checkmarks.writeCSV(out);
out.close();
}
private void writeHabits() throws IOException
{
String filename = "Habits.csv";
@ -134,6 +128,8 @@ public class HabitsCSVExporter
writeScores(habitDirName, h.getScores());
writeCheckmarks(habitDirName, h.getCheckmarks());
}
writeMultipleHabits();
}
private void writeScores(String habitDirName, ScoreList scores)
@ -146,6 +142,118 @@ public class HabitsCSVExporter
out.close();
}
private void writeCheckmarks(String habitDirName, CheckmarkList checkmarks)
throws IOException
{
String filename = habitDirName + "Checkmarks.csv";
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
checkmarks.writeCSV(out);
out.close();
}
/**
* Writes a scores file and a checkmarks file containing scores and checkmarks of every habit.
* The first column corresponds to the date. Subsequent columns correspond to a habit.
* Habits are taken from the list of selected habits.
* Dates are determined from the oldest repetition date to the newest repetition date found in
* the list of habits.
*
* @throws IOException if there was problem writing the files
*/
private void writeMultipleHabits() throws IOException
{
String scoresFileName = "Scores.csv";
String checksFileName = "Checkmarks.csv";
generateFilenames.add(scoresFileName);
generateFilenames.add(checksFileName);
FileWriter scoresWriter = new FileWriter(exportDirName + scoresFileName);
FileWriter checksWriter = new FileWriter(exportDirName + checksFileName);
writeMultipleHabitsHeader(scoresWriter);
writeMultipleHabitsHeader(checksWriter);
long[] timeframe = getTimeframe();
long oldest = timeframe[0];
long newest = DateUtils.getStartOfToday();
List<int[]> checkmarks = new ArrayList<>();
List<int[]> scores = new ArrayList<>();
for (Habit h : selectedHabits)
{
checkmarks.add(h.getCheckmarks().getValues(oldest, newest));
scores.add(h.getScores().getValues(oldest, newest));
}
int days = DateUtils.getDaysBetween(oldest, newest);
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();
for (int i = 0; i <= days; i++)
{
Date day = new Date(newest - i * DateUtils.millisecondsInOneDay);
String date = dateFormat.format(day);
StringBuilder sb = new StringBuilder();
sb.append(date).append(DELIMITER);
checksWriter.write(sb.toString());
scoresWriter.write(sb.toString());
for(int j = 0; j < selectedHabits.size(); j++)
{
checksWriter.write(String.valueOf(checkmarks.get(j)[i]));
checksWriter.write(DELIMITER);
String score =
String.format("%.4f", ((float) scores.get(j)[i]) / Score.MAX_VALUE);
scoresWriter.write(score);
scoresWriter.write(DELIMITER);
}
checksWriter.write("\n");
scoresWriter.write("\n");
}
scoresWriter.close();
checksWriter.close();
}
/**
* Writes the first row, containing header information, using the given writer.
* This consists of the date title and the names of the selected habits.
*
* @param out the writer to use
* @throws IOException if there was a problem writing
*/
private void writeMultipleHabitsHeader(Writer out) throws IOException
{
out.write("Date" + DELIMITER);
for (Habit h : selectedHabits) {
out.write(h.getName());
out.write(DELIMITER);
}
out.write("\n");
}
/**
* Gets the overall timeframe of the selected habits.
* The timeframe is an array containing the oldest timestamp among the habits and the
* newest timestamp among the habits.
* Both timestamps are in milliseconds.
*
* @return the timeframe containing the oldest timestamp and the newest timestamp
*/
private long[] getTimeframe()
{
long oldest = Long.MAX_VALUE;
long newest = -1;
for (Habit h : selectedHabits)
{
if(h.getRepetitions().getOldest() == null || h.getRepetitions().getNewest() == null)
continue;
long currOld = h.getRepetitions().getOldest().getTimestamp();
long currNew = h.getRepetitions().getNewest().getTimestamp();
oldest = currOld > oldest ? oldest : currOld;
newest = currNew < newest ? newest : currNew;
}
return new long[]{oldest, newest};
}
private String writeZipFile() throws IOException
{
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();

@ -107,6 +107,16 @@ public abstract class RepetitionList
*/
@Nullable
public abstract Repetition getOldest();
@Nullable
/**
* Returns the newest repetition in the list.
* <p>
* If the list is empty, returns null. Repetitions in the past are
* discarded.
*
* @return newest repetition in the list, or null if list is empty.
*/
public abstract Repetition getNewest();
/**
* Returns the total number of repetitions for each month, from the first

@ -89,6 +89,46 @@ public abstract class ScoreList implements Iterable<Score>
return s.getValue();
}
/**
* Returns the list of scores that fall within the given interval.
* <p>
* There is exactly one score per day in the interval. The endpoints of
* the interval are included. The list is ordered by timestamp (decreasing).
* That is, the first score corresponds to the newest timestamp, and the
* last score corresponds to the oldest timestamp.
*
* @param fromTimestamp timestamp of the beginning of the interval.
* @param toTimestamp timestamp of the end of the interval.
* @return the list of scores within the interval.
*/
@NonNull
public abstract List<Score> getByInterval(long fromTimestamp,
long toTimestamp);
/**
* Returns the values of the scores that fall inside a certain interval
* of time.
* <p>
* The values are returned in an array containing one integer value for each
* day of the interval. The first entry corresponds to the most recent day
* in the interval. Each subsequent entry corresponds to one day older than
* the previous entry. The boundaries of the time interval are included.
*
* @param from timestamp for the oldest score
* @param to timestamp for the newest score
* @return values for the scores inside the given interval
*/
public final int[] getValues(long from, long to)
{
List<Score> scores = getByInterval(from, to);
int[] values = new int[scores.size()];
for(int i = 0; i < values.length; i++)
values[i] = scores.get(i).getValue();
return values;
}
public List<Score> groupBy(DateUtils.TruncateField field)
{
computeAll();

@ -92,6 +92,26 @@ public class MemoryRepetitionList extends RepetitionList
return oldestRep;
}
@Nullable
@Override
public Repetition getNewest()
{
long newestTime = -1;
Repetition newestRep = null;
for (Repetition rep : list)
{
if (rep.getTimestamp() > newestTime)
{
newestRep = rep;
newestTime = rep.getTimestamp();
}
}
return newestRep;
}
@Override
public void remove(@NonNull Repetition repetition)
{

@ -43,6 +43,21 @@ public class MemoryScoreList extends ScoreList
(s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp()));
}
@NonNull
@Override
public List<Score> getByInterval(long fromTimestamp, long toTimestamp)
{
compute(fromTimestamp, toTimestamp);
List<Score> filtered = new LinkedList<>();
for (Score s : list)
if (s.getTimestamp() >= fromTimestamp &&
s.getTimestamp() <= toTimestamp) filtered.add(s);
return filtered;
}
@Nullable
@Override
public Score getComputedByTimestamp(long timestamp)

@ -125,6 +125,24 @@ public class SQLiteRepetitionList extends RepetitionList
return record.toRepetition();
}
@Override
public Repetition getNewest()
{
check(habit.getId());
String query = "select habit, timestamp " +
"from Repetitions " +
"where habit = ? " +
"order by timestamp desc " +
"limit 1";
String params[] = { Long.toString(habit.getId()) };
RepetitionRecord record = sqlite.querySingle(query, params);
if (record == null) return null;
record.habit = habitRecord;
return record.toRepetition();
}
@Override
public void remove(@NonNull Repetition repetition)
{

@ -84,6 +84,29 @@ public class SQLiteScoreList extends ScoreList
}
}
@NonNull
@Override
public List<Score> getByInterval(long fromTimestamp, long toTimestamp)
{
check(habit.getId());
compute(fromTimestamp, toTimestamp);
String query = "select habit, timestamp, score " +
"from Score " +
"where habit = ? and timestamp >= ? and timestamp <= ? " +
"order by timestamp desc";
String params[] = {
Long.toString(habit.getId()),
Long.toString(fromTimestamp),
Long.toString(toTimestamp)
};
List<ScoreRecord> records = sqlite.query(query, params);
for (ScoreRecord record : records) record.habit = habitRecord;
return toScores(records);
}
@Override
@Nullable
public Score getComputedByTimestamp(long timestamp)
@ -127,11 +150,7 @@ public class SQLiteScoreList extends ScoreList
List<ScoreRecord> records = sqlite.query(query, params);
for (ScoreRecord record : records) record.habit = habitRecord;
List<Score> scores = new LinkedList<>();
for (ScoreRecord rec : records)
scores.add(rec.toScore());
return scores;
return toScores(records);
}
@Nullable
@ -177,4 +196,12 @@ public class SQLiteScoreList extends ScoreList
record.habit = habitRecord;
return record.toScore();
}
@NonNull
private List<Score> toScores(@NonNull List<ScoreRecord> records)
{
List<Score> scores = new LinkedList<>();
for (ScoreRecord r : records) scores.add(r.toScore());
return scores;
}
}

@ -272,4 +272,18 @@ public abstract class DateUtils
{
MONTH, WEEK_NUMBER, YEAR, QUARTER
}
/**
* Gets the number of days between two timestamps (exclusively).
*
* @param t1 the first timestamp to use in milliseconds
* @param t2 the second timestamp to use in milliseconds
* @return the number of days between the two timestamps
*/
public static int getDaysBetween(long t1, long t2)
{
Date d1 = new Date(t1);
Date d2 = new Date(t2);
return (int) (Math.abs((d2.getTime() - d1.getTime()) / millisecondsInOneDay));
}
}

@ -174,6 +174,27 @@ public class ScoreListTest extends BaseUnitTest
assertThat(writer.toString(), equalTo(expectedCSV));
}
@Test
public void test_getValues()
{
toggleRepetitions(0, 20);
long today = DateUtils.getStartOfToday();
long day = DateUtils.millisecondsInOneDay;
long from = today - 4 * day;
long to = today - 2 * day;
int[] expected = {
11883254,
11479288,
11053198,
};
int[] actual = habit.getScores().getValues(from, to);
assertThat(actual, equalTo(expected));
}
private void toggleRepetitions(final int from, final int to)
{
RepetitionList reps = habit.getRepetitions();

@ -134,4 +134,14 @@ public class DateUtilsTest extends BaseUnitTest
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
}
@Test
public void test_getDaysBetween()
{
long t1 = timestamp(2016, JANUARY, 1);
long t2 = timestamp(2016, DECEMBER, 31);
int expected = 365;
assertThat(DateUtils.getDaysBetween(t1, t2), equalTo(expected));
assertThat(DateUtils.getDaysBetween(t2, t1), equalTo(expected));
}
}

Loading…
Cancel
Save