mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
HabitBullCSVImporter: Accept multiple date formats
Fixes 762
This commit is contained in:
82
uhabits-core/assets/test/habitbull2.csv
Normal file
82
uhabits-core/assets/test/habitbull2.csv
Normal file
@@ -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,
|
||||||
|
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user