HabitBullCSVImporter: Accept multiple date formats

Fixes 762
pull/794/head
Alinson S. Xavier 5 years ago
parent c53997ffcc
commit b58af03a7c

@ -0,0 +1,82 @@
HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText
H1,,C1,11/5/2020,1,
H2,,C2,11/5/2020,-2150000000,
H3,Habit 3,C3,4/11/2019,1,
H3,Habit 3,C3,4/12/2019,1,
H3,Habit 3,C3,4/13/2019,0,
H3,Habit 3,C3,4/14/2019,1,
H3,Habit 3,C3,4/15/2019,1,
H3,Habit 3,C3,4/16/2019,1,
H3,Habit 3,C3,4/17/2019,1,
H3,Habit 3,C3,4/18/2019,1,
H3,Habit 3,C3,4/19/2019,0,
H3,Habit 3,C3,4/20/2019,1,
H3,Habit 3,C3,4/21/2019,1,
H3,Habit 3,C3,4/22/2019,1,
H3,Habit 3,C3,4/23/2019,0,
H3,Habit 3,C3,4/24/2019,1,
H3,Habit 3,C3,4/25/2019,1,
H3,Habit 3,C3,4/26/2019,1,
H3,Habit 3,C3,4/27/2019,0,
H3,Habit 3,C3,4/28/2019,1,
H3,Habit 3,C3,4/29/2019,0,
H3,Habit 3,C3,4/30/2019,1,
H3,Habit 3,C3,5/1/2019,1,
H3,Habit 3,C3,5/2/2019,1,
H3,Habit 3,C3,5/3/2019,1,
H3,Habit 3,C3,5/4/2019,1,
H3,Habit 3,C3,5/5/2019,1,
H3,Habit 3,C3,5/6/2019,0,
H3,Habit 3,C3,5/7/2019,1,
H3,Habit 3,C3,5/8/2019,1,
H3,Habit 3,C3,5/9/2019,1,
H3,Habit 3,C3,5/10/2019,1,
H3,Habit 3,C3,5/11/2019,1,
H3,Habit 3,C3,5/12/2019,1,
H3,Habit 3,C3,5/13/2019,1,
H3,Habit 3,C3,5/14/2019,1,
H3,Habit 3,C3,5/15/2019,1,
H3,Habit 3,C3,5/16/2019,1,
H3,Habit 3,C3,5/17/2019,1,
H3,Habit 3,C3,5/18/2019,0,
H3,Habit 3,C3,5/19/2019,1,
H3,Habit 3,C3,5/20/2019,1,
H3,Habit 3,C3,5/21/2019,1,
H3,Habit 3,C3,5/22/2019,1,
H3,Habit 3,C3,5/23/2019,1,
H3,Habit 3,C3,5/24/2019,1,
H3,Habit 3,C3,5/25/2019,1,
H3,Habit 3,C3,5/26/2019,1,
H3,Habit 3,C3,5/27/2019,1,
H3,Habit 3,C3,5/28/2019,1,
H3,Habit 3,C3,5/29/2019,1,
H3,Habit 3,C3,5/30/2019,0,
H3,Habit 3,C3,5/31/2019,1,
H3,Habit 3,C3,6/1/2019,1,
H3,Habit 3,C3,6/2/2019,1,
H3,Habit 3,C3,6/3/2019,1,
H3,Habit 3,C3,6/4/2019,1,
H3,Habit 3,C3,6/5/2019,1,
H3,Habit 3,C3,6/6/2019,1,
H3,Habit 3,C3,6/7/2019,1,
H3,Habit 3,C3,6/8/2019,1,
H3,Habit 3,C3,6/9/2019,1,
H3,Habit 3,C3,6/10/2019,1,
H3,Habit 3,C3,6/11/2019,1,
H3,Habit 3,C3,6/12/2019,1,
H3,Habit 3,C3,6/13/2019,1,
H3,Habit 3,C3,6/14/2019,0,
H3,Habit 3,C3,6/15/2019,1,
H4,Habit 4,C4,11/6/2020,1,
H4,Habit 4,C4,11/9/2020,1,
H4,Habit 4,C4,11/11/2020,1,
H4,Habit 4,C4,12/30/2020,-2150000000,
H5,Habit 5,C4,11/5/2020,1,
H5,Habit 5,C4,11/6/2020,1,
H5,Habit 5,C4,11/9/2020,1,
H5,Habit 5,C4,11/11/2020,1,
H6,Habit 6,C5,11/5/2020,1,
H6,Habit 6,C5,11/6/2020,1,
H6,Habit 6,C5,11/9/2020,1,
H6,Habit 6,C5,11/11/2020,1,
H6,Habit 6,C5,11/12/2020,1,
1 HabitName HabitDescription HabitCategory CalendarDate Value CommentText
2 H1 C1 11/5/2020 1
3 H2 C2 11/5/2020 -2150000000
4 H3 Habit 3 C3 4/11/2019 1
5 H3 Habit 3 C3 4/12/2019 1
6 H3 Habit 3 C3 4/13/2019 0
7 H3 Habit 3 C3 4/14/2019 1
8 H3 Habit 3 C3 4/15/2019 1
9 H3 Habit 3 C3 4/16/2019 1
10 H3 Habit 3 C3 4/17/2019 1
11 H3 Habit 3 C3 4/18/2019 1
12 H3 Habit 3 C3 4/19/2019 0
13 H3 Habit 3 C3 4/20/2019 1
14 H3 Habit 3 C3 4/21/2019 1
15 H3 Habit 3 C3 4/22/2019 1
16 H3 Habit 3 C3 4/23/2019 0
17 H3 Habit 3 C3 4/24/2019 1
18 H3 Habit 3 C3 4/25/2019 1
19 H3 Habit 3 C3 4/26/2019 1
20 H3 Habit 3 C3 4/27/2019 0
21 H3 Habit 3 C3 4/28/2019 1
22 H3 Habit 3 C3 4/29/2019 0
23 H3 Habit 3 C3 4/30/2019 1
24 H3 Habit 3 C3 5/1/2019 1
25 H3 Habit 3 C3 5/2/2019 1
26 H3 Habit 3 C3 5/3/2019 1
27 H3 Habit 3 C3 5/4/2019 1
28 H3 Habit 3 C3 5/5/2019 1
29 H3 Habit 3 C3 5/6/2019 0
30 H3 Habit 3 C3 5/7/2019 1
31 H3 Habit 3 C3 5/8/2019 1
32 H3 Habit 3 C3 5/9/2019 1
33 H3 Habit 3 C3 5/10/2019 1
34 H3 Habit 3 C3 5/11/2019 1
35 H3 Habit 3 C3 5/12/2019 1
36 H3 Habit 3 C3 5/13/2019 1
37 H3 Habit 3 C3 5/14/2019 1
38 H3 Habit 3 C3 5/15/2019 1
39 H3 Habit 3 C3 5/16/2019 1
40 H3 Habit 3 C3 5/17/2019 1
41 H3 Habit 3 C3 5/18/2019 0
42 H3 Habit 3 C3 5/19/2019 1
43 H3 Habit 3 C3 5/20/2019 1
44 H3 Habit 3 C3 5/21/2019 1
45 H3 Habit 3 C3 5/22/2019 1
46 H3 Habit 3 C3 5/23/2019 1
47 H3 Habit 3 C3 5/24/2019 1
48 H3 Habit 3 C3 5/25/2019 1
49 H3 Habit 3 C3 5/26/2019 1
50 H3 Habit 3 C3 5/27/2019 1
51 H3 Habit 3 C3 5/28/2019 1
52 H3 Habit 3 C3 5/29/2019 1
53 H3 Habit 3 C3 5/30/2019 0
54 H3 Habit 3 C3 5/31/2019 1
55 H3 Habit 3 C3 6/1/2019 1
56 H3 Habit 3 C3 6/2/2019 1
57 H3 Habit 3 C3 6/3/2019 1
58 H3 Habit 3 C3 6/4/2019 1
59 H3 Habit 3 C3 6/5/2019 1
60 H3 Habit 3 C3 6/6/2019 1
61 H3 Habit 3 C3 6/7/2019 1
62 H3 Habit 3 C3 6/8/2019 1
63 H3 Habit 3 C3 6/9/2019 1
64 H3 Habit 3 C3 6/10/2019 1
65 H3 Habit 3 C3 6/11/2019 1
66 H3 Habit 3 C3 6/12/2019 1
67 H3 Habit 3 C3 6/13/2019 1
68 H3 Habit 3 C3 6/14/2019 0
69 H3 Habit 3 C3 6/15/2019 1
70 H4 Habit 4 C4 11/6/2020 1
71 H4 Habit 4 C4 11/9/2020 1
72 H4 Habit 4 C4 11/11/2020 1
73 H4 Habit 4 C4 12/30/2020 -2150000000
74 H5 Habit 5 C4 11/5/2020 1
75 H5 Habit 5 C4 11/6/2020 1
76 H5 Habit 5 C4 11/9/2020 1
77 H5 Habit 5 C4 11/11/2020 1
78 H6 Habit 6 C5 11/5/2020 1
79 H6 Habit 6 C5 11/6/2020 1
80 H6 Habit 6 C5 11/9/2020 1
81 H6 Habit 6 C5 11/11/2020 1
82 H6 Habit 6 C5 11/12/2020 1

@ -25,11 +25,19 @@ import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.ModelFactory import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.utils.DateUtils
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.FileReader import java.io.FileReader
import java.text.DateFormat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar.DAY_OF_MONTH
import java.util.Calendar.MONTH
import java.util.Calendar.YEAR
import java.util.Date
import java.util.GregorianCalendar
import java.util.HashMap import java.util.HashMap
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -39,8 +47,11 @@ class HabitBullCSVImporter
@Inject constructor( @Inject constructor(
private val habitList: HabitList, private val habitList: HabitList,
private val modelFactory: ModelFactory, private val modelFactory: ModelFactory,
logging: Logging,
) : AbstractImporter() { ) : AbstractImporter() {
val logger = logging.getLogger("HabitBullCSVImporter")
override fun canHandle(file: File): Boolean { override fun canHandle(file: File): Boolean {
val reader = BufferedReader(FileReader(file)) val reader = BufferedReader(FileReader(file))
val line = reader.readLine() val line = reader.readLine()
@ -50,19 +61,11 @@ class HabitBullCSVImporter
override fun importHabitsFromFile(file: File) { override fun importHabitsFromFile(file: File) {
val reader = CSVReader(FileReader(file)) val reader = CSVReader(FileReader(file))
val map = HashMap<String, Habit>() val map = HashMap<String, Habit>()
for (line in reader) { for (cols in reader) {
val name = line[0] val name = cols[0]
if (name == "HabitName") continue if (name == "HabitName") continue
val description = line[1] val description = cols[1]
val dateString = line[3].split("-").toTypedArray() val timestamp = parseTimestamp(cols[3])
val year = dateString[0].toInt()
val month = dateString[1].toInt()
val day = dateString[2].toInt()
val date = DateUtils.getStartOfTodayCalendar()
date[year, month - 1] = day
val timestamp = Timestamp(date.timeInMillis)
val value = line[4].toInt()
if (value != 1) continue
var h = map[name] var h = map[name]
if (h == null) { if (h == null) {
h = modelFactory.buildHabit() h = modelFactory.buildHabit()
@ -71,8 +74,46 @@ class HabitBullCSVImporter
h.frequency = Frequency.DAILY h.frequency = Frequency.DAILY
habitList.add(h) habitList.add(h)
map[name] = h map[name] = h
logger.info("Creating habit: $name")
} }
if (parseInt(cols[4]) == 1) {
h.originalEntries.add(Entry(timestamp, Entry.YES_MANUAL)) h.originalEntries.add(Entry(timestamp, Entry.YES_MANUAL))
} }
} }
}
private fun parseTimestamp(rawValue: String): Timestamp {
val formats = listOf(
DateFormat.getDateInstance(DateFormat.SHORT),
SimpleDateFormat("yyyy-MM-dd", Locale.US),
SimpleDateFormat("MM/dd/yyyy", Locale.US),
)
var parsedDate: Date? = null
for (fmt in formats) {
try {
parsedDate = fmt.parse(rawValue)
} catch (e: ParseException) {
// ignored
}
}
if (parsedDate == null) {
throw Exception("Unrecognized date format: $rawValue")
}
val parsedCalendar = GregorianCalendar()
parsedCalendar.time = parsedDate
return Timestamp.from(
parsedCalendar[YEAR],
parsedCalendar[MONTH],
parsedCalendar[DAY_OF_MONTH],
)
}
private fun parseInt(rawValue: String): Int {
return try {
rawValue.toInt()
} catch (e: NumberFormatException) {
logger.error("Could not parse int: $rawValue. Replacing by zero.")
0
}
}
} }

@ -56,6 +56,20 @@ class ImportTest : BaseUnitTest() {
assertFalse(isChecked(habit, 2016, 3, 20)) assertFalse(isChecked(habit, 2016, 3, 20))
} }
@Test
@Throws(IOException::class)
fun testHabitBullCSV2() {
importFromFile("habitbull2.csv")
assertThat(habitList.size(), equalTo(6))
val habit = habitList.getByPosition(2)
assertThat(habit.name, equalTo("H3"))
assertThat(habit.description, equalTo("Habit 3"))
assertThat(habit.frequency, equalTo(Frequency.DAILY))
assertTrue(isChecked(habit, 2019, 4, 11))
assertTrue(isChecked(habit, 2019, 5, 7))
assertFalse(isChecked(habit, 2019, 6, 14))
}
@Test @Test
@Throws(IOException::class) @Throws(IOException::class)
fun testLoopDB() { fun testLoopDB() {
@ -129,7 +143,7 @@ class ImportTest : BaseUnitTest() {
), ),
RewireDBImporter(habitList, modelFactory, databaseOpener), RewireDBImporter(habitList, modelFactory, databaseOpener),
TickmateDBImporter(habitList, modelFactory, databaseOpener), TickmateDBImporter(habitList, modelFactory, databaseOpener),
HabitBullCSVImporter(habitList, modelFactory) HabitBullCSVImporter(habitList, modelFactory, StandardLogging())
) )
assertTrue(importer.canHandle(file)) assertTrue(importer.canHandle(file))
importer.importHabitsFromFile(file) importer.importHabitsFromFile(file)

Loading…
Cancel
Save