feat: add csv auto export

This commit is contained in:
Stefan Zollinger
2024-04-12 12:13:02 +02:00
parent 62ce221860
commit a5425083da
8 changed files with 189 additions and 89 deletions

View File

@@ -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)
}