Skip to content

Commit 02fa2d3

Browse files
authored
feat: export csvs (#2204)
2 parents 2fcaa29 + 32af4cd commit 02fa2d3

17 files changed

+423
-414
lines changed

pom-dependency-tree.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ai.elimu:webapp:war:2.6.17-SNAPSHOT
1+
ai.elimu:webapp:war:2.6.20-SNAPSHOT
22
+- ai.elimu:model:jar:model-2.0.97:compile
33
| \- com.google.code.gson:gson:jar:2.13.0:compile
44
| \- com.google.errorprone:error_prone_annotations:jar:2.37.0:compile

src/main/java/ai/elimu/web/analytics/MainAnalyticsController.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package ai.elimu.web.analytics;
22

3+
import ai.elimu.dao.LetterSoundAssessmentEventDao;
34
import ai.elimu.dao.LetterSoundLearningEventDao;
45
import ai.elimu.dao.StoryBookLearningEventDao;
6+
import ai.elimu.dao.StudentDao;
57
import ai.elimu.dao.VideoLearningEventDao;
68
import ai.elimu.dao.WordLearningEventDao;
9+
import ai.elimu.entity.analytics.LetterSoundLearningEvent;
710
import lombok.RequiredArgsConstructor;
811
import lombok.extern.slf4j.Slf4j;
912
import org.springframework.stereotype.Controller;
@@ -17,10 +20,16 @@
1720
@Slf4j
1821
public class MainAnalyticsController {
1922

23+
private final StudentDao studentDao;
24+
25+
private final LetterSoundAssessmentEventDao letterSoundAssessmentEventDao;
2026
private final LetterSoundLearningEventDao letterSoundLearningEventDao;
2127

28+
// private final WordAssessmentEventDao wordAssessmentEventDao;
2229
private final WordLearningEventDao wordLearningEventDao;
2330

31+
// TODO: Numbers
32+
2433
private final StoryBookLearningEventDao storyBookLearningEventDao;
2534

2635
private final VideoLearningEventDao videoLearningEventDao;
@@ -29,9 +38,16 @@ public class MainAnalyticsController {
2938
public String handleRequest(Model model) {
3039
log.info("handleRequest");
3140

41+
model.addAttribute("studentCount", studentDao.readCount());
42+
43+
model.addAttribute("letterSoundAssessmentEventCount", letterSoundAssessmentEventDao.readCount());
3244
model.addAttribute("letterSoundLearningEventCount", letterSoundLearningEventDao.readCount());
45+
46+
// model.addAttribute("wordAssessmentEventCount", wordAssessmentEventDao.readCount());
3347
model.addAttribute("wordLearningEventCount", wordLearningEventDao.readCount());
48+
3449
model.addAttribute("storyBookLearningEventCount", storyBookLearningEventDao.readCount());
50+
3551
model.addAttribute("videoLearningEventCount", videoLearningEventDao.readCount());
3652

3753
return "analytics/main";

src/main/java/ai/elimu/web/analytics/students/LetterSoundAssessmentEventCsvExportController.java renamed to src/main/java/ai/elimu/web/analytics/students/LetterSoundAssessmentEventsCsvExportController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
@RequestMapping("/analytics/students/{studentId}/letter-sound-assessment-events.csv")
2323
@RequiredArgsConstructor
2424
@Slf4j
25-
public class LetterSoundAssessmentEventCsvExportController {
25+
public class LetterSoundAssessmentEventsCsvExportController {
2626

2727
private final StudentDao studentDao;
2828

src/main/java/ai/elimu/web/analytics/LetterSoundLearningEventCsvExportController.java renamed to src/main/java/ai/elimu/web/analytics/students/LetterSoundLearningEventsCsvExportController.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
package ai.elimu.web.analytics;
1+
package ai.elimu.web.analytics.students;
22

33
import ai.elimu.dao.LetterSoundLearningEventDao;
4+
import ai.elimu.dao.StudentDao;
45
import ai.elimu.entity.analytics.LetterSoundLearningEvent;
5-
import ai.elimu.util.AnalyticsHelper;
6+
import ai.elimu.entity.analytics.students.Student;
67
import jakarta.servlet.http.HttpServletResponse;
78
import java.io.IOException;
89
import java.io.OutputStream;
@@ -14,39 +15,41 @@
1415
import org.apache.commons.csv.CSVPrinter;
1516
import org.springframework.stereotype.Controller;
1617
import org.springframework.web.bind.annotation.GetMapping;
18+
import org.springframework.web.bind.annotation.PathVariable;
1719
import org.springframework.web.bind.annotation.RequestMapping;
1820

1921
@Controller
20-
@RequestMapping("/analytics/letter-sound-learning-event/list/letter-sound-learning-events.csv")
22+
@RequestMapping("/analytics/students/{studentId}/letter-sound-learning-events.csv")
2123
@RequiredArgsConstructor
2224
@Slf4j
23-
public class LetterSoundLearningEventCsvExportController {
25+
public class LetterSoundLearningEventsCsvExportController {
26+
27+
private final StudentDao studentDao;
2428

2529
private final LetterSoundLearningEventDao letterSoundLearningEventDao;
2630

2731
@GetMapping
2832
public void handleRequest(
33+
@PathVariable Long studentId,
2934
HttpServletResponse response,
3035
OutputStream outputStream
3136
) throws IOException {
3237
log.info("handleRequest");
3338

34-
List<LetterSoundLearningEvent> letterSoundLearningEvents = letterSoundLearningEventDao.readAll();
39+
Student student = studentDao.read(studentId);
40+
log.info("student.getAndroidId(): " + student.getAndroidId());
41+
42+
List<LetterSoundLearningEvent> letterSoundLearningEvents = letterSoundLearningEventDao.readAll(student.getAndroidId());
3543
log.info("letterSoundLearningEvents.size(): " + letterSoundLearningEvents.size());
36-
for (LetterSoundLearningEvent letterSoundLearningEvent : letterSoundLearningEvents) {
37-
letterSoundLearningEvent.setAndroidId(AnalyticsHelper.redactAndroidId(letterSoundLearningEvent.getAndroidId()));
38-
}
3944

4045
CSVFormat csvFormat = CSVFormat.DEFAULT.builder()
4146
.setHeader(
42-
"id", // The Room database ID
47+
"id",
4348
"timestamp",
44-
"android_id",
4549
"package_name",
50+
// "letter_sound_letters",
51+
// "letter_sound_sounds",
4652
"letter_sound_id",
47-
"letter_sound_letters",
48-
"letter_sound_sounds",
49-
"learning_event_type",
5053
"additional_data"
5154
)
5255
.build();
@@ -60,12 +63,10 @@ public void handleRequest(
6063
csvPrinter.printRecord(
6164
letterSoundLearningEvent.getId(),
6265
letterSoundLearningEvent.getTimestamp().getTimeInMillis(),
63-
letterSoundLearningEvent.getAndroidId(),
6466
letterSoundLearningEvent.getPackageName(),
67+
// letterSoundLearningEvent.getLetterSoundLetters(),
68+
// letterSoundLearningEvent.getLetterSoundSounds(),
6569
letterSoundLearningEvent.getLetterSoundId(),
66-
new String[] {}, // TODO
67-
new String[] {}, // TODO
68-
letterSoundLearningEvent.getLearningEventType(),
6970
letterSoundLearningEvent.getAdditionalData()
7071
);
7172
}

src/main/java/ai/elimu/web/analytics/students/StudentController.java

Lines changed: 45 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ public String handleRequest(@PathVariable Long studentId, Model model) {
6060
model.addAttribute("numeracySkills", NumeracySkill.values());
6161

6262

63+
// Generate a list of weeks from 6 months ago until now
64+
List<String> weekList = new ArrayList<>();
65+
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-ww");
66+
Calendar calendar6MonthsAgo = Calendar.getInstance();
67+
calendar6MonthsAgo.add(Calendar.MONTH, -6);
68+
Calendar calendarNow = Calendar.getInstance();
69+
Calendar week = calendar6MonthsAgo;
70+
while (!week.after(calendarNow)) {
71+
String weekAsString = simpleDateFormat.format(week.getTime());
72+
weekList.add(weekAsString);
73+
week.add(Calendar.WEEK_OF_YEAR, 1);
74+
}
75+
model.addAttribute("weekList", weekList);
76+
77+
6378
List<LetterSoundAssessmentEvent> letterSoundAssessmentEvents = letterSoundAssessmentEventDao.readAll(student.getAndroidId());
6479
model.addAttribute("letterSoundAssessmentEvents", letterSoundAssessmentEvents);
6580

@@ -69,99 +84,69 @@ public String handleRequest(@PathVariable Long studentId, Model model) {
6984

7085
// Prepare chart data - WordLearningEvents
7186
List<WordLearningEvent> wordLearningEvents = wordLearningEventDao.readAll(student.getAndroidId());
72-
List<String> wordMonthList = new ArrayList<>();
7387
List<Integer> wordEventCountList = new ArrayList<>();
7488
if (!wordLearningEvents.isEmpty()) {
75-
// Group event count by month (e.g. "Aug-2024", "Sep-2024")
76-
Map<String, Integer> eventCountByMonthMap = new HashMap<>();
77-
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM-yyyy");
89+
// Group event count by week (e.g. "2024-09", "2024-26")
90+
Map<String, Integer> eventCountByWeekMap = new HashMap<>();
7891
for (WordLearningEvent event : wordLearningEvents) {
79-
String eventMonth = simpleDateFormat.format(event.getTimestamp().getTime());
80-
eventCountByMonthMap.put(eventMonth, eventCountByMonthMap.getOrDefault(eventMonth, 0) + 1);
92+
String eventWeek = simpleDateFormat.format(event.getTimestamp().getTime());
93+
eventCountByWeekMap.put(eventWeek, eventCountByWeekMap.getOrDefault(eventWeek, 0) + 1);
8194
}
8295

83-
// Iterate each month from 4 years ago until now
84-
Calendar calendar4YearsAgo = Calendar.getInstance();
85-
calendar4YearsAgo.add(Calendar.YEAR, -4);
86-
Calendar calendarNow = Calendar.getInstance();
87-
Calendar month = calendar4YearsAgo;
88-
while (!month.after(calendarNow)) {
89-
String monthAsString = simpleDateFormat.format(month.getTime());
90-
wordMonthList.add(monthAsString);
91-
92-
wordEventCountList.add(eventCountByMonthMap.getOrDefault(monthAsString, 0));
93-
94-
// Increase the date by 1 month
95-
month.add(Calendar.MONTH, 1);
96+
// Iterate each week from 6 months ago until now
97+
week = calendar6MonthsAgo;
98+
while (!week.after(calendarNow)) {
99+
String weekAsString = simpleDateFormat.format(week.getTime());
100+
wordEventCountList.add(eventCountByWeekMap.getOrDefault(weekAsString, 0));
101+
week.add(Calendar.WEEK_OF_YEAR, 1);
96102
}
97103
}
98-
model.addAttribute("wordMonthList", wordMonthList);
99104
model.addAttribute("wordEventCountList", wordEventCountList);
100105
model.addAttribute("wordLearningEvents", wordLearningEvents);
101106

102107

103108
// Prepare chart data - StoryBookLearningEvents
104109
List<StoryBookLearningEvent> storyBookLearningEvents = storyBookLearningEventDao.readAll(student.getAndroidId());
105-
List<String> storyBookMonthList = new ArrayList<>();
106110
List<Integer> storyBookEventCountList = new ArrayList<>();
107111
if (!storyBookLearningEvents.isEmpty()) {
108-
// Group event count by month (e.g. "Aug-2024", "Sep-2024")
109-
Map<String, Integer> eventCountByMonthMap = new HashMap<>();
110-
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM-yyyy");
112+
// Group event count by week (e.g. "2024-09", "2024-26")
113+
Map<String, Integer> eventCountByWeekMap = new HashMap<>();
111114
for (StoryBookLearningEvent event : storyBookLearningEvents) {
112-
String eventMonth = simpleDateFormat.format(event.getTimestamp().getTime());
113-
eventCountByMonthMap.put(eventMonth, eventCountByMonthMap.getOrDefault(eventMonth, 0) + 1);
115+
String eventWeek = simpleDateFormat.format(event.getTimestamp().getTime());
116+
eventCountByWeekMap.put(eventWeek, eventCountByWeekMap.getOrDefault(eventWeek, 0) + 1);
114117
}
115118

116-
// Iterate each month from 4 years ago until now
117-
Calendar calendar4YearsAgo = Calendar.getInstance();
118-
calendar4YearsAgo.add(Calendar.YEAR, -4);
119-
Calendar calendarNow = Calendar.getInstance();
120-
Calendar month = calendar4YearsAgo;
121-
while (!month.after(calendarNow)) {
122-
String monthAsString = simpleDateFormat.format(month.getTime());
123-
storyBookMonthList.add(monthAsString);
124-
125-
storyBookEventCountList.add(eventCountByMonthMap.getOrDefault(monthAsString, 0));
126-
127-
// Increase the date by 1 month
128-
month.add(Calendar.MONTH, 1);
119+
// Iterate each week from 6 months ago until now
120+
week = calendar6MonthsAgo;
121+
while (!week.after(calendarNow)) {
122+
String weekAsString = simpleDateFormat.format(week.getTime());
123+
storyBookEventCountList.add(eventCountByWeekMap.getOrDefault(weekAsString, 0));
124+
week.add(Calendar.WEEK_OF_YEAR, 1);
129125
}
130126
}
131-
model.addAttribute("storyBookMonthList", storyBookMonthList);
132127
model.addAttribute("storyBookEventCountList", storyBookEventCountList);
133128
model.addAttribute("storyBookLearningEvents", storyBookLearningEvents);
134129

135130

136131
// Prepare chart data - VideoLearningEvents
137132
List<VideoLearningEvent> videoLearningEvents = videoLearningEventDao.readAll(student.getAndroidId());
138-
List<String> videoMonthList = new ArrayList<>();
139133
List<Integer> videoEventCountList = new ArrayList<>();
140134
if (!videoLearningEvents.isEmpty()) {
141-
// Group event count by month (e.g. "Aug-2024", "Sep-2024")
142-
Map<String, Integer> eventCountByMonthMap = new HashMap<>();
143-
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM-yyyy");
135+
// Group event count by week (e.g. "2024-09", "2024-26")
136+
Map<String, Integer> eventCountByWeekMap = new HashMap<>();
144137
for (VideoLearningEvent event : videoLearningEvents) {
145-
String eventMonth = simpleDateFormat.format(event.getTimestamp().getTime());
146-
eventCountByMonthMap.put(eventMonth, eventCountByMonthMap.getOrDefault(eventMonth, 0) + 1);
138+
String eventWeek = simpleDateFormat.format(event.getTimestamp().getTime());
139+
eventCountByWeekMap.put(eventWeek, eventCountByWeekMap.getOrDefault(eventWeek, 0) + 1);
147140
}
148141

149-
// Iterate each month from 4 years ago until now
150-
Calendar calendar4YearsAgo = Calendar.getInstance();
151-
calendar4YearsAgo.add(Calendar.YEAR, -4);
152-
Calendar calendarNow = Calendar.getInstance();
153-
Calendar month = calendar4YearsAgo;
154-
while (!month.after(calendarNow)) {
155-
String monthAsString = simpleDateFormat.format(month.getTime());
156-
videoMonthList.add(monthAsString);
157-
158-
videoEventCountList.add(eventCountByMonthMap.getOrDefault(monthAsString, 0));
159-
160-
// Increase the date by 1 month
161-
month.add(Calendar.MONTH, 1);
142+
// Iterate each week from 6 months ago until now
143+
week = calendar6MonthsAgo;
144+
while (!week.after(calendarNow)) {
145+
String weekAsString = simpleDateFormat.format(week.getTime());
146+
videoEventCountList.add(eventCountByWeekMap.getOrDefault(weekAsString, 0));
147+
week.add(Calendar.WEEK_OF_YEAR, 1);
162148
}
163149
}
164-
model.addAttribute("videoMonthList", videoMonthList);
165150
model.addAttribute("videoEventCountList", videoEventCountList);
166151
model.addAttribute("videoLearningEvents", videoLearningEvents);
167152

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package ai.elimu.web.analytics.students;
2+
3+
import ai.elimu.dao.StoryBookLearningEventDao;
4+
import ai.elimu.dao.StudentDao;
5+
import ai.elimu.dao.WordLearningEventDao;
6+
import ai.elimu.entity.analytics.students.Student;
7+
import ai.elimu.util.AnalyticsHelper;
8+
import lombok.RequiredArgsConstructor;
9+
import lombok.extern.slf4j.Slf4j;
10+
11+
import java.util.List;
12+
13+
import org.springframework.stereotype.Controller;
14+
import org.springframework.ui.Model;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.RequestMapping;
17+
18+
@Controller
19+
@RequestMapping("/analytics/students")
20+
@RequiredArgsConstructor
21+
@Slf4j
22+
public class StudentListController {
23+
24+
private final StudentDao studentDao;
25+
26+
@GetMapping
27+
public String handleRequest(Model model) {
28+
log.info("handleRequest");
29+
30+
List<Student> students = studentDao.readAll();
31+
log.info("students.size(): " + students.size());
32+
for (Student student : students) {
33+
student.setAndroidId(AnalyticsHelper.redactAndroidId(student.getAndroidId()));
34+
}
35+
model.addAttribute("students", students);
36+
37+
return "analytics/students/list";
38+
}
39+
}

0 commit comments

Comments
 (0)