1
1
package ai.elimu.analytics.task
2
2
3
+ import ai.elimu.analytics.BuildConfig
4
+ import ai.elimu.analytics.db.RoomDb
3
5
import ai.elimu.analytics.db.getAllEvents
4
6
import ai.elimu.analytics.enum.EventType
5
7
import ai.elimu.analytics.entity.AssessmentEvent
6
8
import ai.elimu.analytics.entity.LearningEvent
7
9
import ai.elimu.analytics.enum.getCSVHeaders
8
10
import ai.elimu.analytics.enum.getUploadCsvFile
11
+ import ai.elimu.analytics.util.SharedPreferencesHelper
9
12
import ai.elimu.analytics.util.VersionHelper.getAppVersionCode
10
13
import android.content.Context
11
14
import androidx.work.Worker
@@ -14,6 +17,7 @@ import org.apache.commons.csv.CSVFormat
14
17
import org.apache.commons.csv.CSVPrinter
15
18
import org.apache.commons.io.FileUtils
16
19
import timber.log.Timber
20
+ import java.io.File
17
21
import java.io.IOException
18
22
import java.io.StringWriter
19
23
import java.text.SimpleDateFormat
@@ -34,6 +38,7 @@ class ExportEventsToCsvWorker(context: Context, workerParams: WorkerParameters)
34
38
for (eventType in EventType .entries) {
35
39
exportAnalyticsEventsToCsv(eventType = eventType)
36
40
}
41
+ exportNumberAssessmentEvents()
37
42
38
43
return Result .success()
39
44
}
@@ -88,4 +93,69 @@ class ExportEventsToCsvWorker(context: Context, workerParams: WorkerParameters)
88
93
Timber .e(e)
89
94
}
90
95
}
96
+
97
+ private fun exportNumberAssessmentEvents () {
98
+ Timber .i(" exportNumberAssessmentEvents" )
99
+
100
+ // Read all the events from the database
101
+ val roomDb = RoomDb .getDatabase(applicationContext)
102
+ val numberAssessmentEventDao = roomDb.numberAssessmentEventDao()
103
+ val events = numberAssessmentEventDao.loadAllOrderedByTimestamp(isAsc = true )
104
+ Timber .i(" events.size: ${events.size} " )
105
+
106
+ // Generate one CSV file per day of events, e.g:
107
+ // lang-THA/number-assessment-events/5b7c682a12ecbe2e_4000021_number-assessment-events_2025-06-29.csv
108
+ // lang-THA/number-assessment-events/5b7c682a12ecbe2e_4000021_number-assessment-events_2025-06-30.csv
109
+ var stringWriter: StringWriter ? = null
110
+ var csvPrinter: CSVPrinter ? = null
111
+ var dateOfPreviousEvent: String? = null
112
+ for (event in events) {
113
+ // Get the event's date in ISO format, e.g. "2025-06-29"
114
+ val date: String = eventDateFormat.format(event.time.time)
115
+
116
+ // Prepare the CSV file path
117
+ val languageDir = File (applicationContext.filesDir, " lang-${SharedPreferencesHelper .getLanguage(applicationContext)} " )
118
+ val eventsDir = File (languageDir, " number-assessment-events" )
119
+ val csvFile = File (eventsDir, " ${event.androidId} _${BuildConfig .VERSION_CODE } _number-assessment-events_${date} .csv" )
120
+
121
+ if (date != dateOfPreviousEvent) {
122
+ // Reset file content, and prepare the headers for a new CSV file
123
+ Timber .i(" csvFile: ${csvFile} " )
124
+ stringWriter = StringWriter ()
125
+ csvPrinter = CSVPrinter (stringWriter, CSVFormat .DEFAULT .builder().setHeader(
126
+ " id" ,
127
+ " timestamp" ,
128
+ " package_name" ,
129
+ " mastery_score" ,
130
+ " time_spent_ms" ,
131
+ " additional_data" ,
132
+ " research_experiment" ,
133
+ " experiment_group" ,
134
+ " number_value" ,
135
+ " number_id"
136
+ ).get())
137
+ }
138
+ csvPrinter?.printRecord(
139
+ event.id,
140
+ event.time.timeInMillis / 1_000 ,
141
+ event.packageName,
142
+ event.masteryScore,
143
+ event.timeSpentMs,
144
+ event.additionalData,
145
+ event.researchExperiment?.ordinal,
146
+ event.experimentGroup?.ordinal,
147
+ event.numberValue,
148
+ event.numberId
149
+ )
150
+ csvPrinter?.flush()
151
+
152
+ // Write the content to the CSV file
153
+ val csvFileContent = stringWriter.toString()
154
+ FileUtils .writeStringToFile(csvFile, csvFileContent, " UTF-8" )
155
+
156
+ dateOfPreviousEvent = date
157
+ }
158
+
159
+ Timber .i(" exportNumberAssessmentEvents complete!" )
160
+ }
91
161
}
0 commit comments