Refactor CSVExporter

pull/77/merge
Alinson S. Xavier 10 years ago
parent a0582b65d5
commit dfe5c4954e

@ -35,6 +35,7 @@ dependencies {
compile 'com.android.support:support-v4:23.1.1'
compile 'com.github.paolorotolo:appintro:3.4.0'
compile 'org.apmem.tools:layouts:1.10@aar'
compile 'com.opencsv:opencsv:3.7'
compile project(':libs:drag-sort-listview:library')
compile files('libs/ActiveAndroid.jar')

@ -91,4 +91,9 @@ public class ColorHelper
hsv[index] = newValue;
return Color.HSVToColor(hsv);
}
public static String toHTML(int color)
{
return String.format("#%06X", 0xFFFFFF & color);
}
}

@ -24,6 +24,7 @@ import android.text.format.DateFormat;
import org.isoron.uhabits.R;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
@ -96,6 +97,14 @@ public class DateHelper
return df.format(date);
}
public static SimpleDateFormat getCSVDateFormat()
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
public static String formatHeaderDate(GregorianCalendar day)
{
String dayOfMonth = Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH));

@ -42,7 +42,7 @@ import org.isoron.uhabits.commands.ChangeHabitColorCommand;
import org.isoron.uhabits.commands.DeleteHabitsCommand;
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
import org.isoron.uhabits.fragments.EditHabitFragment;
import org.isoron.uhabits.io.CSVExporter;
import org.isoron.uhabits.io.HabitsExporter;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
@ -226,7 +226,7 @@ public class HabitSelectionCallback implements ActionMode.Callback
{
new AsyncTask<Void, Void, Void>()
{
String filename;
String archiveFilename;
@Override
protected void onPreExecute()
@ -241,15 +241,19 @@ public class HabitSelectionCallback implements ActionMode.Callback
@Override
protected void onPostExecute(Void aVoid)
{
if(filename != null)
if(archiveFilename != null)
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/zip");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename)));
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(archiveFilename)));
activity.startActivity(intent);
}
else
{
activity.showToast(R.string.could_not_export);
}
if(progressBar != null)
progressBar.setVisibility(View.GONE);
@ -258,8 +262,10 @@ public class HabitSelectionCallback implements ActionMode.Callback
@Override
protected Void doInBackground(Void... params)
{
CSVExporter exporter = new CSVExporter(activity, selectedHabits);
filename = exporter.writeArchive();
String dirName = String.format("%s/export/", activity.getExternalCacheDir());
HabitsExporter exporter = new HabitsExporter(selectedHabits, dirName);
archiveFilename = exporter.writeArchive();
return null;
}
}.execute();

@ -1,213 +0,0 @@
/*
* Copyright (C) 2016 Á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.io;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.activeandroid.Cache;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class CSVExporter
{
private List<Habit> habits;
private Context context;
private java.text.DateFormat dateFormat;
private List<String> generateDirs;
private List<String> generateFilenames;
private String basePath;
public CSVExporter(Context context, List<Habit> habits)
{
this.habits = habits;
this.context = context;
generateDirs = new LinkedList<>();
generateFilenames = new LinkedList<>();
basePath = String.format("%s/export/", context.getFilesDir());
dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public String formatDate(long timestamp)
{
return dateFormat.format(new Date(timestamp));
}
public String formatScore(int score)
{
return String.format("%.2f", ((float) score) / Score.MAX_VALUE);
}
private void writeScores(String dirPath, Habit habit) throws IOException
{
String path = dirPath + "scores.csv";
FileWriter out = new FileWriter(basePath + path);
generateFilenames.add(path);
String query = "select timestamp, score from score where habit = ? order by timestamp";
String params[] = { habit.getId().toString() };
SQLiteDatabase db = Cache.openDatabase();
Cursor cursor = db.rawQuery(query, params);
if(!cursor.moveToFirst()) return;
do
{
String timestamp = formatDate(cursor.getLong(0));
String score = formatScore(cursor.getInt(1));
out.write(String.format("%s,%s\n", timestamp, score));
} while(cursor.moveToNext());
out.close();
cursor.close();
}
private void writeCheckmarks(String dirPath, Habit habit) throws IOException
{
String path = dirPath + "checkmarks.csv";
FileWriter out = new FileWriter(basePath + path);
generateFilenames.add(path);
String query = "select timestamp, value from checkmarks where habit = ? order by timestamp";
String params[] = { habit.getId().toString() };
SQLiteDatabase db = Cache.openDatabase();
Cursor cursor = db.rawQuery(query, params);
if(!cursor.moveToFirst()) return;
do
{
String timestamp = formatDate(cursor.getLong(0));
Integer value = cursor.getInt(1);
out.write(String.format("%s,%d\n", timestamp, value));
} while(cursor.moveToNext());
out.close();
cursor.close();
}
private void writeFiles(Habit habit) throws IOException
{
String path = String.format("%s/", habit.name);
new File(basePath + path).mkdirs();
generateDirs.add(path);
writeScores(path, habit);
writeCheckmarks(path, habit);
}
private void writeZipFile(String zipFilename) throws IOException
{
FileOutputStream fos = new FileOutputStream(zipFilename);
ZipOutputStream zos = new ZipOutputStream(fos);
for(String filename : generateFilenames)
addFileToZip(zos, filename);
zos.close();
fos.close();
}
private void addFileToZip(ZipOutputStream zos, String filename) throws IOException
{
FileInputStream fis = new FileInputStream(new File(basePath + filename));
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
int length;
byte bytes[] = new byte[1024];
while((length = fis.read(bytes)) >= 0)
zos.write(bytes, 0, length);
zos.closeEntry();
fis.close();
}
private void cleanup()
{
for(String filename : generateFilenames)
new File(basePath + filename).delete();
for(String filename : generateDirs)
new File(basePath + filename).delete();
new File(basePath).delete();
}
public String writeArchive()
{
String date = formatDate(DateHelper.getStartOfToday());
File dir = context.getExternalCacheDir();
if(dir == null)
{
Log.e("CSVExporter", "No suitable directory found.");
return null;
}
String zipFilename = String.format("%s/habits-%s.zip", dir, date);
try
{
for (Habit h : habits)
writeFiles(h);
writeZipFile(zipFilename);
cleanup();
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
return zipFilename;
}
}

@ -0,0 +1,159 @@
/*
* Copyright (C) 2016 Á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.io;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.models.CheckmarkList;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.ScoreList;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class HabitsExporter
{
private List<Habit> habits;
private List<String> generateDirs;
private List<String> generateFilenames;
private String exportDirName;
public HabitsExporter(List<Habit> habits, String exportDirName)
{
this.habits = habits;
this.exportDirName = exportDirName;
if(!this.exportDirName.endsWith("/"))
this.exportDirName += "/";
generateDirs = new LinkedList<>();
generateFilenames = new LinkedList<>();
}
private void writeHabits() throws IOException
{
String filename = "habits.csv";
new File(exportDirName).mkdirs();
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
Habit.writeCSV(habits, out);
out.close();
for(Habit h : habits)
{
String habitDirName = String.format("%s/", h.name);
new File(exportDirName + habitDirName).mkdirs();
generateDirs.add(habitDirName);
writeScores(habitDirName, h.scores);
writeCheckmarks(habitDirName, h.checkmarks);
}
}
private void writeScores(String habitDirName, ScoreList scores) throws IOException
{
String path = habitDirName + "scores.csv";
FileWriter out = new FileWriter(exportDirName + path);
generateFilenames.add(path);
scores.writeCSV(out);
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();
}
private String writeZipFile() throws IOException
{
SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat();
String date = dateFormat.format(DateHelper.getStartOfToday());
String zipFilename = String.format("%s/habits-%s.zip", exportDirName, date);
FileOutputStream fos = new FileOutputStream(zipFilename);
ZipOutputStream zos = new ZipOutputStream(fos);
for(String filename : generateFilenames)
addFileToZip(zos, filename);
zos.close();
fos.close();
return zipFilename;
}
private void addFileToZip(ZipOutputStream zos, String filename) throws IOException
{
FileInputStream fis = new FileInputStream(new File(exportDirName + filename));
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
int length;
byte bytes[] = new byte[1024];
while((length = fis.read(bytes)) >= 0)
zos.write(bytes, 0, length);
zos.closeEntry();
fis.close();
}
public String writeArchive()
{
String zipFilename;
try
{
writeHabits();
zipFilename = writeZipFile();
cleanup();
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
return zipFilename;
}
private void cleanup()
{
for(String filename : generateFilenames)
new File(exportDirName + filename).delete();
for(String filename : generateDirs)
new File(exportDirName + filename).delete();
new File(exportDirName).delete();
}
}

@ -31,6 +31,10 @@ import com.activeandroid.query.Select;
import org.isoron.helpers.DateHelper;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class CheckmarkList
@ -229,4 +233,27 @@ public class CheckmarkList
if(today != null) return today.value;
else return Checkmark.UNCHECKED;
}
public void writeCSV(Writer out) throws IOException
{
SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat();
String query = "select timestamp, value from checkmarks where habit = ? order by timestamp";
String params[] = { habit.getId().toString() };
SQLiteDatabase db = Cache.openDatabase();
Cursor cursor = db.rawQuery(query, params);
if(!cursor.moveToFirst()) return;
do
{
String timestamp = dateFormat.format(new Date(cursor.getLong(0)));
Integer value = cursor.getInt(1);
out.write(String.format("%s,%d\n", timestamp, value));
} while(cursor.moveToNext());
cursor.close();
}
}

@ -20,6 +20,7 @@
package org.isoron.uhabits.models;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -33,10 +34,17 @@ import com.activeandroid.query.From;
import com.activeandroid.query.Select;
import com.activeandroid.query.Update;
import com.activeandroid.util.SQLiteUtils;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DateHelper;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@ -470,4 +478,38 @@ public class Habit extends Model
reminderMin = null;
reminderDays = DateHelper.ALL_WEEK_DAYS;
}
public static void writeCSV(List<Habit> habits, Writer out) throws IOException
{
CSVWriter csv = new CSVWriter(out);
for(Habit habit : habits)
{
String[] cols = { habit.name, habit.description, Integer.toString(habit.freqNum),
Integer.toString(habit.freqDen), ColorHelper.toHTML(habit.color) };
csv.writeAll(Collections.singletonList(cols));
}
csv.close();
}
public List<Habit> parseCSV(Reader in)
{
CSVReader csv = new CSVReader(in);
List<Habit> habits = new LinkedList<>();
for(String cols[] : csv)
{
Habit habit = new Habit();
habit.name = cols[0];
habit.description = cols[1];
habit.freqNum = Integer.parseInt(cols[2]);
habit.freqDen = Integer.parseInt(cols[3]);
habit.color = Color.parseColor(cols[4]);
habits.add(habit);
}
return habits;
}
}

@ -24,7 +24,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.activeandroid.ActiveAndroid;
import com.activeandroid.Cache;
import com.activeandroid.query.Delete;
import com.activeandroid.query.From;
@ -33,6 +32,11 @@ import com.activeandroid.query.Select;
import org.isoron.helpers.ActiveAndroidHelper;
import org.isoron.helpers.DateHelper;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScoreList
{
@NonNull
@ -278,4 +282,27 @@ public class ScoreList
if(score != null) return score.getStarStatus();
else return Score.EMPTY_STAR;
}
public void writeCSV(Writer out) throws IOException
{
SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat();
String query = "select timestamp, score from score where habit = ? order by timestamp";
String params[] = { habit.getId().toString() };
SQLiteDatabase db = Cache.openDatabase();
Cursor cursor = db.rawQuery(query, params);
if(!cursor.moveToFirst()) return;
do
{
String timestamp = dateFormat.format(new Date(cursor.getLong(0)));
String score = String.format("%.2f", ((float) cursor.getInt(1)) / Score.MAX_VALUE);
out.write(String.format("%s,%s\n", timestamp, score));
} while(cursor.moveToNext());
cursor.close();
}
}

@ -138,4 +138,5 @@
<string name="five_times_per_week">5 times per week</string>
<string name="custom_frequency">Custom …</string>
<string name="help">Help &amp; FAQ</string>
<string name="could_not_export">Failed to export data.</string>
</resources>
Loading…
Cancel
Save