feat: add csv auto export
This commit is contained in:
@@ -10,7 +10,11 @@ import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.SortedMap
|
||||
|
||||
|
||||
object LoggerContract {
|
||||
@@ -30,6 +34,7 @@ object LoggerContract {
|
||||
|
||||
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${LogEntry.TABLE_NAME}"
|
||||
|
||||
|
||||
class LoggerDbHelper(context: Context) :
|
||||
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
@@ -60,6 +65,9 @@ object LoggerContract {
|
||||
private val dbHelper = LoggerDbHelper(context)
|
||||
private val dbWrite = dbHelper.writableDatabase
|
||||
private val dbRead = dbHelper.writableDatabase
|
||||
|
||||
private val tag = "LoggerDb"
|
||||
|
||||
val context: Context = context
|
||||
|
||||
fun writeLog(payload: Any): Long? {
|
||||
@@ -67,8 +75,6 @@ object LoggerContract {
|
||||
val jsonString = gson.toJson(payload)
|
||||
val ts = Instant.now().toString()
|
||||
|
||||
Log.i("Database", jsonString)
|
||||
|
||||
val values = ContentValues().apply {
|
||||
put(LogEntry.COLUMN_NAME_TS, ts)
|
||||
put(LogEntry.COLUMN_NAME_PAYLOAD, jsonString)
|
||||
@@ -77,6 +83,21 @@ object LoggerContract {
|
||||
return dbWrite?.insert(LogEntry.TABLE_NAME, null, values)
|
||||
}
|
||||
|
||||
fun getExportFileUri(): Uri? {
|
||||
val file = File(context.filesDir, "export.csv")
|
||||
if (!file.exists()) {
|
||||
file.createNewFile()
|
||||
}
|
||||
|
||||
file.setReadable(true, false)
|
||||
|
||||
return Uri.fromFile(file)
|
||||
}
|
||||
|
||||
fun exportToCsv() {
|
||||
val uri = getExportFileUri() ?: return
|
||||
exportToUri(uri)
|
||||
}
|
||||
|
||||
fun exportToUri(uri: Uri) {
|
||||
val projection =
|
||||
@@ -97,7 +118,7 @@ object LoggerContract {
|
||||
try {
|
||||
|
||||
val gson = Gson()
|
||||
val type = object : TypeToken<HashMap<String, Any>>() {}.type
|
||||
|
||||
var headerWritten = false
|
||||
val sep = ","
|
||||
|
||||
@@ -109,30 +130,26 @@ object LoggerContract {
|
||||
val ts = getString(getColumnIndexOrThrow(LogEntry.COLUMN_NAME_TS))
|
||||
val storedField =
|
||||
getString(getColumnIndexOrThrow(LogEntry.COLUMN_NAME_PAYLOAD))
|
||||
|
||||
val payload = parsePayload(storedField)
|
||||
try {
|
||||
val payload: HashMap<String, Any> = gson.fromJson(storedField, type)
|
||||
|
||||
if (!headerWritten) {
|
||||
val headerRow =
|
||||
"timestamp" + sep + payload.keys.joinToString(sep) + newLine
|
||||
"timestamp" + sep + "local_time" + sep + payload.keys.joinToString(sep) + newLine
|
||||
writer.write(headerRow.toByteArray())
|
||||
|
||||
headerWritten = true
|
||||
}
|
||||
|
||||
val row = ts + sep + payload.values.joinToString(sep) + newLine
|
||||
val localTime = convertIsoToLocalTime(ts)
|
||||
val row = ts + sep + localTime + sep + payload.values.joinToString(sep) + newLine
|
||||
|
||||
writer.write(row.toByteArray())
|
||||
} catch (exception: JsonSyntaxException) {
|
||||
Log.e("db", exception.toString())
|
||||
Log.e(tag, exception.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
truncate()
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
@@ -140,9 +157,80 @@ object LoggerContract {
|
||||
cursor.close()
|
||||
}
|
||||
|
||||
private fun truncate() {
|
||||
dbWrite.execSQL("DELETE FROM ${LogEntry.TABLE_NAME}");
|
||||
dbWrite.execSQL("VACUUM");
|
||||
fun exportToMultipleCSV() {
|
||||
val projection =
|
||||
arrayOf(BaseColumns._ID, LogEntry.COLUMN_NAME_PAYLOAD, LogEntry.COLUMN_NAME_TS)
|
||||
|
||||
val sortOrder = "${BaseColumns._ID} ASC"
|
||||
|
||||
val cursor = dbRead.query(
|
||||
LogEntry.TABLE_NAME, // The table to query
|
||||
projection, // The array of columns to return (pass null to get all)
|
||||
null, // The columns for the WHERE clause
|
||||
null, // The values for the WHERE clause
|
||||
null, // don't group the rows
|
||||
null, // don't filter by row groups
|
||||
sortOrder // The sort order
|
||||
)
|
||||
|
||||
val files = HashMap<String, File>()
|
||||
|
||||
try {
|
||||
val sep = ","
|
||||
val newLine = '\n'
|
||||
|
||||
with(cursor) {
|
||||
while (moveToNext()) {
|
||||
val ts = getString(getColumnIndexOrThrow(LogEntry.COLUMN_NAME_TS))
|
||||
val storedField =
|
||||
getString(getColumnIndexOrThrow(LogEntry.COLUMN_NAME_PAYLOAD))
|
||||
|
||||
try {
|
||||
val payload = parsePayload(storedField)
|
||||
val deviceId = payload.getOrDefault("bleAddress", "unknown") as String
|
||||
val fileName = "kirby_export_${deviceId.replace(":", "")}.csv"
|
||||
|
||||
val f = files.getOrElse(deviceId) {
|
||||
val file = File(context.filesDir, fileName)
|
||||
if (!file.exists()) {
|
||||
file.createNewFile()
|
||||
}
|
||||
|
||||
val headerRow =
|
||||
"timestamp" + sep + "local_time" + sep + payload.keys.joinToString(
|
||||
sep
|
||||
) + newLine
|
||||
file.writeText(headerRow)
|
||||
|
||||
files[deviceId] = file
|
||||
|
||||
Log.i(tag, file.absolutePath)
|
||||
file.setReadable(true, false)
|
||||
file
|
||||
}
|
||||
|
||||
val localTime = convertIsoToLocalTime(ts)
|
||||
|
||||
val row =
|
||||
ts + sep + localTime + sep + payload.values.joinToString(sep) + newLine
|
||||
|
||||
f.appendText(row)
|
||||
} catch (exception: JsonSyntaxException) {
|
||||
Log.e(tag, exception.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
cursor.close()
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
dbWrite.execSQL("DELETE FROM ${LogEntry.TABLE_NAME}")
|
||||
dbWrite.execSQL("VACUUM")
|
||||
}
|
||||
|
||||
fun close() {
|
||||
@@ -152,7 +240,16 @@ object LoggerContract {
|
||||
}
|
||||
}
|
||||
|
||||
fun parsePayload(payload: String): SortedMap<String, Any> {
|
||||
val type = object : TypeToken<HashMap<String, Any>>() {}.type
|
||||
val gson = Gson()
|
||||
val parsed : HashMap<String, Any> = gson.fromJson(payload, type)
|
||||
return parsed.toSortedMap()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun convertIsoToLocalTime(isoDateTime: String): String {
|
||||
val systemZone = ZoneId.systemDefault()
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
return Instant.parse(isoDateTime).atZone(systemZone).format(formatter)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user