diff --git a/src/main/java/ai/elimu/web/analytics/StoryBookLearningEventListController.java b/src/main/java/ai/elimu/web/analytics/StoryBookLearningEventListController.java index 2e98725f2..1d0b673e5 100644 --- a/src/main/java/ai/elimu/web/analytics/StoryBookLearningEventListController.java +++ b/src/main/java/ai/elimu/web/analytics/StoryBookLearningEventListController.java @@ -34,7 +34,7 @@ public String handleRequest(Model model) { List storyBookLearningEvents = storyBookLearningEventDao.readAllOrderedByTime(); model.addAttribute("storyBookLearningEvents", storyBookLearningEvents); - // Prepare data for chart in UI + // Prepare chart data List monthList = new ArrayList<>(); List eventCountList = new ArrayList<>(); if (!storyBookLearningEvents.isEmpty()) { diff --git a/src/main/java/ai/elimu/web/analytics/WordLearningEventListController.java b/src/main/java/ai/elimu/web/analytics/WordLearningEventListController.java index 7fb5ba1c2..d09b19b4d 100644 --- a/src/main/java/ai/elimu/web/analytics/WordLearningEventListController.java +++ b/src/main/java/ai/elimu/web/analytics/WordLearningEventListController.java @@ -2,7 +2,14 @@ import ai.elimu.dao.WordLearningEventDao; import ai.elimu.model.analytics.WordLearningEvent; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; import java.util.List; +import java.util.Map; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +34,36 @@ public String handleRequest(Model model) { List wordLearningEvents = wordLearningEventDao.readAll(); model.addAttribute("wordLearningEvents", wordLearningEvents); + // Prepare chart data + List monthList = new ArrayList<>(); + List eventCountList = new ArrayList<>(); + if (!wordLearningEvents.isEmpty()) { + // Group event count by month (e.g. "Aug-2024", "Sep-2024") + Map eventCountByMonthMap = new HashMap<>(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM-yyyy"); + for (WordLearningEvent event : wordLearningEvents) { + String eventMonth = simpleDateFormat.format(event.getTimestamp().getTime()); + eventCountByMonthMap.put(eventMonth, eventCountByMonthMap.getOrDefault(eventMonth, 0) + 1); + } + + // Iterate each month from 4 years ago until now + Calendar calendar4YearsAgo = Calendar.getInstance(); + calendar4YearsAgo.add(Calendar.YEAR, -4); + Calendar calendarNow = Calendar.getInstance(); + Calendar month = calendar4YearsAgo; + while (!month.after(calendarNow)) { + String monthAsString = simpleDateFormat.format(month.getTime()); + monthList.add(monthAsString); + + eventCountList.add(eventCountByMonthMap.getOrDefault(monthAsString, 0)); + + // Increase the date by 1 month + month.add(Calendar.MONTH, 1); + } + } + model.addAttribute("monthList", monthList); + model.addAttribute("eventCountList", eventCountList); + return "analytics/word-learning-event/list"; } } diff --git a/src/main/webapp/WEB-INF/jsp/analytics/main.jsp b/src/main/webapp/WEB-INF/jsp/analytics/main.jsp index 6d4fd93b8..fcebae5f4 100644 --- a/src/main/webapp/WEB-INF/jsp/analytics/main.jsp +++ b/src/main/webapp/WEB-INF/jsp/analytics/main.jsp @@ -25,7 +25,7 @@ sms diff --git a/src/main/webapp/WEB-INF/jsp/analytics/storybook-learning-event/list.jsp b/src/main/webapp/WEB-INF/jsp/analytics/storybook-learning-event/list.jsp index 6b76ff929..b6b5672ee 100644 --- a/src/main/webapp/WEB-INF/jsp/analytics/storybook-learning-event/list.jsp +++ b/src/main/webapp/WEB-INF/jsp/analytics/storybook-learning-event/list.jsp @@ -49,12 +49,12 @@ - - - - - - + + + + + + diff --git a/src/main/webapp/WEB-INF/jsp/analytics/word-learning-event/list.jsp b/src/main/webapp/WEB-INF/jsp/analytics/word-learning-event/list.jsp index 2f4912ddd..4155ee94d 100644 --- a/src/main/webapp/WEB-INF/jsp/analytics/word-learning-event/list.jsp +++ b/src/main/webapp/WEB-INF/jsp/analytics/word-learning-event/list.jsp @@ -5,25 +5,21 @@
- - - + +
@@ -53,12 +49,12 @@
timestampandroid_idpackage_namestorybook_idstorybook_titlelearning_event_typetimestampandroid_idpackage_namestorybook_idstorybook_titlelearning_event_type
- - - - - - + + + + + + diff --git a/src/test/java/ai/elimu/rest/v2/content/EmojisRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/EmojisRestControllerTest.java index 7ddee76c1..804bc0e53 100644 --- a/src/test/java/ai/elimu/rest/v2/content/EmojisRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/EmojisRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/ImagesRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/ImagesRestControllerTest.java index 63bbefc08..efd0e6481 100644 --- a/src/test/java/ai/elimu/rest/v2/content/ImagesRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/ImagesRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/LetterSoundsRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/LetterSoundsRestControllerTest.java index cf2d6668b..ff891cdcd 100644 --- a/src/test/java/ai/elimu/rest/v2/content/LetterSoundsRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/LetterSoundsRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/LettersRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/LettersRestControllerTest.java index 69e055933..ef90d5945 100644 --- a/src/test/java/ai/elimu/rest/v2/content/LettersRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/LettersRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/SoundsRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/SoundsRestControllerTest.java index 89235823d..2ba704f1b 100644 --- a/src/test/java/ai/elimu/rest/v2/content/SoundsRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/SoundsRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/StoryBooksRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/StoryBooksRestControllerTest.java index 76bfb4a7a..b7f0c39e4 100644 --- a/src/test/java/ai/elimu/rest/v2/content/StoryBooksRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/StoryBooksRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/ai/elimu/rest/v2/content/WordsRestControllerTest.java b/src/test/java/ai/elimu/rest/v2/content/WordsRestControllerTest.java index b1f29418a..30c0c5a9a 100644 --- a/src/test/java/ai/elimu/rest/v2/content/WordsRestControllerTest.java +++ b/src/test/java/ai/elimu/rest/v2/content/WordsRestControllerTest.java @@ -1,12 +1,13 @@ package ai.elimu.rest.v2.content; import ai.elimu.util.JsonLoader; +import selenium.util.DomainHelper; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; -import selenium.DomainHelper; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/selenium/web/WelcomePage.java b/src/test/java/selenium/WelcomePage.java similarity index 86% rename from src/test/java/selenium/web/WelcomePage.java rename to src/test/java/selenium/WelcomePage.java index 297152b5f..5138fe6d6 100644 --- a/src/test/java/selenium/web/WelcomePage.java +++ b/src/test/java/selenium/WelcomePage.java @@ -1,9 +1,9 @@ -package selenium.web; +package selenium; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; -import selenium.ErrorHelper; +import selenium.util.ErrorHelper; public class WelcomePage { diff --git a/src/test/java/selenium/web/WelcomePageTest.java b/src/test/java/selenium/WelcomePageTest.java similarity index 93% rename from src/test/java/selenium/web/WelcomePageTest.java rename to src/test/java/selenium/WelcomePageTest.java index 616d674a1..73e61be8b 100644 --- a/src/test/java/selenium/web/WelcomePageTest.java +++ b/src/test/java/selenium/WelcomePageTest.java @@ -1,4 +1,4 @@ -package selenium.web; +package selenium; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -9,7 +9,7 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; -import selenium.DomainHelper; +import selenium.util.DomainHelper; public class WelcomePageTest { @@ -24,7 +24,7 @@ public void setUp() { ChromeOptions chromeOptions = new ChromeOptions(); // Read "headless" property set on the command line: - // mvn clean verify -P regression-testing-ui -D headless=true -D base.url=https://eng.test.elimu.ai + // mvn clean verify -P regression-testing-ui -D headless=true String headlessSystemProperty = System.getProperty("headless"); logger.info("headlessSystemProperty: \"" + headlessSystemProperty + "\""); if ("true".equals(headlessSystemProperty)) { diff --git a/src/test/java/selenium/analytics/MainAnalyticsPage.java b/src/test/java/selenium/analytics/MainAnalyticsPage.java new file mode 100644 index 000000000..1057d2051 --- /dev/null +++ b/src/test/java/selenium/analytics/MainAnalyticsPage.java @@ -0,0 +1,25 @@ +package selenium.analytics; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import selenium.util.ErrorHelper; + +public class MainAnalyticsPage { + + private WebDriver driver; + + public MainAnalyticsPage(WebDriver driver) { + this.driver = driver; + + driver.findElement(By.id("mainAnalyticsPage")); + + ErrorHelper.verifyNoScriptOrMarkupError(driver); + } + + public void pressWordLearningEventsLink() { + WebElement link = driver.findElement(By.id("wordLearningEventsLink")); + link.click(); + } +} diff --git a/src/test/java/selenium/analytics/MainAnalyticsPageTest.java b/src/test/java/selenium/analytics/MainAnalyticsPageTest.java new file mode 100644 index 000000000..f27727914 --- /dev/null +++ b/src/test/java/selenium/analytics/MainAnalyticsPageTest.java @@ -0,0 +1,52 @@ +package selenium.analytics; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; + +import selenium.util.DomainHelper; + +public class MainAnalyticsPageTest { + + private final Logger logger = LogManager.getLogger(); + + private WebDriver driver; + + @BeforeEach + public void setUp() { + logger.info("setUp"); + + ChromeOptions chromeOptions = new ChromeOptions(); + + // Read "headless" property set on the command line: + // mvn clean verify -P regression-testing-ui -D headless=true + String headlessSystemProperty = System.getProperty("headless"); + logger.info("headlessSystemProperty: \"" + headlessSystemProperty + "\""); + if ("true".equals(headlessSystemProperty)) { + chromeOptions.addArguments("headless"); + } + + driver = new ChromeDriver(chromeOptions); + + driver.get(DomainHelper.getBaseUrl() + "/analytics"); + } + + @AfterEach + public void tearDown() { + logger.info("tearDown"); + + driver.quit(); + } + + @Test + public void testMainAnalyticsPage() { + logger.info("testMainAnalyticsPage"); + + MainAnalyticsPage mainAnalyticsPage = new MainAnalyticsPage(driver); + } +} diff --git a/src/test/java/selenium/analytics/WordLearningEventsPage.java b/src/test/java/selenium/analytics/WordLearningEventsPage.java new file mode 100644 index 000000000..3059751d0 --- /dev/null +++ b/src/test/java/selenium/analytics/WordLearningEventsPage.java @@ -0,0 +1,19 @@ +package selenium.analytics; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; + +import selenium.util.ErrorHelper; + +public class WordLearningEventsPage { + + private WebDriver driver; + + public WordLearningEventsPage(WebDriver driver) { + this.driver = driver; + + driver.findElement(By.id("wordLearningEventsPage")); + + ErrorHelper.verifyNoScriptOrMarkupError(driver); + } +} diff --git a/src/test/java/selenium/analytics/WordLearningEventsPageTest.java b/src/test/java/selenium/analytics/WordLearningEventsPageTest.java new file mode 100644 index 000000000..e857dd0b4 --- /dev/null +++ b/src/test/java/selenium/analytics/WordLearningEventsPageTest.java @@ -0,0 +1,56 @@ +package selenium.analytics; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openjdk.tools.javac.main.Main; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; + +import selenium.util.DomainHelper; + +public class WordLearningEventsPageTest { + + private final Logger logger = LogManager.getLogger(); + + private WebDriver driver; + + @BeforeEach + public void setUp() { + logger.info("setUp"); + + ChromeOptions chromeOptions = new ChromeOptions(); + + // Read "headless" property set on the command line: + // mvn clean verify -P regression-testing-ui -D headless=true + String headlessSystemProperty = System.getProperty("headless"); + logger.info("headlessSystemProperty: \"" + headlessSystemProperty + "\""); + if ("true".equals(headlessSystemProperty)) { + chromeOptions.addArguments("headless"); + } + + driver = new ChromeDriver(chromeOptions); + + driver.get(DomainHelper.getBaseUrl() + "/analytics"); + } + + @AfterEach + public void tearDown() { + logger.info("tearDown"); + + driver.quit(); + } + + @Test + public void testWordLearningEventsPage() { + logger.info("testWordLearningEventsPage"); + + MainAnalyticsPage mainAnalyticsPage = new MainAnalyticsPage(driver); + mainAnalyticsPage.pressWordLearningEventsLink(); + + WordLearningEventsPage wordLearningEventsPage = new WordLearningEventsPage(driver); + } +} diff --git a/src/test/java/selenium/DomainHelper.java b/src/test/java/selenium/util/DomainHelper.java similarity index 97% rename from src/test/java/selenium/DomainHelper.java rename to src/test/java/selenium/util/DomainHelper.java index 4a712b8fc..0dce57b54 100644 --- a/src/test/java/selenium/DomainHelper.java +++ b/src/test/java/selenium/util/DomainHelper.java @@ -1,4 +1,4 @@ -package selenium; +package selenium.util; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; diff --git a/src/test/java/selenium/ErrorHelper.java b/src/test/java/selenium/util/ErrorHelper.java similarity index 92% rename from src/test/java/selenium/ErrorHelper.java rename to src/test/java/selenium/util/ErrorHelper.java index b9459ef0a..67ddeaa37 100644 --- a/src/test/java/selenium/ErrorHelper.java +++ b/src/test/java/selenium/util/ErrorHelper.java @@ -1,4 +1,4 @@ -package selenium; +package selenium.util; import org.openqa.selenium.WebDriver; diff --git a/src/test/java/selenium/error_pages/ErrorPage.java b/src/test/java/selenium/util/ErrorPage.java similarity index 94% rename from src/test/java/selenium/error_pages/ErrorPage.java rename to src/test/java/selenium/util/ErrorPage.java index 6a2ad1400..385808ba4 100644 --- a/src/test/java/selenium/error_pages/ErrorPage.java +++ b/src/test/java/selenium/util/ErrorPage.java @@ -1,4 +1,4 @@ -package selenium.error_pages; +package selenium.util; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; diff --git a/src/test/java/selenium/JavaScriptHelper.java b/src/test/java/selenium/util/JavaScriptHelper.java similarity index 96% rename from src/test/java/selenium/JavaScriptHelper.java rename to src/test/java/selenium/util/JavaScriptHelper.java index 66599413d..516bec658 100644 --- a/src/test/java/selenium/JavaScriptHelper.java +++ b/src/test/java/selenium/util/JavaScriptHelper.java @@ -1,8 +1,9 @@ -package selenium; +package selenium.util; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; + import static org.junit.jupiter.api.Assertions.*; /** diff --git a/src/test/java/selenium/MarkupValidationHelper.java b/src/test/java/selenium/util/MarkupValidationHelper.java similarity index 98% rename from src/test/java/selenium/MarkupValidationHelper.java rename to src/test/java/selenium/util/MarkupValidationHelper.java index d7a51b803..058a6d898 100644 --- a/src/test/java/selenium/MarkupValidationHelper.java +++ b/src/test/java/selenium/util/MarkupValidationHelper.java @@ -1,4 +1,4 @@ -package selenium; +package selenium.util; import java.nio.charset.Charset; import java.util.ArrayList; @@ -7,6 +7,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.RestTemplate; + import static org.junit.jupiter.api.Assertions.*; /** diff --git a/src/test/java/selenium/SignOnHelper.java b/src/test/java/selenium/util/SignOnHelper.java similarity index 91% rename from src/test/java/selenium/SignOnHelper.java rename to src/test/java/selenium/util/SignOnHelper.java index b0c7cf677..49ed901d7 100644 --- a/src/test/java/selenium/SignOnHelper.java +++ b/src/test/java/selenium/util/SignOnHelper.java @@ -1,6 +1,7 @@ -package selenium; +package selenium.util; import ai.elimu.model.enums.Role; + import org.openqa.selenium.WebDriver; public class SignOnHelper {
Android IDtimestampandroid_idpackage_nameword_idword_textlearning_event_type