diff --git a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java index b09cd572..b02ad2e0 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java +++ b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java @@ -40,10 +40,12 @@ import com.health.openscale.core.database.ScaleUserDAO; import com.health.openscale.core.database.ScaleUserDatabase; import com.health.openscale.core.datatypes.ScaleMeasurement; import com.health.openscale.core.datatypes.ScaleUser; +import com.health.openscale.core.utils.CsvHelper; import com.health.openscale.gui.fragments.FragmentUpdateListener; -import com.j256.simplecsv.processor.CsvProcessor; -import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -287,15 +289,14 @@ public class OpenScale { } public void importData(String filename) { - CsvProcessor csvProcessor = new CsvProcessor(ScaleMeasurement.class) - .withHeaderValidation(true) - .withFlexibleOrder(true) - .withAlwaysTrimInput(true) - .withAllowPartialLines(true); - File csvFile = new File(filename); - try { - List csvScaleMeasurementList = csvProcessor.readAll(csvFile, null); + List csvScaleMeasurementList = + CsvHelper.importFrom(new BufferedReader(new FileReader(filename))); + + final int userId = getSelectedScaleUser().getId(); + for (ScaleMeasurement measurement : csvScaleMeasurementList) { + measurement.setUserId(userId); + } measurementDAO.insertAll(csvScaleMeasurementList); updateScaleData(); @@ -308,12 +309,8 @@ public class OpenScale { } public void exportData(String filename) { - CsvProcessor csvProcessor = new CsvProcessor(ScaleMeasurement.class); - - File csvFile = new File(filename); - try { - csvProcessor.writeAll(csvFile, scaleMeasurementList, true); + CsvHelper.exportTo(new FileWriter(filename), scaleMeasurementList); Toast.makeText(context, context.getString(R.string.info_data_exported) + " /sdcard" + filename, Toast.LENGTH_SHORT).show(); } catch (IOException e) { Toast.makeText(context, context.getResources().getString(R.string.error_exporting) + " " + e.getMessage(), Toast.LENGTH_SHORT).show(); diff --git a/android_app/app/src/main/java/com/health/openscale/core/utils/CsvHelper.java b/android_app/app/src/main/java/com/health/openscale/core/utils/CsvHelper.java new file mode 100644 index 00000000..96772378 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/core/utils/CsvHelper.java @@ -0,0 +1,105 @@ +/* Copyright (C) 2018 Erik Johansson +* +* This program 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. +* +* This program 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 +*/ + +package com.health.openscale.core.utils; + +import com.health.openscale.core.datatypes.ScaleMeasurement; +import com.j256.simplecsv.processor.CsvProcessor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Writer; +import java.text.ParseException; +import java.util.List; + +public class CsvHelper { + + public static void exportTo(Writer writer, List measurements) throws IOException { + CsvProcessor csvProcessor = new CsvProcessor<>(ScaleMeasurement.class); + csvProcessor.writeAll(writer, measurements, true); + } + + static private String[] getOldStyleHeaders(String sampleLine) { + final String[] fields = sampleLine.split(",", -1); + + // Return an array with header fields so that all the headers that actually are + // in the file for a given version comes first, and then the rest comes at the end. + if (fields.length == 10) { + // From version 1.6 up to 1.7 + return new String[]{ + "dateTime", "weight", "fat", "water", "muscle", "lbw", "bone", "waist", "hip", "comment"}; + } + else if (fields.length == 9) { + // From version 1.5.5 (lbw unused) + return new String[]{ + "dateTime", "weight", "fat", "water", "muscle", "bone", "waist", "hip", "comment", + "lbw"}; + } + else if (fields.length == 8) { + // From version 1.3 (lbw and bone unused) + return new String[]{ + "dateTime", "weight", "fat", "water", "muscle", "waist", "hip", "comment", + "lbw", "bone"}; + } + else if (fields.length == 6) { + // From version 1.2 (lbw, bone, waist and hip unused) + return new String[]{ + "dateTime", "weight", "fat", "water", "muscle", "comment", + "lbw", "bone", "waist", "hip"}; + } + else if (fields.length == 5) { + // From version 1.0 (lbw, bone, waist, hip and muscle unused) + return new String[]{ + "dateTime", "weight", "fat", "water", "comment", + "lbw", "bone", "waist", "hip", "muscle"}; + } + + // Unknown input data format + return null; + } + + public static List importFrom(BufferedReader reader) + throws IOException, ParseException { + CsvProcessor csvProcessor = + new CsvProcessor(ScaleMeasurement.class) + .withHeaderValidation(true) + .withFlexibleOrder(true) + .withAlwaysTrimInput(true) + .withAllowPartialLines(true); + + reader.mark(1000); + try { + csvProcessor.readHeader(reader, null); + } + catch (ParseException ex) { + // Try to import it as an old style CSV export + reader.reset(); + final String sampleLine = reader.readLine(); + reader.reset(); + + final String[] header = getOldStyleHeaders(sampleLine); + + if (header == null) { + // Don't know what to do with this, let Simple CSV error out + return csvProcessor.readAll(reader, null); + } + + csvProcessor.validateHeaderColumns(header, null); + } + + return csvProcessor.readRows(reader, null); + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/junit/CsvHelperTest.java b/android_app/app/src/main/java/com/health/openscale/junit/CsvHelperTest.java new file mode 100644 index 00000000..995a0473 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/junit/CsvHelperTest.java @@ -0,0 +1,184 @@ +/* Copyright (C) 2018 Erik Johansson +* +* This program 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. +* +* This program 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 +*/ + +package com.health.openscale.junit; + +import com.health.openscale.core.datatypes.ScaleMeasurement; +import com.health.openscale.core.utils.CsvHelper; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +public class CsvHelperTest { + private static String HEADERS = + "\"bone\",\"comment\",\"dateTime\",\"fat\",\"hip\",\"lbw\"," + + "\"muscle\",\"waist\",\"water\",\"weight\"\n"; + + private void validateEntry(ScaleMeasurement m, int version) throws Exception { + assertEquals(8.0, m.getWeight(), 0.001); + assertEquals(2.0, m.getFat(), 0.001); + assertEquals(7.0, m.getWater(), 0.001); + + if (version > 0) { + assertEquals(5.0, m.getMuscle(), 0.001); + } + else { + assertEquals(0.0, m.getMuscle(), 0.001); + + } + if (version > 1) { + assertEquals(6.0, m.getWaist(), 0.001); + assertEquals(3.0, m.getHip(), 0.001); + } + else { + assertEquals(0.0, m.getWaist(), 0.001); + assertEquals(0.0, m.getHip(), 0.001); + } + if (version > 2) { + assertEquals(1.0, m.getBone(), 0.001); + } + else { + assertEquals(0.0, m.getBone(), 0.001); + } + if (version > 3) { + assertEquals(4.0, m.getLbw(), 0.001); + } + else { + assertEquals(0.0, m.getLbw(), 0.001); + } + + assertEquals("some text", m.getComment()); + + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2018, 2, 1, 12, 45); + assertEquals(cal.getTime(), m.getDateTime()); + } + + @Test + public void newStyleSingleEntry() throws Exception { + final String data = HEADERS + + "1.0,\"some text\",\"01.03.2018 12:45\",2.0,3.0,4.0,5.0,6.0,7.0,8.0\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 5); + } + + @Test + public void exportImport() throws Exception { + ScaleMeasurement m = new ScaleMeasurement(); + m.setWeight(8.0f); + m.setFat(2.0f); + m.setWater(7.0f); + m.setMuscle(5.0f); + m.setLbw(4.0f); + m.setBone(1.0f); + m.setWaist(6.0f); + m.setHip(3.0f); + m.setComment("some text"); + + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2018, 2, 1, 12, 45); + m.setDateTime(cal.getTime()); + + List measurements = new ArrayList<>(); + for (int i = 0; i < 10; ++i) { + measurements.add(m); + } + + StringWriter writer = new StringWriter(); + CsvHelper.exportTo(writer, measurements); + + List imported = CsvHelper.importFrom( + new BufferedReader(new StringReader(writer.toString()))); + assertEquals(measurements.size(), imported.size()); + for (ScaleMeasurement newM : imported) { + validateEntry(newM, 5); + } + } + + @Test + public void oldVersion16SingleEntry() throws Exception { + final String data = + "01.03.2018 12:45,8.0,2.0,7.0,5.0,4.0,1.0,6.0,3.0,some text\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 4); + } + + @Test + public void oldVersion155SingleEntry() throws Exception { + final String data = + "01.03.2018 12:45,8.0,2.0,7.0,5.0,1.0,6.0,3.0,some text\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 3); + } + + @Test + public void oldVersion13SingleEntry() throws Exception { + final String data = + "01.03.2018 12:45,8.0,2.0,7.0,5.0,6.0,3.0,some text\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 2); + } + + @Test + public void oldVersion12SingleEntry() throws Exception { + final String data = + "01.03.2018 12:45,8.0,2.0,7.0,5.0,some text\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 1); + } + + @Test + public void oldVersion10SingleEntry() throws Exception { + final String data = + "01.03.2018 12:45,8.0,2.0,7.0,some text\n"; + + List list = CsvHelper.importFrom( + new BufferedReader(new StringReader(data))); + + assertEquals(1, list.size()); + validateEntry(list.get(0), 0); + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/junit/DateTimeHelpersTest.java b/android_app/app/src/main/java/com/health/openscale/junit/DateTimeHelpersTest.java index 867ac35b..018cbd13 100644 --- a/android_app/app/src/main/java/com/health/openscale/junit/DateTimeHelpersTest.java +++ b/android_app/app/src/main/java/com/health/openscale/junit/DateTimeHelpersTest.java @@ -23,7 +23,6 @@ import org.junit.Test; import java.util.Calendar; import static junit.framework.Assert.assertEquals; -import static org.junit.Assert.assertEquals; public class DateTimeHelpersTest { Calendar getDate(int year, int month, int day, int hour, int minute, int second, int ms) {