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.ModelFactory
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
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.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -39,8 +47,11 @@ class HabitBullCSVImporter
|
||||
@Inject constructor(
|
||||
private val habitList: HabitList,
|
||||
private val modelFactory: ModelFactory,
|
||||
logging: Logging,
|
||||
) : AbstractImporter() {
|
||||
|
||||
val logger = logging.getLogger("HabitBullCSVImporter")
|
||||
|
||||
override fun canHandle(file: File): Boolean {
|
||||
val reader = BufferedReader(FileReader(file))
|
||||
val line = reader.readLine()
|
||||
@@ -50,19 +61,11 @@ class HabitBullCSVImporter
|
||||
override fun importHabitsFromFile(file: File) {
|
||||
val reader = CSVReader(FileReader(file))
|
||||
val map = HashMap<String, Habit>()
|
||||
for (line in reader) {
|
||||
val name = line[0]
|
||||
for (cols in reader) {
|
||||
val name = cols[0]
|
||||
if (name == "HabitName") continue
|
||||
val description = line[1]
|
||||
val dateString = line[3].split("-").toTypedArray()
|
||||
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
|
||||
val description = cols[1]
|
||||
val timestamp = parseTimestamp(cols[3])
|
||||
var h = map[name]
|
||||
if (h == null) {
|
||||
h = modelFactory.buildHabit()
|
||||
@@ -71,8 +74,46 @@ class HabitBullCSVImporter
|
||||
h.frequency = Frequency.DAILY
|
||||
habitList.add(h)
|
||||
map[name] = h
|
||||
logger.info("Creating habit: $name")
|
||||
}
|
||||
if (parseInt(cols[4]) == 1) {
|
||||
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))
|
||||
}
|
||||
|
||||
@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
|
||||
@Throws(IOException::class)
|
||||
fun testLoopDB() {
|
||||
@@ -129,7 +143,7 @@ class ImportTest : BaseUnitTest() {
|
||||
),
|
||||
RewireDBImporter(habitList, modelFactory, databaseOpener),
|
||||
TickmateDBImporter(habitList, modelFactory, databaseOpener),
|
||||
HabitBullCSVImporter(habitList, modelFactory)
|
||||
HabitBullCSVImporter(habitList, modelFactory, StandardLogging())
|
||||
)
|
||||
assertTrue(importer.canHandle(file))
|
||||
importer.importHabitsFromFile(file)
|
||||
|
||||
Reference in New Issue
Block a user