mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Move importers to uhabits-core
This commit is contained in:
@@ -17,6 +17,7 @@ dependencies {
|
||||
implementation 'com.android.support:support-annotations:25.3.1'
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
implementation 'org.apache.commons:commons-lang3:3.5'
|
||||
implementation 'org.apache.commons:commons-io:1.3.2'
|
||||
implementation 'com.google.code.gson:gson:2.7'
|
||||
|
||||
testImplementation 'junit:junit:4+'
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core;
|
||||
|
||||
public class Config
|
||||
{
|
||||
public static final String DATABASE_FILENAME = "uhabits.db";
|
||||
public static int DATABASE_VERSION = 21;
|
||||
}
|
||||
@@ -41,4 +41,8 @@ public interface Database
|
||||
void setTransactionSuccessful();
|
||||
|
||||
void endTransaction();
|
||||
|
||||
void close();
|
||||
|
||||
int getVersion();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.database;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface DatabaseOpener
|
||||
{
|
||||
Database open(@NonNull File file);
|
||||
|
||||
File getProductionDatabaseFile();
|
||||
}
|
||||
@@ -180,4 +180,27 @@ public class JdbcDatabase implements Database
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.close();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion()
|
||||
{
|
||||
try (Cursor c = select("PRAGMA user_version"))
|
||||
{
|
||||
c.moveToNext();
|
||||
return c.getInt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* AbstractImporter is the base class for all classes that import data from
|
||||
* files into the app.
|
||||
*/
|
||||
public abstract class AbstractImporter
|
||||
{
|
||||
protected final HabitList habits;
|
||||
|
||||
public AbstractImporter(HabitList habits)
|
||||
{
|
||||
this.habits = habits;
|
||||
}
|
||||
|
||||
public abstract boolean canHandle(@NonNull File file) throws IOException;
|
||||
|
||||
public abstract void importHabitsFromFile(@NonNull File file) throws IOException;
|
||||
|
||||
public static boolean isSQLite3File(@NonNull File file) throws IOException
|
||||
{
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
|
||||
byte[] sqliteHeader = "SQLite format 3".getBytes();
|
||||
byte[] buffer = new byte[sqliteHeader.length];
|
||||
|
||||
int count = fis.read(buffer);
|
||||
if(count < sqliteHeader.length) return false;
|
||||
|
||||
return Arrays.equals(buffer, sqliteHeader);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
/**
|
||||
* A GenericImporter decides which implementation of AbstractImporter is able to
|
||||
* handle a given file and delegates to it the task of importing the data.
|
||||
*/
|
||||
public class GenericImporter extends AbstractImporter
|
||||
{
|
||||
List<AbstractImporter> importers;
|
||||
|
||||
@Inject
|
||||
public GenericImporter(@NonNull HabitList habits,
|
||||
@NonNull LoopDBImporter loopDBImporter,
|
||||
@NonNull RewireDBImporter rewireDBImporter,
|
||||
@NonNull TickmateDBImporter tickmateDBImporter,
|
||||
@NonNull HabitBullCSVImporter habitBullCSVImporter)
|
||||
{
|
||||
super(habits);
|
||||
importers = new LinkedList<>();
|
||||
importers.add(loopDBImporter);
|
||||
importers.add(rewireDBImporter);
|
||||
importers.add(tickmateDBImporter);
|
||||
importers.add(habitBullCSVImporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
for (AbstractImporter importer : importers)
|
||||
if (importer.canHandle(file)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
for (AbstractImporter importer : importers)
|
||||
if (importer.canHandle(file)) importer.importHabitsFromFile(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import com.opencsv.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
|
||||
/**
|
||||
* Class that imports data from HabitBull CSV files.
|
||||
*/
|
||||
public class HabitBullCSVImporter extends AbstractImporter
|
||||
{
|
||||
private ModelFactory modelFactory;
|
||||
|
||||
@Inject
|
||||
public HabitBullCSVImporter(@NonNull HabitList habits,
|
||||
@NonNull ModelFactory modelFactory)
|
||||
{
|
||||
super(habits);
|
||||
this.modelFactory = modelFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
String line = reader.readLine();
|
||||
return line.startsWith("HabitName,HabitDescription,HabitCategory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull final File file)
|
||||
throws IOException
|
||||
{
|
||||
CSVReader reader = new CSVReader(new FileReader(file));
|
||||
HashMap<String, Habit> map = new HashMap<>();
|
||||
|
||||
for (String line[] : reader)
|
||||
{
|
||||
String name = line[0];
|
||||
if (name.equals("HabitName")) continue;
|
||||
|
||||
String description = line[1];
|
||||
String dateString[] = line[3].split("-");
|
||||
int year = Integer.parseInt(dateString[0]);
|
||||
int month = Integer.parseInt(dateString[1]);
|
||||
int day = Integer.parseInt(dateString[2]);
|
||||
|
||||
Calendar date = DateUtils.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
|
||||
long timestamp = date.getTimeInMillis();
|
||||
|
||||
int value = Integer.parseInt(line[4]);
|
||||
if (value != 1) continue;
|
||||
|
||||
Habit h = map.get(name);
|
||||
|
||||
if (h == null)
|
||||
{
|
||||
h = modelFactory.buildHabit();
|
||||
h.setName(name);
|
||||
h.setDescription(description);
|
||||
h.setFrequency(Frequency.DAILY);
|
||||
habits.add(h);
|
||||
map.put(name, h);
|
||||
}
|
||||
|
||||
if (!h.getRepetitions().containsTimestamp(timestamp))
|
||||
h.getRepetitions().toggle(timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.apache.commons.io.*;
|
||||
import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static org.isoron.uhabits.core.Config.DATABASE_VERSION;
|
||||
|
||||
/**
|
||||
* Class that imports data from database files exported by Loop Habit Tracker.
|
||||
*/
|
||||
public class LoopDBImporter extends AbstractImporter
|
||||
{
|
||||
@NonNull
|
||||
private final DatabaseOpener opener;
|
||||
|
||||
@Inject
|
||||
public LoopDBImporter(@NonNull HabitList habits,
|
||||
@NonNull DatabaseOpener opener)
|
||||
{
|
||||
super(habits);
|
||||
this.opener = opener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
if (!isSQLite3File(file)) return false;
|
||||
|
||||
Database db = opener.open(file);
|
||||
boolean canHandle = true;
|
||||
|
||||
Cursor c = db.select("select count(*) from SQLITE_MASTER " +
|
||||
"where name='Checkmarks' or name='Repetitions'");
|
||||
|
||||
if (!c.moveToNext() || c.getInt(0) != 2)
|
||||
{
|
||||
// Log.w("LoopDBImporter", "Cannot handle file: tables not found");
|
||||
canHandle = false;
|
||||
}
|
||||
|
||||
if (db.getVersion() > DATABASE_VERSION)
|
||||
{
|
||||
// Log.w("LoopDBImporter", String.format(
|
||||
// "Cannot handle file: incompatible version: %d > %d",
|
||||
// db.getVersion(), DATABASE_VERSION));
|
||||
canHandle = false;
|
||||
}
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return canHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
// DatabaseUtils.dispose();
|
||||
File originalDB = opener.getProductionDatabaseFile();
|
||||
FileUtils.copyFile(file, originalDB);
|
||||
// DatabaseUtils.initializeDatabase(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
/**
|
||||
* Class that imports database files exported by Rewire.
|
||||
*/
|
||||
public class RewireDBImporter extends AbstractImporter
|
||||
{
|
||||
private ModelFactory modelFactory;
|
||||
|
||||
@NonNull
|
||||
private final DatabaseOpener opener;
|
||||
|
||||
@Inject
|
||||
public RewireDBImporter(@NonNull HabitList habits,
|
||||
@NonNull ModelFactory modelFactory,
|
||||
@NonNull DatabaseOpener opener)
|
||||
{
|
||||
super(habits);
|
||||
this.modelFactory = modelFactory;
|
||||
this.opener = opener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
if (!isSQLite3File(file)) return false;
|
||||
|
||||
Database db = opener.open(file);
|
||||
Cursor c = db.select("select count(*) from SQLITE_MASTER " +
|
||||
"where name='CHECKINS' or name='UNIT'");
|
||||
|
||||
boolean result = (c.moveToNext() && c.getInt(0) == 2);
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
Database db = opener.open(file);
|
||||
db.beginTransaction();
|
||||
createHabits(db);
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void createHabits(Database db)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = db.select("select _id, name, description, schedule, " +
|
||||
"active_days, repeating_count, days, period " +
|
||||
"from habits");
|
||||
if (!c.moveToNext()) return;
|
||||
|
||||
do
|
||||
{
|
||||
int id = c.getInt(0);
|
||||
String name = c.getString(1);
|
||||
String description = c.getString(2);
|
||||
int schedule = c.getInt(3);
|
||||
String activeDays = c.getString(4);
|
||||
int repeatingCount = c.getInt(5);
|
||||
int days = c.getInt(6);
|
||||
int periodIndex = c.getInt(7);
|
||||
|
||||
Habit habit = modelFactory.buildHabit();
|
||||
habit.setName(name);
|
||||
habit.setDescription(description);
|
||||
|
||||
int periods[] = { 7, 31, 365 };
|
||||
int numerator, denominator;
|
||||
|
||||
switch (schedule)
|
||||
{
|
||||
case 0:
|
||||
numerator = activeDays.split(",").length;
|
||||
denominator = 7;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
numerator = days;
|
||||
denominator = (periods[periodIndex]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
numerator = 1;
|
||||
denominator = repeatingCount;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
habit.setFrequency(new Frequency(numerator, denominator));
|
||||
habits.add(habit);
|
||||
|
||||
createReminder(db, habit, id);
|
||||
createCheckmarks(db, habit, id);
|
||||
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void createCheckmarks(@NonNull Database db,
|
||||
@NonNull Habit habit,
|
||||
int rewireHabitId)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
String[] params = { Integer.toString(rewireHabitId) };
|
||||
c = db.select(
|
||||
"select distinct date from checkins where habit_id=? and type=2",
|
||||
params);
|
||||
if (!c.moveToNext()) return;
|
||||
|
||||
do
|
||||
{
|
||||
String date = c.getString(0);
|
||||
int year = Integer.parseInt(date.substring(0, 4));
|
||||
int month = Integer.parseInt(date.substring(4, 6));
|
||||
int day = Integer.parseInt(date.substring(6, 8));
|
||||
|
||||
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
|
||||
cal.set(year, month - 1, day);
|
||||
|
||||
habit.getRepetitions().toggle(cal.getTimeInMillis());
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void createReminder(Database db, Habit habit, int rewireHabitId)
|
||||
{
|
||||
String[] params = { Integer.toString(rewireHabitId) };
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = db.select(
|
||||
"select time, active_days from reminders where habit_id=? limit 1",
|
||||
params);
|
||||
|
||||
if (!c.moveToNext()) return;
|
||||
int rewireReminder = Integer.parseInt(c.getString(0));
|
||||
if (rewireReminder <= 0 || rewireReminder >= 1440) return;
|
||||
|
||||
boolean reminderDays[] = new boolean[7];
|
||||
|
||||
String activeDays[] = c.getString(1).split(",");
|
||||
for (String d : activeDays)
|
||||
{
|
||||
int idx = (Integer.parseInt(d) + 1) % 7;
|
||||
reminderDays[idx] = true;
|
||||
}
|
||||
|
||||
int hour = rewireReminder / 60;
|
||||
int minute = rewireReminder % 60;
|
||||
WeekdayList days = new WeekdayList(reminderDays);
|
||||
|
||||
Reminder reminder = new Reminder(hour, minute, days);
|
||||
habit.setReminder(reminder);
|
||||
habits.update(habit);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
/**
|
||||
* Class that imports data from database files exported by Tickmate.
|
||||
*/
|
||||
public class TickmateDBImporter extends AbstractImporter
|
||||
{
|
||||
private ModelFactory modelFactory;
|
||||
|
||||
@NonNull
|
||||
private final DatabaseOpener opener;
|
||||
|
||||
@Inject
|
||||
public TickmateDBImporter(@NonNull HabitList habits,
|
||||
@NonNull ModelFactory modelFactory,
|
||||
@NonNull DatabaseOpener opener)
|
||||
{
|
||||
super(habits);
|
||||
this.modelFactory = modelFactory;
|
||||
this.opener = opener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
if (!isSQLite3File(file)) return false;
|
||||
|
||||
Database db = opener.open(file);
|
||||
Cursor c = db.select("select count(*) from SQLITE_MASTER " +
|
||||
"where name='tracks' or name='track2groups'");
|
||||
|
||||
boolean result = (c.moveToNext() && c.getInt(0) == 2);
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
final Database db = opener.open(file);
|
||||
db.beginTransaction();
|
||||
createHabits(db);
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void createCheckmarks(@NonNull Database db,
|
||||
@NonNull Habit habit,
|
||||
int tickmateTrackId)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
String[] params = {Integer.toString(tickmateTrackId)};
|
||||
c = db.select(
|
||||
"select distinct year, month, day from ticks where _track_id=?",
|
||||
params);
|
||||
if (!c.moveToNext()) return;
|
||||
|
||||
do
|
||||
{
|
||||
int year = c.getInt(0);
|
||||
int month = c.getInt(1);
|
||||
int day = c.getInt(2);
|
||||
|
||||
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
|
||||
cal.set(year, month, day);
|
||||
|
||||
habit.getRepetitions().toggle(cal.getTimeInMillis());
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void createHabits(Database db)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = db.select("select _id, name, description from tracks",
|
||||
new String[0]);
|
||||
if (!c.moveToNext()) return;
|
||||
|
||||
do
|
||||
{
|
||||
int id = c.getInt(0);
|
||||
String name = c.getString(1);
|
||||
String description = c.getString(2);
|
||||
|
||||
Habit habit = modelFactory.buildHabit();
|
||||
habit.setName(name);
|
||||
habit.setDescription(description);
|
||||
habit.setFrequency(Frequency.DAILY);
|
||||
habits.add(habit);
|
||||
|
||||
createCheckmarks(db, habit, id);
|
||||
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides classes that deal with importing from and exporting to files.
|
||||
*/
|
||||
package org.isoron.uhabits.core.io;
|
||||
@@ -66,7 +66,7 @@ public class BaseUnitTest
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
validateMockitoUsage();
|
||||
DateUtils.setFixedLocalTime(null);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import org.apache.commons.io.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.junit.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class HabitsCSVExporterTest extends BaseUnitTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
habitList.add(fixtures.createShortHabit());
|
||||
habitList.add(fixtures.createEmptyHabit());
|
||||
baseDir = Files.createTempDirectory("csv").toFile();
|
||||
assertNotNull(baseDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
FileUtils.deleteDirectory(baseDir);
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCSV() throws IOException
|
||||
{
|
||||
List<Habit> selected = new LinkedList<>();
|
||||
for (Habit h : habitList) selected.add(h);
|
||||
|
||||
HabitsCSVExporter exporter =
|
||||
new HabitsCSVExporter(habitList, selected, baseDir);
|
||||
String filename = exporter.writeArchive();
|
||||
assertAbsolutePathExists(filename);
|
||||
|
||||
File archive = new File(filename);
|
||||
unzip(archive);
|
||||
|
||||
assertPathExists("Habits.csv");
|
||||
assertPathExists("001 Wake up early");
|
||||
assertPathExists("001 Wake up early/Checkmarks.csv");
|
||||
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 unzip(File file) throws IOException
|
||||
{
|
||||
ZipFile zip = new ZipFile(file);
|
||||
Enumeration<? extends ZipEntry> e = zip.entries();
|
||||
|
||||
while (e.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = e.nextElement();
|
||||
InputStream stream = zip.getInputStream(entry);
|
||||
|
||||
String outputFilename =
|
||||
String.format("%s/%s", baseDir.getAbsolutePath(),
|
||||
entry.getName());
|
||||
File out = new File(outputFilename);
|
||||
File parent = out.getParentFile();
|
||||
if (parent != null) parent.mkdirs();
|
||||
|
||||
IOUtils.copy(stream, new FileOutputStream(out));
|
||||
}
|
||||
|
||||
zip.close();
|
||||
}
|
||||
|
||||
private void assertPathExists(String s)
|
||||
{
|
||||
assertAbsolutePathExists(
|
||||
String.format("%s/%s", baseDir.getAbsolutePath(), s));
|
||||
}
|
||||
|
||||
private void assertAbsolutePathExists(String s)
|
||||
{
|
||||
File file = new File(s);
|
||||
assertTrue(
|
||||
String.format("File %s should exist", file.getAbsolutePath()),
|
||||
file.exists());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.core.io;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.apache.commons.io.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.junit.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
|
||||
import sun.reflect.generics.reflectiveObjects.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.isoron.uhabits.core.models.Frequency.THREE_TIMES_PER_WEEK;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ImportTest extends BaseUnitTest
|
||||
{
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
DateUtils.setFixedLocalTime(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHabitBullCSV() throws IOException
|
||||
{
|
||||
importFromFile("habitbull.csv");
|
||||
|
||||
assertThat(habitList.size(), equalTo(4));
|
||||
|
||||
Habit habit = habitList.getByPosition(0);
|
||||
assertThat(habit.getName(), equalTo("Breed dragons"));
|
||||
assertThat(habit.getDescription(), equalTo("with love and fire"));
|
||||
assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 19));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoopDB() throws IOException
|
||||
{
|
||||
importFromFile("loop.db");
|
||||
|
||||
assertThat(habitList.size(), equalTo(9));
|
||||
|
||||
Habit habit = habitList.getByPosition(0);
|
||||
assertThat(habit.getName(), equalTo("Wake up early"));
|
||||
assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 14));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 16));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 17));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRewireDB() throws IOException
|
||||
{
|
||||
importFromFile("rewire.db");
|
||||
|
||||
assertThat(habitList.size(), equalTo(3));
|
||||
|
||||
Habit habit = habitList.getByPosition(0);
|
||||
assertThat(habit.getName(), equalTo("Wake up early"));
|
||||
assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK));
|
||||
assertFalse(habit.hasReminder());
|
||||
assertFalse(containsRepetition(habit, 2015, 12, 31));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 28));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 10));
|
||||
|
||||
habit = habitList.getByPosition(1);
|
||||
assertThat(habit.getName(), equalTo("brush teeth"));
|
||||
assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK));
|
||||
assertThat(habit.hasReminder(), equalTo(true));
|
||||
|
||||
Reminder reminder = habit.getReminder();
|
||||
assertThat(reminder.getHour(), equalTo(8));
|
||||
assertThat(reminder.getMinute(), equalTo(0));
|
||||
boolean[] reminderDays = { false, true, true, true, true, true, false };
|
||||
assertThat(reminder.getDays().toArray(), equalTo(reminderDays));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTickmateDB() throws IOException
|
||||
{
|
||||
importFromFile("tickmate.db");
|
||||
|
||||
assertThat(habitList.size(), equalTo(3));
|
||||
|
||||
Habit h = habitList.getByPosition(0);
|
||||
assertThat(h.getName(), equalTo("Vegan"));
|
||||
assertTrue(containsRepetition(h, 2016, 1, 24));
|
||||
assertTrue(containsRepetition(h, 2016, 2, 5));
|
||||
assertTrue(containsRepetition(h, 2016, 3, 18));
|
||||
assertFalse(containsRepetition(h, 2016, 3, 14));
|
||||
}
|
||||
|
||||
private boolean containsRepetition(Habit h, int year, int month, int day)
|
||||
{
|
||||
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
return h.getRepetitions().containsTimestamp(date.getTimeInMillis());
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = getClass().getResourceAsStream(assetPath);
|
||||
if(in == null) {
|
||||
File file = new File("uhabits-core/src/test/resources/" + assetPath);
|
||||
if(file.exists()) in = new FileInputStream(file);
|
||||
}
|
||||
|
||||
IOUtils.copy(in, new FileOutputStream(dst));
|
||||
}
|
||||
|
||||
private void importFromFile(String assetFilename) throws IOException
|
||||
{
|
||||
File file = File.createTempFile("asset", "");
|
||||
copyAssetToFile(assetFilename, file);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(file.canRead());
|
||||
|
||||
DatabaseOpener opener = new DatabaseOpener() {
|
||||
@Override
|
||||
public Database open(@NonNull File file)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new JdbcDatabase(DriverManager.getConnection(
|
||||
String.format("jdbc:sqlite:%s", file.getAbsolutePath())));
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getProductionDatabaseFile()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
};
|
||||
GenericImporter importer = new GenericImporter(habitList,
|
||||
new LoopDBImporter(habitList, opener),
|
||||
new RewireDBImporter(habitList, modelFactory, opener),
|
||||
new TickmateDBImporter(habitList, modelFactory, opener),
|
||||
new HabitBullCSVImporter(habitList, modelFactory));
|
||||
|
||||
assertTrue(importer.canHandle(file));
|
||||
importer.importHabitsFromFile(file);
|
||||
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class RepetitionListTest extends BaseUnitTest
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown()
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class SQLiteHabitListTest extends BaseUnitTest
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown()
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
habitList.getObservable().removeListener(listener);
|
||||
super.tearDown();
|
||||
|
||||
@@ -23,7 +23,6 @@ package org.isoron.uhabits.core.models.sqlite.records;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.models.sqlite.records.*;
|
||||
import org.junit.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
19
uhabits-core/src/test/resources/habitbull.csv
Normal file
19
uhabits-core/src/test/resources/habitbull.csv
Normal file
@@ -0,0 +1,19 @@
|
||||
HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-18,1,
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-19,1,
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-21,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1,
|
||||
|
BIN
uhabits-core/src/test/resources/loop.db
Normal file
BIN
uhabits-core/src/test/resources/loop.db
Normal file
Binary file not shown.
BIN
uhabits-core/src/test/resources/rewire.db
Normal file
BIN
uhabits-core/src/test/resources/rewire.db
Normal file
Binary file not shown.
BIN
uhabits-core/src/test/resources/tickmate.db
Normal file
BIN
uhabits-core/src/test/resources/tickmate.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user