diff --git a/pom-dependency-tree.txt b/pom-dependency-tree.txt index c71d92ea4..1e4351075 100644 --- a/pom-dependency-tree.txt +++ b/pom-dependency-tree.txt @@ -1,5 +1,5 @@ -ai.elimu:webapp:war:2.6.108-SNAPSHOT -+- ai.elimu:model:jar:model-2.0.117:compile +ai.elimu:webapp:war:2.6.109-SNAPSHOT ++- ai.elimu:model:jar:model-2.0.118:compile | \- com.google.code.gson:gson:jar:2.13.1:compile | \- com.google.errorprone:error_prone_annotations:jar:2.38.0:compile +- org.springframework:spring-context:jar:6.0.11:compile diff --git a/pom.xml b/pom.xml index cc1068157..4fd07d6e1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 17 UTF-8 - 2.0.117 + 2.0.118 6.1.7.Final 11.0.24 6.0.11 diff --git a/src/main/java/ai/elimu/entity/analytics/LetterSoundAssessmentEvent.java b/src/main/java/ai/elimu/entity/analytics/LetterSoundAssessmentEvent.java index db91ef8cc..b80043cac 100644 --- a/src/main/java/ai/elimu/entity/analytics/LetterSoundAssessmentEvent.java +++ b/src/main/java/ai/elimu/entity/analytics/LetterSoundAssessmentEvent.java @@ -1,6 +1,10 @@ package ai.elimu.entity.analytics; +import java.util.List; + import ai.elimu.entity.content.LetterSound; +import ai.elimu.entity.converters.StringListConverter; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotNull; @@ -16,13 +20,15 @@ public class LetterSoundAssessmentEvent extends AssessmentEvent { * The sequence of letters. E.g. "sh". */ @NotNull - private String letterSoundLetters; + @Convert(converter = StringListConverter.class) + private List letterSoundLetters; /** * The sequence of sounds (IPA values). E.g. "ʃ". */ @NotNull - private String letterSoundSounds; + @Convert(converter = StringListConverter.class) + private List letterSoundSounds; /** * This field might not be included, e.g. if the assessment task was done in a diff --git a/src/main/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelper.java b/src/main/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelper.java index 520f7bc20..1dfbda0ab 100644 --- a/src/main/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelper.java +++ b/src/main/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelper.java @@ -35,86 +35,6 @@ */ @Slf4j public class CsvAnalyticsExtractionHelper { - - public static List extractLetterSoundAssessmentEvents(File csvFile) { - log.info("extractLetterSoundAssessmentEvents"); - - Integer versionCode = AnalyticsHelper.extractVersionCodeFromCsvFilename(csvFile.getName()); - log.info("versionCode: " + versionCode); - - List letterSoundAssessmentEvents = new ArrayList<>(); - - // Iterate each row in the CSV file - Path csvFilePath = Paths.get(csvFile.toURI()); - log.info("csvFilePath: " + csvFilePath); - try { - Reader reader = Files.newBufferedReader(csvFilePath); - CSVFormat csvFormat = CSVFormat.DEFAULT.withFirstRecordAsHeader(); - log.info("header: " + Arrays.toString(csvFormat.getHeader())); - CSVParser csvParser = new CSVParser(reader, csvFormat); - for (CSVRecord csvRecord : csvParser) { - log.info("csvRecord: " + csvRecord); - - // Convert from CSV to Java - - LetterSoundAssessmentEvent letterSoundAssessmentEvent = new LetterSoundAssessmentEvent(); - - long timestampInMillis = Long.valueOf(csvRecord.get("timestamp")); - Calendar timestamp = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - timestamp.setTimeInMillis(timestampInMillis); - letterSoundAssessmentEvent.setTimestamp(timestamp); - - String androidId = AnalyticsHelper.extractAndroidIdFromCsvFilename(csvFile.getName()); - letterSoundAssessmentEvent.setAndroidId(androidId); - - String packageName = csvRecord.get("package_name"); - letterSoundAssessmentEvent.setPackageName(packageName); - - Float masteryScore = Float.valueOf(csvRecord.get("mastery_score")); - letterSoundAssessmentEvent.setMasteryScore(masteryScore); - - Long timeSpentMs = Long.valueOf(csvRecord.get("time_spent_ms")); - letterSoundAssessmentEvent.setTimeSpentMs(timeSpentMs); - - String additionalData = csvRecord.get("additional_data"); - if (StringUtils.isNotBlank(additionalData)) { - letterSoundAssessmentEvent.setAdditionalData(additionalData); - } - - if (versionCode >= 3005009) { - // https://github.com/elimu-ai/analytics/releases/tag/3.5.9 - - if (StringUtils.isNotBlank(csvRecord.get("research_experiment"))) { - int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); - ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; - letterSoundAssessmentEvent.setResearchExperiment(researchExperiment); - } - - if (StringUtils.isNotBlank(csvRecord.get("experiment_group"))) { - int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); - ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; - letterSoundAssessmentEvent.setExperimentGroup(experimentGroup); - } - } - - String letterSoundLetters = csvRecord.get("letter_sound_letters"); - letterSoundAssessmentEvent.setLetterSoundLetters(letterSoundLetters); - - String letterSoundSounds = csvRecord.get("letter_sound_sounds"); - letterSoundAssessmentEvent.setLetterSoundLetters(letterSoundSounds); - - Long letterSoundId = Long.valueOf(csvRecord.get("letter_sound_id")); - letterSoundAssessmentEvent.setLetterSoundId(letterSoundId); - - letterSoundAssessmentEvents.add(letterSoundAssessmentEvent); - } - csvParser.close(); - } catch (IOException ex) { - log.error(ex.getMessage()); - } - - return letterSoundAssessmentEvents; - } public static List extractLetterSoundLearningEvents(File csvFile) { log.info("extractLetterSoundLearningEvents"); @@ -181,9 +101,24 @@ public static List extractLetterSoundLearningEvents(Fi } } - // TODO: letter_sound_letters + if (versionCode >= 4001000) { + // https://github.com/elimu-ai/analytics/releases/tag/4.1.0 - // TODO: letter_sound_sounds + // Convert letters from String to String list. E.g. "[ห, ล]" --> ["ห", "ล"] + String letterSoundLettersAsString = csvRecord.get("letter_sound_letters"); + String[] letterSoundLettersAsArray = letterSoundLettersAsString.substring(1, letterSoundLettersAsString.length() - 1).split(", "); + List letterSoundLetters = Arrays.asList(letterSoundLettersAsArray); + letterSoundLearningEvent.setLetterSoundLetters(letterSoundLetters); + + // Convert sounds from String to String list. E.g. "[a, w]" --> ["a", "w"] + String letterSoundSoundsAsString = csvRecord.get("letter_sound_sounds"); + String[] letterSoundSoundsAsArray = letterSoundSoundsAsString.substring(1, letterSoundSoundsAsString.length() - 1).split(", "); + List letterSoundSounds = Arrays.asList(letterSoundSoundsAsArray); + letterSoundLearningEvent.setLetterSoundSounds(letterSoundSounds); + } else { + letterSoundLearningEvent.setLetterSoundLetters(new ArrayList<>()); + letterSoundLearningEvent.setLetterSoundSounds(new ArrayList<>()); + } Long letterSoundId = Long.valueOf(csvRecord.get("letter_sound_id")); letterSoundLearningEvent.setLetterSoundId(letterSoundId); @@ -198,14 +133,13 @@ public static List extractLetterSoundLearningEvents(Fi return letterSoundLearningEvents; } - - public static List extractNumberAssessmentEvents(File csvFile) { - log.info("extractNumberAssessmentEvents"); + public static List extractLetterSoundAssessmentEvents(File csvFile) { + log.info("extractLetterSoundAssessmentEvents"); Integer versionCode = AnalyticsHelper.extractVersionCodeFromCsvFilename(csvFile.getName()); log.info("versionCode: " + versionCode); - List numberAssessmentEvents = new ArrayList<>(); + List letterSoundAssessmentEvents = new ArrayList<>(); // Iterate each row in the CSV file Path csvFilePath = Paths.get(csvFile.toURI()); @@ -220,66 +154,86 @@ public static List extractNumberAssessmentEvents(File csv // Convert from CSV to Java - NumberAssessmentEvent numberAssessmentEvent = new NumberAssessmentEvent(); + LetterSoundAssessmentEvent letterSoundAssessmentEvent = new LetterSoundAssessmentEvent(); - long timestampInMillis = Long.valueOf(csvRecord.get("timestamp").substring(0, 10)) * 1_000; + long timestampInMillis = Long.valueOf(csvRecord.get("timestamp")); Calendar timestamp = Calendar.getInstance(TimeZone.getTimeZone("UTC")); timestamp.setTimeInMillis(timestampInMillis); - numberAssessmentEvent.setTimestamp(timestamp); + letterSoundAssessmentEvent.setTimestamp(timestamp); String androidId = AnalyticsHelper.extractAndroidIdFromCsvFilename(csvFile.getName()); - numberAssessmentEvent.setAndroidId(androidId); + letterSoundAssessmentEvent.setAndroidId(androidId); String packageName = csvRecord.get("package_name"); - numberAssessmentEvent.setPackageName(packageName); + letterSoundAssessmentEvent.setPackageName(packageName); Float masteryScore = Float.valueOf(csvRecord.get("mastery_score")); - numberAssessmentEvent.setMasteryScore(masteryScore); + letterSoundAssessmentEvent.setMasteryScore(masteryScore); Long timeSpentMs = Long.valueOf(csvRecord.get("time_spent_ms")); - numberAssessmentEvent.setTimeSpentMs(timeSpentMs); + letterSoundAssessmentEvent.setTimeSpentMs(timeSpentMs); String additionalData = csvRecord.get("additional_data"); if (StringUtils.isNotBlank(additionalData)) { - numberAssessmentEvent.setAdditionalData(additionalData); + letterSoundAssessmentEvent.setAdditionalData(additionalData); } - int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); - ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; - numberAssessmentEvent.setResearchExperiment(researchExperiment); + if (versionCode >= 3005009) { + // https://github.com/elimu-ai/analytics/releases/tag/3.5.9 + + if (StringUtils.isNotBlank(csvRecord.get("research_experiment"))) { + int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); + ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; + letterSoundAssessmentEvent.setResearchExperiment(researchExperiment); + } - int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); - ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; - numberAssessmentEvent.setExperimentGroup(experimentGroup); + if (StringUtils.isNotBlank(csvRecord.get("experiment_group"))) { + int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); + ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; + letterSoundAssessmentEvent.setExperimentGroup(experimentGroup); + } + } - Integer numberValue = Integer.valueOf(csvRecord.get("number_value")); - numberAssessmentEvent.setNumberValue(numberValue); + if (versionCode >= 4001000) { + // https://github.com/elimu-ai/analytics/releases/tag/4.1.0 - // String numberSymbol = csvRecord.get("number_symbol"); - // numberAssessmentEvent.setNumberSymbol(numberSymbol); + // Convert letters from String to String list. E.g. "[ห, ล]" --> ["ห", "ล"] + String letterSoundLettersAsString = csvRecord.get("letter_sound_letters"); + String[] letterSoundLettersAsArray = letterSoundLettersAsString.substring(1, letterSoundLettersAsString.length() - 1).split(", "); + List letterSoundLetters = Arrays.asList(letterSoundLettersAsArray); + letterSoundAssessmentEvent.setLetterSoundLetters(letterSoundLetters); - if (StringUtils.isNotBlank(csvRecord.get("number_id"))) { - Long numberId = Long.valueOf(csvRecord.get("number_id")); - numberAssessmentEvent.setNumberId(numberId); + // Convert sounds from String to String list. E.g. "[a, w]" --> ["a", "w"] + String letterSoundSoundsAsString = csvRecord.get("letter_sound_sounds"); + String[] letterSoundSoundsAsArray = letterSoundSoundsAsString.substring(1, letterSoundSoundsAsString.length() - 1).split(", "); + List letterSoundSounds = Arrays.asList(letterSoundSoundsAsArray); + letterSoundAssessmentEvent.setLetterSoundSounds(letterSoundSounds); + } else { + letterSoundAssessmentEvent.setLetterSoundLetters(new ArrayList<>()); + letterSoundAssessmentEvent.setLetterSoundSounds(new ArrayList<>()); } - numberAssessmentEvents.add(numberAssessmentEvent); + Long letterSoundId = Long.valueOf(csvRecord.get("letter_sound_id")); + letterSoundAssessmentEvent.setLetterSoundId(letterSoundId); + + letterSoundAssessmentEvents.add(letterSoundAssessmentEvent); } csvParser.close(); } catch (IOException ex) { log.error(ex.getMessage()); } - return numberAssessmentEvents; + return letterSoundAssessmentEvents; } - public static List extractNumberLearningEvents(File csvFile) { - log.info("extractNumberLearningEvents"); + + public static List extractWordLearningEvents(File csvFile) { + log.info("extractWordLearningEvents"); Integer versionCode = AnalyticsHelper.extractVersionCodeFromCsvFilename(csvFile.getName()); log.info("versionCode: " + versionCode); - List numberLearningEvents = new ArrayList<>(); + List wordLearningEvents = new ArrayList<>(); // Iterate each row in the CSV file Path csvFilePath = Paths.get(csvFile.toURI()); @@ -294,28 +248,38 @@ public static List extractNumberLearningEvents(File csvFile // Convert from CSV to Java - NumberLearningEvent numberLearningEvent = new NumberLearningEvent(); + WordLearningEvent wordLearningEvent = new WordLearningEvent(); - long timestampInMillis = Long.valueOf(csvRecord.get("timestamp").substring(0, 10)) * 1_000; + String timestampColumnName = null; + if (versionCode < 3004000) { + timestampColumnName = "time"; + } else { + // https://github.com/elimu-ai/analytics/releases/tag/3.4.0 + timestampColumnName = "timestamp"; + } + long timestampInMillis = Long.valueOf(csvRecord.get(timestampColumnName).substring(0, 10)) * 1_000; Calendar timestamp = Calendar.getInstance(TimeZone.getTimeZone("UTC")); timestamp.setTimeInMillis(timestampInMillis); - numberLearningEvent.setTimestamp(timestamp); + wordLearningEvent.setTimestamp(timestamp); String androidId = AnalyticsHelper.extractAndroidIdFromCsvFilename(csvFile.getName()); - numberLearningEvent.setAndroidId(androidId); + wordLearningEvent.setAndroidId(androidId); String packageName = csvRecord.get("package_name"); - numberLearningEvent.setPackageName(packageName); - - String additionalData = csvRecord.get("additional_data"); - if (StringUtils.isNotBlank(additionalData)) { - numberLearningEvent.setAdditionalData(additionalData); - } + wordLearningEvent.setPackageName(packageName); if (versionCode < 4000033) { if (StringUtils.isNotBlank(csvRecord.get("learning_event_type"))) { LearningEventType learningEventType = LearningEventType.valueOf(csvRecord.get("learning_event_type")); - numberLearningEvent.setLearningEventType(learningEventType); + wordLearningEvent.setLearningEventType(learningEventType); + } + } + + if (versionCode >= 3006000) { + // https://github.com/elimu-ai/analytics/releases/tag/3.6.0 + String additionalData = csvRecord.get("additional_data"); + if (StringUtils.isNotBlank(additionalData)) { + wordLearningEvent.setAdditionalData(additionalData); } } @@ -325,39 +289,31 @@ public static List extractNumberLearningEvents(File csvFile if (StringUtils.isNotBlank(csvRecord.get("research_experiment"))) { int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; - numberLearningEvent.setResearchExperiment(researchExperiment); + wordLearningEvent.setResearchExperiment(researchExperiment); } if (StringUtils.isNotBlank(csvRecord.get("experiment_group"))) { int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; - numberLearningEvent.setExperimentGroup(experimentGroup); + wordLearningEvent.setExperimentGroup(experimentGroup); } } - Integer numberValue = Integer.valueOf(csvRecord.get("number_value")); - numberLearningEvent.setNumberValue(numberValue); - - if (StringUtils.isNotBlank(csvRecord.get("number_symbol"))) { - String numberSymbol = csvRecord.get("number_symbol"); - numberLearningEvent.setNumberSymbol(numberSymbol); - } + String wordText = csvRecord.get("word_text"); + wordLearningEvent.setWordText(wordText); - if (StringUtils.isNotBlank(csvRecord.get("number_id"))) { - Long numberId = Long.valueOf(csvRecord.get("number_id")); - numberLearningEvent.setNumberId(numberId); - } + Long wordId = Long.valueOf(csvRecord.get("word_id")); + wordLearningEvent.setWordId(wordId); - numberLearningEvents.add(numberLearningEvent); + wordLearningEvents.add(wordLearningEvent); } csvParser.close(); } catch (IOException ex) { log.error(ex.getMessage()); } - return numberLearningEvents; + return wordLearningEvents; } - public static List extractWordAssessmentEvents(File csvFile) { log.info("extractWordAssessmentEvents"); @@ -445,14 +401,15 @@ public static List extractWordAssessmentEvents(File csvFile return wordAssessmentEvents; } - - public static List extractWordLearningEvents(File csvFile) { - log.info("extractWordLearningEvents"); + + + public static List extractNumberLearningEvents(File csvFile) { + log.info("extractNumberLearningEvents"); Integer versionCode = AnalyticsHelper.extractVersionCodeFromCsvFilename(csvFile.getName()); log.info("versionCode: " + versionCode); - List wordLearningEvents = new ArrayList<>(); + List numberLearningEvents = new ArrayList<>(); // Iterate each row in the CSV file Path csvFilePath = Paths.get(csvFile.toURI()); @@ -467,38 +424,28 @@ public static List extractWordLearningEvents(File csvFile) { // Convert from CSV to Java - WordLearningEvent wordLearningEvent = new WordLearningEvent(); + NumberLearningEvent numberLearningEvent = new NumberLearningEvent(); - String timestampColumnName = null; - if (versionCode < 3004000) { - timestampColumnName = "time"; - } else { - // https://github.com/elimu-ai/analytics/releases/tag/3.4.0 - timestampColumnName = "timestamp"; - } - long timestampInMillis = Long.valueOf(csvRecord.get(timestampColumnName).substring(0, 10)) * 1_000; + long timestampInMillis = Long.valueOf(csvRecord.get("timestamp").substring(0, 10)) * 1_000; Calendar timestamp = Calendar.getInstance(TimeZone.getTimeZone("UTC")); timestamp.setTimeInMillis(timestampInMillis); - wordLearningEvent.setTimestamp(timestamp); + numberLearningEvent.setTimestamp(timestamp); String androidId = AnalyticsHelper.extractAndroidIdFromCsvFilename(csvFile.getName()); - wordLearningEvent.setAndroidId(androidId); + numberLearningEvent.setAndroidId(androidId); String packageName = csvRecord.get("package_name"); - wordLearningEvent.setPackageName(packageName); + numberLearningEvent.setPackageName(packageName); + + String additionalData = csvRecord.get("additional_data"); + if (StringUtils.isNotBlank(additionalData)) { + numberLearningEvent.setAdditionalData(additionalData); + } if (versionCode < 4000033) { if (StringUtils.isNotBlank(csvRecord.get("learning_event_type"))) { LearningEventType learningEventType = LearningEventType.valueOf(csvRecord.get("learning_event_type")); - wordLearningEvent.setLearningEventType(learningEventType); - } - } - - if (versionCode >= 3006000) { - // https://github.com/elimu-ai/analytics/releases/tag/3.6.0 - String additionalData = csvRecord.get("additional_data"); - if (StringUtils.isNotBlank(additionalData)) { - wordLearningEvent.setAdditionalData(additionalData); + numberLearningEvent.setLearningEventType(learningEventType); } } @@ -508,30 +455,111 @@ public static List extractWordLearningEvents(File csvFile) { if (StringUtils.isNotBlank(csvRecord.get("research_experiment"))) { int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; - wordLearningEvent.setResearchExperiment(researchExperiment); + numberLearningEvent.setResearchExperiment(researchExperiment); } if (StringUtils.isNotBlank(csvRecord.get("experiment_group"))) { int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; - wordLearningEvent.setExperimentGroup(experimentGroup); + numberLearningEvent.setExperimentGroup(experimentGroup); } } - String wordText = csvRecord.get("word_text"); - wordLearningEvent.setWordText(wordText); + Integer numberValue = Integer.valueOf(csvRecord.get("number_value")); + numberLearningEvent.setNumberValue(numberValue); - Long wordId = Long.valueOf(csvRecord.get("word_id")); - wordLearningEvent.setWordId(wordId); + if (StringUtils.isNotBlank(csvRecord.get("number_symbol"))) { + String numberSymbol = csvRecord.get("number_symbol"); + numberLearningEvent.setNumberSymbol(numberSymbol); + } - wordLearningEvents.add(wordLearningEvent); + if (StringUtils.isNotBlank(csvRecord.get("number_id"))) { + Long numberId = Long.valueOf(csvRecord.get("number_id")); + numberLearningEvent.setNumberId(numberId); + } + + numberLearningEvents.add(numberLearningEvent); } csvParser.close(); } catch (IOException ex) { log.error(ex.getMessage()); } - return wordLearningEvents; + return numberLearningEvents; + } + + public static List extractNumberAssessmentEvents(File csvFile) { + log.info("extractNumberAssessmentEvents"); + + Integer versionCode = AnalyticsHelper.extractVersionCodeFromCsvFilename(csvFile.getName()); + log.info("versionCode: " + versionCode); + + List numberAssessmentEvents = new ArrayList<>(); + + // Iterate each row in the CSV file + Path csvFilePath = Paths.get(csvFile.toURI()); + log.info("csvFilePath: " + csvFilePath); + try { + Reader reader = Files.newBufferedReader(csvFilePath); + CSVFormat csvFormat = CSVFormat.DEFAULT.withFirstRecordAsHeader(); + log.info("header: " + Arrays.toString(csvFormat.getHeader())); + CSVParser csvParser = new CSVParser(reader, csvFormat); + for (CSVRecord csvRecord : csvParser) { + log.info("csvRecord: " + csvRecord); + + // Convert from CSV to Java + + NumberAssessmentEvent numberAssessmentEvent = new NumberAssessmentEvent(); + + long timestampInMillis = Long.valueOf(csvRecord.get("timestamp").substring(0, 10)) * 1_000; + Calendar timestamp = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + timestamp.setTimeInMillis(timestampInMillis); + numberAssessmentEvent.setTimestamp(timestamp); + + String androidId = AnalyticsHelper.extractAndroidIdFromCsvFilename(csvFile.getName()); + numberAssessmentEvent.setAndroidId(androidId); + + String packageName = csvRecord.get("package_name"); + numberAssessmentEvent.setPackageName(packageName); + + Float masteryScore = Float.valueOf(csvRecord.get("mastery_score")); + numberAssessmentEvent.setMasteryScore(masteryScore); + + Long timeSpentMs = Long.valueOf(csvRecord.get("time_spent_ms")); + numberAssessmentEvent.setTimeSpentMs(timeSpentMs); + + String additionalData = csvRecord.get("additional_data"); + if (StringUtils.isNotBlank(additionalData)) { + numberAssessmentEvent.setAdditionalData(additionalData); + } + + int researchExperimentOrdinal = Integer.valueOf(csvRecord.get("research_experiment")); + ResearchExperiment researchExperiment = ResearchExperiment.values()[researchExperimentOrdinal]; + numberAssessmentEvent.setResearchExperiment(researchExperiment); + + int experimentGroupOrdinal = Integer.valueOf(csvRecord.get("experiment_group")); + ExperimentGroup experimentGroup = ExperimentGroup.values()[experimentGroupOrdinal]; + numberAssessmentEvent.setExperimentGroup(experimentGroup); + + Integer numberValue = Integer.valueOf(csvRecord.get("number_value")); + numberAssessmentEvent.setNumberValue(numberValue); + + // String numberSymbol = csvRecord.get("number_symbol"); + // numberAssessmentEvent.setNumberSymbol(numberSymbol); + + if (StringUtils.isNotBlank(csvRecord.get("number_id"))) { + Long numberId = Long.valueOf(csvRecord.get("number_id")); + numberAssessmentEvent.setNumberId(numberId); + } + + numberAssessmentEvents.add(numberAssessmentEvent); + } + csvParser.close(); + } catch (IOException ex) { + log.error(ex.getMessage()); + } + + return numberAssessmentEvents; } diff --git a/src/main/java/ai/elimu/web/analytics/students/LetterSoundLearningEventsCsvExportController.java b/src/main/java/ai/elimu/web/analytics/students/LetterSoundLearningEventsCsvExportController.java index 3c913dc79..504217936 100644 --- a/src/main/java/ai/elimu/web/analytics/students/LetterSoundLearningEventsCsvExportController.java +++ b/src/main/java/ai/elimu/web/analytics/students/LetterSoundLearningEventsCsvExportController.java @@ -54,8 +54,8 @@ public void handleRequest( "additional_data", "research_experiment", "experiment_group", - // "letter_sound_letters", - // "letter_sound_sounds", + "letter_sound_letters", + "letter_sound_sounds", "letter_sound_id" ).build(); StringWriter stringWriter = new StringWriter(); @@ -69,8 +69,8 @@ public void handleRequest( event.getAdditionalData(), (event.getResearchExperiment() != null) ? event.getResearchExperiment().ordinal() : null, (event.getExperimentGroup() != null) ? event.getExperimentGroup().ordinal() : null, - // letterSoundLearningEvent.getLetterSoundLetters(), - // letterSoundLearningEvent.getLetterSoundSounds(), + event.getLetterSoundLetters(), + event.getLetterSoundSounds(), event.getLetterSoundId() ); } diff --git a/src/test/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelperTest.java b/src/test/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelperTest.java index 8b48c7886..1aa358194 100644 --- a/src/test/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelperTest.java +++ b/src/test/java/ai/elimu/util/csv/CsvAnalyticsExtractionHelperTest.java @@ -1,12 +1,12 @@ package ai.elimu.util.csv; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.json.JSONObject; @@ -29,8 +29,6 @@ public class CsvAnalyticsExtractionHelperTest { - // TODO: letter-sound assessment events - /** * Test extraction of data from CSV files generated by version 3002014 of the Analytics app: * https://github.com/elimu-ai/analytics/releases/tag/3.2.14 @@ -92,9 +90,37 @@ public void testExtractLetterSoundLearningEvents_v3005013() throws IOException { assertEquals("ai.elimu.herufi", letterSoundLearningEvent.getPackageName()); assertNull(letterSoundLearningEvent.getResearchExperiment()); assertNull(letterSoundLearningEvent.getExperimentGroup()); + assertEquals(Arrays.asList(), letterSoundLearningEvent.getLetterSoundLetters()); + assertEquals(Arrays.asList(), letterSoundLearningEvent.getLetterSoundSounds()); assertEquals(1, letterSoundLearningEvent.getLetterSoundId()); } + /** + * Test extraction of data from CSV files generated by version 4001002 of the Analytics app: + * https://github.com/elimu-ai/analytics/releases/tag/4.1.2 + */ + @Test + public void testExtractLetterSoundLearningEvents_v4001002() throws IOException { + ResourceLoader resourceLoader = new ClassRelativeResourceLoader(CsvAnalyticsExtractionHelper.class); + Resource resource = resourceLoader.getResource("5b7c682a12ecbe2e_4001002_letter-sound-learning-events_2025-07-28.csv"); + File csvFile = resource.getFile(); + + List letterSoundLearningEvents = CsvAnalyticsExtractionHelper.extractLetterSoundLearningEvents(csvFile); + assertEquals(22, letterSoundLearningEvents.size()); + + LetterSoundLearningEvent letterSoundLearningEvent = letterSoundLearningEvents.get(0); + assertEquals(1753693734 * 1_000L, letterSoundLearningEvent.getTimestamp().getTimeInMillis()); + assertEquals("5b7c682a12ecbe2e", letterSoundLearningEvent.getAndroidId()); + assertEquals("ai.elimu.herufi.debug", letterSoundLearningEvent.getPackageName()); + assertEquals(ResearchExperiment.EXP_0_WORD_EMOJIS, letterSoundLearningEvent.getResearchExperiment()); + assertEquals(ExperimentGroup.TREATMENT, letterSoundLearningEvent.getExperimentGroup()); + assertEquals(Arrays.asList("น"), letterSoundLearningEvent.getLetterSoundLetters()); + assertEquals(Arrays.asList("n"), letterSoundLearningEvent.getLetterSoundSounds()); + assertEquals(7, letterSoundLearningEvent.getLetterSoundId()); + } + + // TODO: letter-sound assessment events + /** * Test extraction of data from CSV files generated by version 3001030 of the Analytics app: diff --git a/src/test/resources/ai/elimu/util/csv/5b7c682a12ecbe2e_4001002_letter-sound-learning-events_2025-07-28.csv b/src/test/resources/ai/elimu/util/csv/5b7c682a12ecbe2e_4001002_letter-sound-learning-events_2025-07-28.csv new file mode 100644 index 000000000..9f8796c51 --- /dev/null +++ b/src/test/resources/ai/elimu/util/csv/5b7c682a12ecbe2e_4001002_letter-sound-learning-events_2025-07-28.csv @@ -0,0 +1,23 @@ +id,timestamp,package_name,additional_data,research_experiment,experiment_group,letter_sound_letters,letter_sound_sounds,letter_sound_id +44,1753693734,ai.elimu.herufi.debug,,0,1,[น],[n],7 +45,1753693740,ai.elimu.herufi.debug,,0,1,[ก],[k],3 +46,1753693756,ai.elimu.herufi.debug,,0,1,[◌ั],[a],34 +47,1753694783,ai.elimu.herufi.debug,,0,1,[น],[n],7 +48,1753694800,ai.elimu.herufi.debug,,0,1,[ก],[k],3 +49,1753694805,ai.elimu.herufi.debug,,0,1,[◌ั],[a],34 +50,1753694807,ai.elimu.herufi.debug,,0,1,[◌า],[aː],13 +51,1753694813,ai.elimu.herufi.debug,,0,1,[◌อ],[ɔː],31 +52,1753694819,ai.elimu.herufi.debug,,0,1,[ข],[kʰ],38 +53,1753694821,ai.elimu.herufi.debug,,0,1,[จ],[tɕ],24 +54,1753694825,ai.elimu.herufi.debug,,0,1,[ไ◌],"[a, j]",40 +55,1753694846,ai.elimu.herufi.debug,,0,1,[ฉ],[tɕʰ],44 +56,1753694865,ai.elimu.herufi.debug,,0,1,[เ◌า],"[a, w]",69 +57,1753694871,ai.elimu.herufi.debug,,0,1,[แ◌ะ],[ɛ],70 +58,1753694877,ai.elimu.herufi.debug,,0,1,[เ◌อ],[ɤː],72 +59,1753694881,ai.elimu.herufi.debug,,0,1,[น],"[n, o]",62 +60,1753694885,ai.elimu.herufi.debug,,0,1,[เ◌ย],"[ɤː, j]",79 +61,1753694893,ai.elimu.herufi.debug,,0,1,"[ห, ล]",[l],65 +62,1753694897,ai.elimu.herufi.debug,,0,1,"[ห, ล]",[l],65 +63,1753694918,ai.elimu.herufi.debug,,0,1,"[ห, ย]",[y],59 +64,1753694930,ai.elimu.herufi.debug,,0,1,"[ท, ร]",[s],42 +65,1753695261,ai.elimu.herufi.debug,,0,1,[น],[n],7