Repository: elimu-ai/webapp
Pull request #2172: feat: load SupportedLanguages dynamically in publish.jsp
Author: login: lushlife99
Created: 2025-05-02T10:01:12Z | Merged: 2025-05-05T17:19:39Z

### Issue Number
<!-- Which issue does this PR address? E.g. "Resolves #123" -->
* Resolves #2132 

### Purpose
<!-- What is the purpose of this PR? Why is it needed? -->
* To fetch the list of supported languages dynamically in publish.jsp in order to design a more scalable and maintainable application.

### Technical Details
<!-- Are there any key aspects of the implementation to highlight? -->
* 

### Testing Instructions
<!-- How can the reviewer verify the functionality or fix introduced by this PR? Please provide steps. -->
* 

### Screenshots
<!-- If this PR affects the UI, please include before/after screenshots demonstrating the change(s). -->
* The UI remains the same before and after. Only the data-fetching method has been changed to a dynamic approach.


<!-- Attribution: External code is properly credited. -->

---

### Format Checks
<!-- If this PR contains files with format violations, run 'mvn spotless:apply' to fix them. -->

> [!NOTE]
> Files in PRs are automatically checked for format violations with `mvn spotless:check`.

If this PR contains files with format violations, run `mvn spotless:apply` to fix them.



Diff:
diff --git a/src/main/java/ai/elimu/web/contributions/PublishController.java b/src/main/java/ai/elimu/web/contributions/PublishController.java
new file mode 100644
index 000000000..692d3f719
--- /dev/null
+++ b/src/main/java/ai/elimu/web/contributions/PublishController.java
@@ -0,0 +1,28 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.model.v2.enums.Language;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.Arrays;
+
+@Controller
+@RequestMapping("/contributions/publish")
+@Slf4j
+public class PublishController {
+
+    @GetMapping
+    public String handleRequest(Model model) {
+        log.info("handleRequest");
+
+        Language[] languages = Arrays.stream(Language.values())
+                .filter(lang -> lang != Language.ENG)
+                .toArray(Language[]::new);
+        model.addAttribute("supportedLanguages", languages);
+
+        return "contributions/publish";
+    }
+}
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/jsp/contributions/publish.jsp b/src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
index 193d36a2d..9bce2e937 100644
--- a/src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
+++ b/src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
@@ -4,18 +4,18 @@
 
 <content:section cssId="publishPage">
     <h2><content:gettitle /></h2>
-    
+
     <p>
         Do you have (or wish to develop) an Android app/game/puzzle which teaches literacy/numeracy skills to disadvantaged children?
     </p>
-    
+
     <p>
-        We are building a platform of educational <a href="<spring:url value='/apps' />">apps and games</a> that teaches 
-        a child to read, write and perform arithmetic fully autonomously and without the aid of a qualified teacher. 
-        This will help bring quality basic education to the more than <a href="http://uis.unesco.org/sites/default/files/documents/fs46-more-than-half-children-not-learning-en-2017.pdf" target="_blank">387 million children</a> 
+        We are building a platform of educational <a href="<spring:url value='/apps' />">apps and games</a> that teaches
+        a child to read, write and perform arithmetic fully autonomously and without the aid of a qualified teacher.
+        This will help bring quality basic education to the more than <a href="http://uis.unesco.org/sites/default/files/documents/fs46-more-than-half-children-not-learning-en-2017.pdf" target="_blank">387 million children</a>
         worldwide who are not achieving minimum proficiency levels.
     </p>
-    
+
     <div class="row section">
         <div class="col s12 m4">
             <img src="<spring:url value='/static/img/publish-nya.png' />" alt="Nya's Space Quest" style="border-radius: 8px;" />
@@ -27,15 +27,15 @@
             <img src="<spring:url value='/static/img/publish-literacy2.png' />" alt="elimu.ai" style="border-radius: 8px;" />
         </div>
     </div>
-    
+
     <p>
         <i class="left material-icons medium deep-purple-text">grade</i>
-        For each literacy and numeracy skill our learning platform detect the apps that are most 
+        For each literacy and numeracy skill our learning platform detect the apps that are most
         effective at teaching the children. The best-performing apps are then automatically promoted.
     </p>
-    
+
     <h4>Instructions</h4>
-    
+
     <ol class="browser-default">
         <li>Select <b>one</b> literacy/numeracy skill that your app/game will be teaching.</li>
         <li>Create a new repository for the project on <a href="https://github.com/elimu-ai" target="_blank">GitHub</a>.</li>
@@ -44,60 +44,66 @@
         <li>Add build and release instructions for generating APK files.</li>
         <li>Once quality-approved, we release the app and initiate user testing and usage data collection.</li>
     </ol>
-    
+
     <div class="divider"></div>
-    
+
     <p>
-        If you have any questions, contact us at <a href="mailto:info@elimu.ai">info@elimu.ai</a> or via 
+        If you have any questions, contact us at <a href="mailto:info@elimu.ai">info@elimu.ai</a> or via
         <a href="https://discord.gg/9rz4XYJJDE">Discord</a>.
     </p>
 </content:section>
 
 <content:aside>
     <h5 class="center">FAQ</h5>
-    
+
     <ul class="browser-default">
         <li>
             <b>What type of license is required?</b><br />
-            We require all code to be licensed under the <a href="https://opensource.org/licenses/MIT">MIT License</a> 
+            We require all code to be licensed under the <a href="https://opensource.org/licenses/MIT">MIT License</a>
             or equivalent, creating a global public good available for anyone to use and build upon.<br />
             <br />
         </li>
         <li>
             <b>What languages should the app/game support?</b><br />
-            As a minimum, the app/game should support English, and at least one of the following languages: 
-            Hindi/Tagalog/Thai/Vietnamese. 
-            We target primary age children (about 6 to 11 years) who are out of school and who speak one of these 
+            As a minimum, the app/game should support English, and at least one of the following languages:
+
+            <ul>
+                <c:forEach var="language" items="${supportedLanguages}" varStatus="status">
+                    ${language.englishName}<c:if test="${!status.last}">/</c:if>
+                </c:forEach>.
+            </ul>
+
+            We target primary age children (about 6 to 11 years) who are out of school and who speak one of these
             languages as their mother tongue. And we continuously <a href="https://github.com/elimu-ai/wiki/blob/main/LOCALIZATION.md#how-to-add-support-for-a-new-language" target="_blank">add support for more languages</a>.<br />
             <br />
         </li>
         <li>
             <b>Do I have to submit one APK file per language?</b><br />
-            No. If the code is the same for all languages, you only need to submit one APK file. 
-            For scalability (and ease of future maintenance), we encourage you to design your code to work with 
+            No. If the code is the same for all languages, you only need to submit one APK file.
+            For scalability (and ease of future maintenance), we encourage you to design your code to work with
             multiple types of languages.<br />
             <br />
         </li>
         <li>
             <b>What content should I use in my app?</b><br />
-            The elimu.ai platform offers a <a href="https://github.com/elimu-ai/content-provider">content provider 
-            library</a> that will automatically provide educational content matching the current skill level of each 
+            The elimu.ai platform offers a <a href="https://github.com/elimu-ai/content-provider">content provider
+            library</a> that will automatically provide educational content matching the current skill level of each
             child.<br />
             <br />
         </li>
         <li>
             <b>Is Internet access allowed?</b><br />
-            No, the devices will in many cases be used in areas where there is little or no Internet access, 
-            so all software has to work offline. When configuring a new Android device, the 
-            <a href="https://github.com/elimu-ai/appstore">elimu.ai Appstore</a> will take care of downloading and 
+            No, the devices will in many cases be used in areas where there is little or no Internet access,
+            so all software has to work offline. When configuring a new Android device, the
+            <a href="https://github.com/elimu-ai/appstore">elimu.ai Appstore</a> will take care of downloading and
             installing all the necessary apps and content.<br />
             <br />
         </li>
         <li>
             <b>What devices are being used?</b><br />
-            We are building our software for Android devices with 6"-10" displays installed with  
+            We are building our software for Android devices with 6"-10" displays installed with
             Android 8.0 (API 26) or higher.<br />
             <br />
         </li>
     </ul>
-</content:aside>
+</content:aside>
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/spring/applicationContext.xml b/src/main/webapp/WEB-INF/spring/applicationContext.xml
index a93c097ee..b0357862e 100644
--- a/src/main/webapp/WEB-INF/spring/applicationContext.xml
+++ b/src/main/webapp/WEB-INF/spring/applicationContext.xml
@@ -81,8 +81,7 @@
                 <prop key="/error/session-timeout">viewController</prop>
                 
                 <prop key="/contributions/privacy-policy">viewController</prop>
-                <prop key="/contributions/publish">viewController</prop>
-                
+
                 <prop key="/apps">viewController</prop>
             </props>
         </property>


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/entity/enums/SupportedLanguage.java (1)</summary><blockquote>

`1-18`: **Well-structured enum for supported languages.**

The enum is clean and follows good practices with Lombok annotations to reduce boilerplate. Each language has an associated display name that can be used in the UI.


However, consider these enhancements for future scalability:
1. Adding ISO language codes (e.g., "hi" for Hindi) would help with internationalization
2. Adding Javadoc to explain the purpose of this enum


```diff
package ai.elimu.entity.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * Enum representing the languages supported by the application.
 * Used for dynamic rendering of language options in the UI.
 */
@AllArgsConstructor
@Getter
public enum SupportedLanguage {
-    HINDI("Hindi"),
-    TAGALOG("Tagalog"),
-    THAI("Thai"),
-    VIETNAMESE("Vietnamese")
+    HINDI("Hindi", "hi"),
+    TAGALOG("Tagalog", "tl"),
+    THAI("Thai", "th"),
+    VIETNAMESE("Vietnamese", "vi")

    ;

    private final String displayName;
+    private final String isoCode;

}
```

</blockquote></details>
<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (1)</summary><blockquote>

`10-23`: **Controller implementation is clean but has naming inconsistency.**

The controller is well-structured and properly maps to the correct URL path. It successfully provides the supported languages to the view.


However, there's a potential naming inconsistency: the attribute name "supportedLanguage" is singular but contains multiple languages. Also, the log message could be more descriptive.


```diff
@Controller
@RequestMapping("/contributions/publish")
@Slf4j
public class PublishController {

    @GetMapping
    public String handleRequest(Model model) {
-        log.info("handleRequest");
+        log.info("Preparing publish page with supported languages");

-        model.addAttribute("supportedLanguage", SupportedLanguage.values());
+        model.addAttribute("supportedLanguages", SupportedLanguage.values());

        return "contributions/publish";
    }
}
```

If you make this change, don't forget to update the JSP file to use the new attribute name:

```diff
-<c:forEach var="language" items="${supportedLanguage}" varStatus="status">
+<c:forEach var="language" items="${supportedLanguages}" varStatus="status">
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 67640e8ed59ec4585f21d45237a3e378b0b27c11 and d1130ab2498647f8236c1fc931fef3d1ecc95111.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `src/main/webapp/WEB-INF/spring/applicationContext.xml` is excluded by `!**/*.xml`

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `src/main/java/ai/elimu/entity/enums/SupportedLanguage.java` (1 hunks)
* `src/main/java/ai/elimu/web/contributions/PublishController.java` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (12)</summary>

* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_rest
* GitHub Check: build (windows-latest, 21)
* GitHub Check: build (macos-latest, 21)
* GitHub Check: build (windows-latest, 17)
* GitHub Check: build (ubuntu-latest, 17)
* GitHub Check: build (ubuntu-latest, 21)
* GitHub Check: build (macos-latest, 17)
* GitHub Check: test_rest

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T10:03:59Z
user: login: jo-elimu
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-02T13:36:00Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (1)</summary><blockquote>

`19-19`: **Consider using the attribute name suggested in previous feedback**

A previous review comment suggested using "supportedLanguage" as the attribute name. While "languages" is semantically reasonable since it's a collection, consistency with team conventions is important.



```diff
- model.addAttribute("languages", Language.values());
+ model.addAttribute("supportedLanguage", Language.values());
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (1)</summary><blockquote>

`1-24`: **Consider adding JavaDoc documentation**

Adding JavaDoc documentation to the class and method would improve code maintainability and help other developers understand the purpose and functionality of this controller more easily.


```diff
package ai.elimu.web.contributions;

import ai.elimu.entity.enums.Language;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

+/**
+ * Controller responsible for handling requests to the contributions publish page.
+ * Provides the list of supported languages to the view.
+ */
@Controller
@RequestMapping("/contributions/publish")
@Slf4j
public class PublishController {

+    /**
+     * Handles GET requests to the contributions publish page.
+     * Adds the list of supported languages to the model.
+     *
+     * @param model The Spring MVC model
+     * @return The view name to render
+     */
    @GetMapping
    public String handleRequest(Model model) {
        log.info("handleRequest");

        model.addAttribute("languages", Language.values());

        return "contributions/publish";
    }
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d1130ab2498647f8236c1fc931fef3d1ecc95111 and cbd76c9e976e35037e88b3f5ee3c32a9a641d762.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `src/main/java/ai/elimu/entity/enums/Language.java` (1 hunks)
* `src/main/java/ai/elimu/web/contributions/PublishController.java` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* src/main/java/ai/elimu/entity/enums/Language.java

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/main/webapp/WEB-INF/jsp/contributions/publish.jsp

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (12)</summary>

* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_rest
* GitHub Check: build (ubuntu-latest, 21)
* GitHub Check: build (ubuntu-latest, 17)
* GitHub Check: build (windows-latest, 21)
* GitHub Check: build (windows-latest, 17)
* GitHub Check: build (macos-latest, 21)
* GitHub Check: build (macos-latest, 17)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (4)</summary>

`1-9`: **Appropriate imports and package structure**

The file has proper imports for Spring MVC controller annotations, the Language enum, and Lombok's Slf4j annotation for logging. The package structure follows the expected conventions for a web controller.

---

`10-13`: **Well-structured controller setup**

The controller is properly annotated with `@Controller` and mapped to "/contributions/publish" with `@RequestMapping`. The use of Lombok's `@Slf4j` for logging is a good practice that reduces boilerplate code.

---

`15-18`: **Good request handling method implementation**

The method is correctly annotated with `@GetMapping` to handle GET requests, and properly logs the method entry which will be helpful for debugging and monitoring.

---

`21-23`: **Return value correctly points to the JSP view**

The return value "contributions/publish" correctly maps to the corresponding JSP file that will render the supported languages.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-03T06:16:30Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (2)</summary><blockquote>

`21-24`: **Consider using a more appropriate attribute name**

The attribute name "supportedLanguage" is singular but contains multiple languages. Consider renaming it to "supportedLanguages" (plural) for better clarity.

```diff
-        model.addAttribute("supportedLanguage", languages);
+        model.addAttribute("supportedLanguages", languages);
```

---

`18-27`: **Consider adding Javadoc comments**

Adding Javadoc comments to explain the purpose of the method and the meaning of the returned model attributes would improve code maintainability.

```diff
+    /**
+     * Handles GET requests to the contributions/publish page.
+     * Provides the view with all supported languages except English.
+     *
+     * @param model The Spring model to add attributes to
+     * @return The view name for rendering
+     */
     @GetMapping
     public String handleRequest(Model model) {
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between cbd76c9e976e35037e88b3f5ee3c32a9a641d762 and 5856fc35a45bf4f21dc711f8a61050f283064212.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/main/java/ai/elimu/web/contributions/PublishController.java` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/main/webapp/WEB-INF/jsp/contributions/publish.jsp

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (10)</summary>

* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: build (windows-latest, 21)
* GitHub Check: build (windows-latest, 17)
* GitHub Check: build (ubuntu-latest, 17)
* GitHub Check: build (ubuntu-latest, 21)
* GitHub Check: test_rest
* GitHub Check: test_rest
* GitHub Check: test_rest

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/web/contributions/PublishController.java (1)</summary>

`1-28`: **Good architecture choice for dynamic language fetching**

The introduction of a dedicated controller to provide supported languages dynamically is a good architectural decision. This approach improves maintainability by centralizing language management logic instead of hardcoding values in the JSP.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-03T07:56:52Z
user: login: jo-elimu
body: Use the existing `Language` enum instead.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-03T08:01:14Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🔭 Outside diff range comments (3)</summary><blockquote>

<details>
<summary>src/main/java/ai/elimu/entity/content/multimedia/Video.java (1)</summary><blockquote>

`42-46`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Consider removing the deprecated thumbnail field**

The thumbnail field is marked as @Deprecated, but still required as @NotNull. If it's no longer needed, consider making it optional or implementing a migration plan to completely remove it in the future.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check how the thumbnail field is used throughout the codebase
rg "video.*thumbnail|thumbnail.*video" --type java
```

Length of output: 731

---



</details>

**Remove or Refactor Deprecated `thumbnail` Field**

The `thumbnail` byte[] is still actively used by:

- `src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp`
- `src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp`
- `src/main/java/ai/elimu/web/download/VideoController.java`
- `src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java`

Either:
- Remove the `@Deprecated` annotation (and leave the field `@NotNull`) if you intend to keep this built-in thumbnail support,  
or
- Fully migrate off this field—refactor or remove all the above references—and then drop the field (and its `@NotNull`) in a follow-up.

</blockquote></details>
<details>
<summary>src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java (1)</summary><blockquote>

`72-84`: _⚠️ Potential issue_

**`bytes` may remain `null` when an `IOException` occurs**

`bytes` is declared outside the `try` block and assigned inside it.  
If `multipartFile.getBytes()` throws, the catch only logs the error; `result` is not marked with an error, so execution continues and `bytes` stays `null`.

Any subsequent call that dereferences the array (e.g. `bytes.length` or passing it to GitHub LFS) will then raise a `NullPointerException`.

Add error handling that:

1. Sets a validation error, and  
2. Short-circuits further processing when `bytes == null`.

```diff
 } catch (IOException e) {
     log.error(e.getMessage());
+    result.rejectValue("bytes", "IOException");
 }

+if (bytes == null) {
+    return "content/multimedia/video/create";
+}
```

</blockquote></details>
<details>
<summary>src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java (1)</summary><blockquote>

`100-110`: _⚠️ Potential issue_

**Same `IOException` / `null`-bytes risk as in create flow**

Replicate the guard introduced in `VideoCreateController` so that editing a video does not proceed when the uploaded file cannot be read.

```diff
 } catch (IOException e) {
     log.error(e.getMessage());
+    result.rejectValue("bytes", "IOException");
 }

+if (bytes == null) {
+    return "content/multimedia/video/edit";
+}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (9)</summary><blockquote>

<details>
<summary>src/test/java/selenium/content/video/VideoEditPage.java (1)</summary><blockquote>

`1-15`: **Good implementation of the VideoEditPage page object.**

This is a clean implementation of a Selenium page object for the video edit page. It correctly verifies that the page has loaded by checking for the presence of an element with ID "videoEditPage".

However, consider enhancing this page object with methods that represent user actions that can be performed on this page, such as form interactions or navigation to other pages.


```diff
 package selenium.content.video;

 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;

 public class VideoEditPage {
     
     private WebDriver driver;

     public VideoEditPage(WebDriver driver) {
         this.driver = driver;

         driver.findElement(By.id("videoEditPage"));
     }
+    
+    // Example of methods that could be added:
+    public void setTitle(String title) {
+        driver.findElement(By.id("title")).clear();
+        driver.findElement(By.id("title")).sendKeys(title);
+    }
+    
+    public VideoListPage clickCancel() {
+        driver.findElement(By.id("cancelButton")).click();
+        return new VideoListPage(driver);
+    }
+    
+    public VideoListPage submitForm() {
+        driver.findElement(By.id("submitButton")).click();
+        return new VideoListPage(driver);
+    }
 }
```

</blockquote></details>
<details>
<summary>src/test/java/selenium/content/video/VideoCreatePage.java (1)</summary><blockquote>

`1-15`: **Good implementation of the VideoCreatePage page object.**

This is a clean implementation of a Selenium page object for the video creation page. It correctly verifies that the page has loaded by checking for the presence of an element with ID "videoCreatePage".

Similar to the VideoEditPage, consider enhancing this page object with methods that represent user actions that can be performed on this page, such as form interactions and form submission.


```diff
 package selenium.content.video;

 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;

 public class VideoCreatePage {
     
     private WebDriver driver;

     public VideoCreatePage(WebDriver driver) {
         this.driver = driver;

         driver.findElement(By.id("videoCreatePage"));
     }
+    
+    // Example of methods that could be added:
+    public void setTitle(String title) {
+        driver.findElement(By.id("title")).sendKeys(title);
+    }
+    
+    public void uploadVideo(String filePath) {
+        driver.findElement(By.id("videoFile")).sendKeys(filePath);
+    }
+    
+    public VideoListPage submitForm() {
+        driver.findElement(By.id("submitButton")).click();
+        return new VideoListPage(driver);
+    }
 }
```

</blockquote></details>
<details>
<summary>src/test/java/ai/elimu/rest/v2/content/VideosRestControllerTest.java (1)</summary><blockquote>

`1-28`: **Good test coverage for the Video REST endpoint**

The test verifies that the REST API returns a non-empty list of videos with valid titles. Consider adding additional assertions to verify other important fields of the returned Video objects, such as fileSize, checksumMd5, and videoFormat.

```diff
 JSONObject videoJsonObject = videosJSONArray.getJSONObject(0);
 assertNotNull(videoJsonObject.getString("title"));
+assertNotNull(videoJsonObject.getInt("fileSize"));
+assertNotNull(videoJsonObject.getString("checksumMd5"));
+assertNotNull(videoJsonObject.getString("videoFormat"));
```

</blockquote></details>
<details>
<summary>src/main/java/ai/elimu/web/servlet/CustomDispatcherServlet.java (2)</summary><blockquote>

`266-283`: **Good initialization of Video entity**

The implementation for creating a placeholder Video follows the same pattern as for other content types. A few suggestions:

1. Consider using a try-with-resources block for handling resource loading.
2. Consolidate resource loading for both the video and thumbnail to avoid duplicate code.

```diff
 Video video = new Video();
 video.setTitle("placeholder");
 try {
     ResourceLoader resourceLoader = new ClassRelativeResourceLoader(this.getClass());
-    Resource resource = resourceLoader.getResource("placeholder.mp4");
-    byte[] bytes = Files.readAllBytes(resource.getFile().toPath());
+    Resource videoResource = resourceLoader.getResource("placeholder.mp4");
+    Resource thumbnailResource = resourceLoader.getResource("placeholder.png");
+    
+    byte[] bytes = Files.readAllBytes(videoResource.getFile().toPath());
     video.setFileSize(bytes.length);
     video.setChecksumMd5(ChecksumHelper.calculateMD5(bytes));
     video.setChecksumGitHub("1331d7c476649f4449ec7c6663fc107ce2b4d88b");
-    video.setThumbnail(Files.readAllBytes(new ClassRelativeResourceLoader(this.getClass()).getResource("placeholder.png").getFile().toPath()));
+    video.setThumbnail(Files.readAllBytes(thumbnailResource.getFile().toPath()));
 } catch (IOException e) {
     logger.error(null, e);
 }
```

---

`276-276`: **Avoid hardcoding the GitHub checksum**

The GitHub checksum value is hardcoded. Consider generating it dynamically or documenting why this specific value is used.

```diff
-    video.setChecksumGitHub("1331d7c476649f4449ec7c6663fc107ce2b4d88b");
+    // Using a placeholder GitHub checksum for development/testing
+    video.setChecksumGitHub("1331d7c476649f4449ec7c6663fc107ce2b4d88b");
```

</blockquote></details>
<details>
<summary>src/test/java/selenium/content/video/VideoListPage.java (1)</summary><blockquote>

`11-15`: **Make `driver` field `final`**

The WebDriver reference is immutable after construction; marking it `final` communicates that intent and makes the class thread-safe for free.

```diff
-    private WebDriver driver;
+    private final WebDriver driver;
```

</blockquote></details>
<details>
<summary>src/test/java/selenium/content/video/VideoTest.java (3)</summary><blockquote>

`15-18`: **Class structure looks good but lacks documentation.**

The test class is well-structured with proper annotations and field declarations. Consider adding Javadoc comments to document the purpose of this test class and its relationship to the video content functionality.

```diff
@Slf4j
+/**
+ * Selenium UI tests for video-related pages, including navigation 
+ * to video list, video edit, and video create pages.
+ */
public class VideoTest {
    
    private WebDriver driver;
```

---

`19-36`: **Setup method is well-implemented but consider adding WebDriver wait configuration.**

The setup method correctly initializes the ChromeDriver with headless option support, which is great for CI/CD pipelines. However, the test might be prone to flakiness without explicit wait conditions for page elements.

Consider adding a WebDriverWait configuration to make tests more reliable:

```diff
driver = new ChromeDriver(chromeOptions);
+ // Configure implicit or explicit waits to improve test reliability
+ driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

driver.get(DomainHelper.getBaseUrl() + "/content");
```

---

`1-72`: **Consider testing more complex user flows**

The current tests verify navigation, but don't test actual functionality like uploading a video, filling form fields, or submitting forms. Consider adding more comprehensive tests that verify the full user workflow.

For example, add a test that:
1. Navigates to the video create page
2. Uploads a test video file
3. Fills in required metadata
4. Submits the form
5. Verifies the video appears in the list

This would provide more thorough test coverage for the video functionality.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fe08de1b77f94adc453d586e572a2b6718294e4b and 9fa0e2cb6ab99ca71fbc88439efab1f31e40777c.

</details>

<details>
<summary>⛔ Files ignored due to path filters (3)</summary>

* `pom.xml` is excluded by `!**/*.xml`
* `src/main/resources/ai/elimu/web/servlet/placeholder.mp4` is excluded by `!**/*.mp4`, `!**/*.mp4`
* `src/main/webapp/WEB-INF/spring/applicationContext.xml` is excluded by `!**/*.xml`

</details>

<details>
<summary>📒 Files selected for processing (26)</summary>

* `pom-dependency-tree.txt` (1 hunks)
* `src/main/java/ai/elimu/dao/VideoDao.java` (1 hunks)
* `src/main/java/ai/elimu/dao/jpa/VideoDaoJpa.java` (1 hunks)
* `src/main/java/ai/elimu/entity/content/multimedia/Video.java` (2 hunks)
* `src/main/java/ai/elimu/entity/enums/Language.java` (1 hunks)
* `src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java` (1 hunks)
* `src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java` (3 hunks)
* `src/main/java/ai/elimu/web/content/multimedia/video/VideoCsvExportController.java` (2 hunks)
* `src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java` (3 hunks)
* `src/main/java/ai/elimu/web/contributions/PublishController.java` (1 hunks)
* `src/main/java/ai/elimu/web/download/VideoController.java` (0 hunks)
* `src/main/java/ai/elimu/web/servlet/CustomDispatcherServlet.java` (3 hunks)
* `src/main/resources/META-INF/jpa-schema-export.sql` (1 hunks)
* `src/main/resources/db/migration/2005108.sql` (1 hunks)
* `src/main/resources/db/migration/2005109.sql` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/content/main.jsp` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/content/multimedia/video/create.jsp` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp` (2 hunks)
* `src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp` (1 hunks)
* `src/test/java/ai/elimu/rest/v2/content/VideosRestControllerTest.java` (1 hunks)
* `src/test/java/selenium/content/MainContentPage.java` (1 hunks)
* `src/test/java/selenium/content/video/VideoCreatePage.java` (1 hunks)
* `src/test/java/selenium/content/video/VideoEditPage.java` (1 hunks)
* `src/test/java/selenium/content/video/VideoListPage.java` (1 hunks)
* `src/test/java/selenium/content/video/VideoTest.java` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* src/main/java/ai/elimu/web/download/VideoController.java

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (6)</summary>

* src/main/resources/db/migration/2005108.sql
* src/main/webapp/WEB-INF/jsp/content/main.jsp
* src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp
* src/main/resources/db/migration/2005109.sql
* src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java
* pom-dependency-tree.txt

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (3)</summary>

* src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
* src/main/java/ai/elimu/web/contributions/PublishController.java
* src/main/java/ai/elimu/entity/enums/Language.java

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/test/java/selenium/content/video/VideoTest.java (2)</summary><blockquote>

<details>
<summary>src/test/java/ai/elimu/rest/v2/content/VideosRestControllerTest.java (1)</summary>

* `Slf4j` (13-28)

</details>
<details>
<summary>src/test/java/selenium/content/MainContentPage.java (1)</summary>

* `MainContentPage` (7-61)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (12)</summary><blockquote>

<details>
<summary>src/test/java/selenium/content/MainContentPage.java (1)</summary>

`56-60`: **Appropriate addition of pressVideoListLink method.**

The method follows the same consistent pattern as the other navigation methods in this class, providing a clean abstraction for Selenium tests that need to navigate to the video list page.

</details>
<details>
<summary>src/main/webapp/WEB-INF/jsp/content/multimedia/video/create.jsp (1)</summary>

`84-84`: **Good architectural change from form:input to plain HTML input.**

This change aligns with the architectural shift away from storing raw video bytes directly in the `Video` entity. Using a plain HTML input instead of Spring's form binding is appropriate since the bytes are no longer directly bound to the model attribute.

</details>
<details>
<summary>src/main/java/ai/elimu/dao/VideoDao.java (1)</summary>

`13-13`: **Good addition of readByChecksumMd5 method.**

This method supports the deduplication logic by allowing lookup of videos by their MD5 checksum, which is an important part of the new approach where videos are identified by their checksum rather than storing duplicate raw bytes.

</details>
<details>
<summary>src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp (2)</summary>

`92-92`: **Good architectural change from form:input to plain HTML input.**

This change is consistent with the same change in the create.jsp and aligns with the architectural shift away from storing raw video bytes in the `Video` entity.

---

`361-363`: **Good addition of file size display.**

The display of file size information is a helpful addition that complements the architectural changes where video file size is tracked instead of storing raw bytes. This provides useful metadata to users editing videos.

</details>
<details>
<summary>src/main/java/ai/elimu/web/content/multimedia/video/VideoCsvExportController.java (2)</summary>

`30-30`: **CSV header updated to include the new file_size column.**

The CSV header now includes the "file_size" column, which reflects the changes in the Video entity model. This properly aligns with the broader refactoring that replaced the bytes field with fileSize.

---

`41-41`: **File size information now included in CSV export.**

The video's fileSize property is now being included in the CSV export, which ensures that consumers of this CSV data have access to the file size information. This is consistent with the entity model changes.

</details>
<details>
<summary>src/main/resources/META-INF/jpa-schema-export.sql (1)</summary>

`512-512`: **Video table updated with fileSize field.**

The Video table schema has been updated to include the fileSize integer field, which replaces the previous bytes longblob field. This change is part of the broader refactoring to store file metadata rather than raw binary content.

</details>
<details>
<summary>src/main/java/ai/elimu/dao/jpa/VideoDaoJpa.java (1)</summary>

`27-39`: **Implementation looks good!**

The new `readByChecksumMd5` method follows the same pattern as the existing `read` method, with proper error handling for the `NoResultException` case. This method will be useful for deduplication of videos based on their content rather than just titles.

</details>
<details>
<summary>src/main/java/ai/elimu/entity/content/multimedia/Video.java (1)</summary>

`23-27`: **Good addition of documentation for fileSize field**

The Javadoc comment clearly explains the purpose of the field and aligns with the code changes.

</details>
<details>
<summary>src/test/java/selenium/content/video/VideoTest.java (2)</summary>

`38-43`: **Proper resource cleanup in tearDown method.**

The tearDown method correctly quits the WebDriver, which is essential for releasing browser resources after each test.

---

`1-72`: <details>
<summary>❌ Incorrect review comment</summary>

**Missing page objects in repository**

The test relies on three page object classes (`VideoListPage`, `VideoEditPage`, and `VideoCreatePage`) that need to be implemented. Ensure these classes are included in your PR or already exist in the repository.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the required page object classes exist
echo "Checking for required page object classes..."
for file in "VideoListPage.java" "VideoEditPage.java" "VideoCreatePage.java"
do
  if [ -f "src/test/java/selenium/content/video/$file" ]; then
    echo "✅ $file exists"
  else
    echo "❌ $file is missing"
  fi
done
```

Length of output: 743

---



</details>

**Required page object classes are present**  
All three page object classes exist under `src/test/java/selenium/content/video`:  
- VideoListPage.java  
- VideoEditPage.java  
- VideoCreatePage.java  

You can ignore the previous comment about missing page objects.  


> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-03T08:43:40Z
user: login: jo-elimu
body: @lushlife99 Looks good now. The only missing change is making the attribute name plural instead of singular.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-04T10:53:21Z
user: login: jo-elimu
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-05T17:19:31Z

Review comments:
diff_hunk: @@ -65,8 +65,14 @@
         </li>
         <li>
             <b>What languages should the app/game support?</b><br />
-            As a minimum, the app/game should support English, and at least one of the following languages: 
-            Hindi/Tagalog/Thai/Vietnamese. 
+            As a minimum, the app/game should support English, and at least one of the following languages:
+
+            <ul>
+                <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
+                    ${language.displayName}<c:if test="${!status.last}">/</c:if>
+                </c:forEach>.
+            </ul>
path: src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Dynamic language rendering implemented, but formatting needs improvement.**

The implementation successfully replaces hardcoded languages with dynamic rendering, which is good for maintainability. However, there are some formatting issues to address.


1. The period at the end of the language list is part of the markup, which could look awkward as languages are added or removed
2. The indentation is inconsistent with the rest of the file

Suggested improvement:

```diff
            <ul>
-                <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
-                    ${language.displayName}<c:if test="${!status.last}">/</c:if>
-                </c:forEach>.
+                <li>
+                    <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
+                        ${language.displayName}<c:if test="${!status.last}">/</c:if>
+                    </c:forEach>
+                </li>
            </ul>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            <ul>
                <li>
                    <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
                        ${language.displayName}<c:if test="${!status.last}">/</c:if>
                    </c:forEach>
                </li>
            </ul>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T10:03:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,23 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.entity.enums.SupportedLanguage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping("/contributions/publish")
+@Slf4j
+public class PublishController {
+
+    @GetMapping
+    public String handleRequest(Model model) {
+        log.info("handleRequest");
+
+        model.addAttribute("supportedLanguage", SupportedLanguage.values());
path: src/main/java/ai/elimu/web/contributions/PublishController.java
user: login: jo-elimu
body: ```suggestion
        model.addAttribute("supportedLanguage", Language.values());
```

Use the existing `Language` enum instead.
created_at: 2025-05-02T13:35:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,28 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.model.v2.enums.Language;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.Arrays;
+
+@Controller
+@RequestMapping("/contributions/publish")
+@Slf4j
+public class PublishController {
+
+    @GetMapping
+    public String handleRequest(Model model) {
+        log.info("handleRequest");
+
+        Language[] languages = Arrays.stream(Language.values())
+                .filter(lang -> lang != Language.ENG)
+                .toArray(Language[]::new);
path: src/main/java/ai/elimu/web/contributions/PublishController.java
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify the exclusion of English language is intentional**

The code is filtering out `Language.ENG` from the supported languages. Ensure this is intentional and consistent with the application requirements. The exclude logic implies English is available in the enum but should not be presented as an option.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check the Language enum definition to understand the values and any comments
find src/main/java -name "Language.java" -type f -exec cat {} \;

# Look for any references to excluding English in requirements or documentation
grep -r "exclude.*English" --include="*.md" --include="*.txt" .
grep -r "filter.*ENG" --include="*.java" .
```

Length of output: 578

---



</details>

**Fix invalid `Language.ENG` reference**  
The `Language` enum no longer defines an `ENG` constant, so the filter condition causes a compilation error. Remove or update this line to reflect the current enum values.

• File: `src/main/java/ai/elimu/web/contributions/PublishController.java`  
  Lines 21–23

Suggested diff:
```diff
-        Language[] languages = Arrays.stream(Language.values())
-                .filter(lang -> lang != Language.ENG)
-                .toArray(Language[]::new);
+        // Remove ENG filter; Language.ENG has been removed from the enum
+        Language[] languages = Language.values();
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        // Remove ENG filter; Language.ENG has been removed from the enum
        Language[] languages = Language.values();
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T07:56:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.model.v2.enums.Language;
path: src/main/java/ai/elimu/web/contributions/PublishController.java
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify the correct Language enum import**

Ensure that you're importing the correct Language enum. The import statement references `ai.elimu.model.v2.enums.Language`, but the AI summary mentions it's defined in `src/main/java/ai/elimu/entity/enums/Language.java`.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the Language enum exists in the imported package
find src/main/java/ai/elimu/model/v2/enums/ -name "Language.java" -type f

# Check if there's another Language enum in the entity package
find src/main/java/ai/elimu/entity/enums/ -name "Language.java" -type f
```

Length of output: 269

---



</details>

**Fix incorrect Language import**

The package `ai.elimu.model.v2.enums.Language` doesn’t exist in the codebase. Update the import to reference the `Language` enum in the `entity` package:

- File: `src/main/java/ai/elimu/web/contributions/PublishController.java`, line 3

```diff
-import ai.elimu.model.v2.enums.Language;
+import ai.elimu.entity.enums.Language;
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import ai.elimu.entity.enums.Language;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T07:56:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.model.v2.enums.Language;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.Arrays;
+
+@Controller
+@RequestMapping("/contributions/publish")
+@Slf4j
+public class PublishController {
+
+    @GetMapping
+    public String handleRequest(Model model) {
+        log.info("handleRequest");
+
+        Language[] languages = Arrays.stream(Language.values())
+                .filter(lang -> lang != Language.ENG)
+                .toArray(Language[]::new);
+        model.addAttribute("supportedLanguage", languages);
+
+        return "contributions/publish";
+    }
path: src/main/java/ai/elimu/web/contributions/PublishController.java
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Ensure JSP view is updated to match the attribute name**

Make sure the JSP view at `contributions/publish.jsp` correctly references the model attribute. If you've made changes to the attribute name, the JSP should be updated accordingly.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check the JSP file to see how it's using the supportedLanguage attribute
grep -r "supportedLanguage" --include="*.jsp" src/main/webapp/
```

Length of output: 1011

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate and inspect the contributions publish JSP to see how it references the languages attribute
file=$(find src/main/webapp -type f -iname publish.jsp | grep -i "contributions" | head -n1)
echo "Inspecting $file"
sed -n '1,200p' "$file"
```

Length of output: 5742

---



</details>

**Rename the model attribute to match the JSP**  

In `PublishController.java`, you’re adding the array under the name `supportedLanguage`, but in `publish.jsp` you’re iterating over `${languages}`. These must match.  

• File: src/main/java/ai/elimu/web/contributions/PublishController.java  
  Change:  
  ```diff
- model.addAttribute("supportedLanguage", languages);
+ model.addAttribute("languages", languages);
  ```  

This aligns the controller with the JSP’s `<c:forEach items="${languages}" …>`.  


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public String handleRequest(Model model) {
        log.info("handleRequest");

        Language[] languages = Arrays.stream(Language.values())
                .filter(lang -> lang != Language.ENG)
                .toArray(Language[]::new);
        model.addAttribute("languages", languages);

        return "contributions/publish";
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T07:56:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+package selenium.content.video;
+
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+public class VideoListPage {
+    
+    private WebDriver driver;
+
+    public VideoListPage(WebDriver driver) {
+        this.driver = driver;
+
+        driver.findElement(By.id("videoListPage"));
+    }
+
+    public void pressRandomVideo() {
+        List<WebElement> links = driver.findElements(By.className("editLink"));
+        int randomIndex = (int) (Math.random() * links.size());
+        WebElement randomLink = links.get(randomIndex);
+        randomLink.click();
+    }
path: src/test/java/selenium/content/video/VideoListPage.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Guard against empty result set before selecting a random video**

`links.size()` can be `0` when the page contains no elements with class `editLink`, which will make `links.get(0)` throw `IndexOutOfBoundsException`.  
Add an explicit guard so the test fails with a clear message rather than an unexpected runtime exception.

```diff
     List<WebElement> links = driver.findElements(By.className("editLink"));
+    if (links.isEmpty()) {
+        throw new IllegalStateException("No video links were found on the page – check test data/setup.");
+    }
-    int randomIndex = (int) (Math.random() * links.size());
+    int randomIndex = ThreadLocalRandom.current().nextInt(links.size());
```
(The `ThreadLocalRandom` helper avoids the cast and is marginally faster than `Math.random`.)  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public void pressRandomVideo() {
        List<WebElement> links = driver.findElements(By.className("editLink"));
+       if (links.isEmpty()) {
+           throw new IllegalStateException("No video links were found on the page – check test data/setup.");
+       }
-       int randomIndex = (int) (Math.random() * links.size());
+       int randomIndex = ThreadLocalRandom.current().nextInt(links.size());
        WebElement randomLink = links.get(randomIndex);
        randomLink.click();
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T08:43:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -122,9 +122,15 @@ public String handleSubmit(
       return "content/multimedia/video/create";
     } else {
       video.setTitle(video.getTitle().toLowerCase());
-      String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
-      video.setChecksumGitHub(checksumGitHub);
       video.setTimeLastUpdate(Calendar.getInstance());
+      Video existingVideoWithSameFileContent = videoDao.readByChecksumMd5(video.getChecksumMd5());
+      if (existingVideoWithSameFileContent != null) {
+        // Re-use existing file
+        video.setChecksumGitHub(existingVideoWithSameFileContent.getChecksumGitHub());
+      } else {
+        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
+        video.setChecksumGitHub(checksumGitHub);
+      }
path: src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Prevent upload with `null`/empty byte array**

Even with the guard above, defensive programming on the upload path is prudent.  
Validate that `bytes.length > 0` before calling GitHub LFS; otherwise, abort with an explanatory validation error.

```diff
-      } else {
-        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
-        video.setChecksumGitHub(checksumGitHub);
-      }
+      } else if (bytes != null && bytes.length > 0) {
+        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
+        video.setChecksumGitHub(checksumGitHub);
+      } else {
+        result.rejectValue("bytes", "NotNull");
+        return "content/multimedia/video/create";
+      }
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      Video existingVideoWithSameFileContent = videoDao.readByChecksumMd5(video.getChecksumMd5());
      if (existingVideoWithSameFileContent != null) {
        // Re-use existing file
        video.setChecksumGitHub(existingVideoWithSameFileContent.getChecksumGitHub());
      } else if (bytes != null && bytes.length > 0) {
        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
        video.setChecksumGitHub(checksumGitHub);
      } else {
        result.rejectValue("bytes", "NotNull");
        return "content/multimedia/video/create";
      }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits cf24be3 to ce366bb
created_at: 2025-05-03T08:43:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -150,6 +142,14 @@ public String handleSubmit(
       video.setTitle(video.getTitle().toLowerCase());
       video.setTimeLastUpdate(Calendar.getInstance());
       video.setRevisionNumber(video.getRevisionNumber() + 1);
+      Video existingVideoWithSameFileContent = videoDao.readByChecksumMd5(video.getChecksumMd5());
+      if (existingVideoWithSameFileContent != null) {
+        // Re-use existing file
+        video.setChecksumGitHub(existingVideoWithSameFileContent.getChecksumGitHub());
+      } else {
+        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
+        video.setChecksumGitHub(checksumGitHub);
+      }
path: src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Skip GitHub upload when no new bytes supplied**

When the user leaves the file input blank during an edit, `multipartFile` is empty, yet the code may attempt to upload `null` bytes.  
Add a positive length check before the upload call (mirrors the create controller suggestion).

```diff
-      } else {
+      } else if (bytes != null && bytes.length > 0) {
         String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
         video.setChecksumGitHub(checksumGitHub);
       }
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      Video existingVideoWithSameFileContent = videoDao.readByChecksumMd5(video.getChecksumMd5());
      if (existingVideoWithSameFileContent != null) {
        // Re-use existing file
        video.setChecksumGitHub(existingVideoWithSameFileContent.getChecksumGitHub());
      } else if (bytes != null && bytes.length > 0) {
        String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, bytes);
        video.setChecksumGitHub(checksumGitHub);
      }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T08:43:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,71 @@
+package selenium.content.video;
+
+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 lombok.extern.slf4j.Slf4j;
+import selenium.content.MainContentPage;
+import selenium.util.DomainHelper;
+
+@Slf4j
+public class VideoTest {
+    
+    private WebDriver driver;
+
+    @BeforeEach
+    public void setUp() { 
+        log.info("setUp");
+
+        ChromeOptions chromeOptions = new ChromeOptions();
+
+        // Read "headless" property set on the command line: 
+        //    mvn clean verify -P regression-test-ui -D headless=true
+        String headlessSystemProperty = System.getProperty("headless");
+        log.info("headlessSystemProperty: \"" + headlessSystemProperty + "\"");
+        if ("true".equals(headlessSystemProperty)) {
+            chromeOptions.addArguments("headless");
+        }
+        
+        driver = new ChromeDriver(chromeOptions);
+
+        driver.get(DomainHelper.getBaseUrl() + "/content");
+    }
+
+    @AfterEach
+    public void tearDown() {
+        log.info("tearDown");
+
+        driver.quit();
+    }
+
+    @Test
+    public void testRandomVideoEditPage() {
+        log.info("testRandomVideoEditPage");
+        
+        MainContentPage mainContentPage = new MainContentPage(driver);
+        mainContentPage.pressVideoListLink();
+
+        VideoListPage videoListPage = new VideoListPage(driver);
+        videoListPage.pressRandomVideo();
+        log.info("driver.getCurrentUrl(): " + driver.getCurrentUrl());
+
+        VideoEditPage videoEditPage = new VideoEditPage(driver);
+    }
path: src/test/java/selenium/content/video/VideoTest.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Test method lacks explicit assertions.**

While the test navigates through the UI correctly using page objects, it doesn't contain explicit assertions to verify expected behaviors beyond page existence. This limits the test's effectiveness in catching regression issues.

Add explicit assertions to verify the expected state or elements on the video edit page:

```diff
VideoEditPage videoEditPage = new VideoEditPage(driver);
+ // Verify that critical elements of the edit page are present
+ assertTrue(videoEditPage.hasEditForm(), "Edit form should be present");
+ assertTrue(videoEditPage.hasVideoPreview(), "Video preview should be present");
```

Note: You'll need to add these methods to the VideoEditPage class.


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T08:43:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,71 @@
+package selenium.content.video;
+
+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 lombok.extern.slf4j.Slf4j;
+import selenium.content.MainContentPage;
+import selenium.util.DomainHelper;
+
+@Slf4j
+public class VideoTest {
+    
+    private WebDriver driver;
+
+    @BeforeEach
+    public void setUp() { 
+        log.info("setUp");
+
+        ChromeOptions chromeOptions = new ChromeOptions();
+
+        // Read "headless" property set on the command line: 
+        //    mvn clean verify -P regression-test-ui -D headless=true
+        String headlessSystemProperty = System.getProperty("headless");
+        log.info("headlessSystemProperty: \"" + headlessSystemProperty + "\"");
+        if ("true".equals(headlessSystemProperty)) {
+            chromeOptions.addArguments("headless");
+        }
+        
+        driver = new ChromeDriver(chromeOptions);
+
+        driver.get(DomainHelper.getBaseUrl() + "/content");
+    }
+
+    @AfterEach
+    public void tearDown() {
+        log.info("tearDown");
+
+        driver.quit();
+    }
+
+    @Test
+    public void testRandomVideoEditPage() {
+        log.info("testRandomVideoEditPage");
+        
+        MainContentPage mainContentPage = new MainContentPage(driver);
+        mainContentPage.pressVideoListLink();
+
+        VideoListPage videoListPage = new VideoListPage(driver);
+        videoListPage.pressRandomVideo();
+        log.info("driver.getCurrentUrl(): " + driver.getCurrentUrl());
+
+        VideoEditPage videoEditPage = new VideoEditPage(driver);
+    }
+
+    @Test
+    public void testVideoCreatePage() {
+        log.info("testVideoCreatePage");
+
+        MainContentPage mainContentPage = new MainContentPage(driver);
+        mainContentPage.pressVideoListLink();
+
+        VideoListPage videoListPage = new VideoListPage(driver);
+        videoListPage.pressCreateButton();
+
+        VideoCreatePage videoCreatePage = new VideoCreatePage(driver);
+    }
path: src/test/java/selenium/content/video/VideoTest.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Test method lacks explicit assertions.**

Similar to the first test method, this test navigates to the video create page but lacks explicit assertions to verify the expected elements or state of the page.

Add assertions to verify critical elements on the create page:

```diff
VideoCreatePage videoCreatePage = new VideoCreatePage(driver);
+ // Verify that critical elements of the create page are present
+ assertTrue(videoCreatePage.hasUploadForm(), "Upload form should be present");
+ assertTrue(videoCreatePage.hasRequiredFields(), "Required fields should be present");
```

Note: You'll need to add these methods to the VideoCreatePage class.


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T08:43:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+package ai.elimu.web.contributions;
+
+import ai.elimu.model.v2.enums.Language;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.Arrays;
+
+@Controller
+@RequestMapping("/contributions/publish")
+@Slf4j
+public class PublishController {
+
+    @GetMapping
+    public String handleRequest(Model model) {
+        log.info("handleRequest");
+
+        Language[] languages = Arrays.stream(Language.values())
+                .filter(lang -> lang != Language.ENG)
+                .toArray(Language[]::new);
+        model.addAttribute("supportedLanguage", languages);
path: src/main/java/ai/elimu/web/contributions/PublishController.java
user: login: jo-elimu
body: ```suggestion
        model.addAttribute("supportedLanguages", languages);
```
created_at: 2025-05-04T10:49:14Z
author_association: MEMBER
diff_hunk: @@ -44,60 +44,66 @@
         <li>Add build and release instructions for generating APK files.</li>
         <li>Once quality-approved, we release the app and initiate user testing and usage data collection.</li>
     </ol>
-    
+
     <div class="divider"></div>
-    
+
     <p>
-        If you have any questions, contact us at <a href="mailto:info@elimu.ai">info@elimu.ai</a> or via 
+        If you have any questions, contact us at <a href="mailto:info@elimu.ai">info@elimu.ai</a> or via
         <a href="https://discord.gg/9rz4XYJJDE">Discord</a>.
     </p>
 </content:section>
 
 <content:aside>
     <h5 class="center">FAQ</h5>
-    
+
     <ul class="browser-default">
         <li>
             <b>What type of license is required?</b><br />
-            We require all code to be licensed under the <a href="https://opensource.org/licenses/MIT">MIT License</a> 
+            We require all code to be licensed under the <a href="https://opensource.org/licenses/MIT">MIT License</a>
             or equivalent, creating a global public good available for anyone to use and build upon.<br />
             <br />
         </li>
         <li>
             <b>What languages should the app/game support?</b><br />
-            As a minimum, the app/game should support English, and at least one of the following languages: 
-            Hindi/Tagalog/Thai/Vietnamese. 
-            We target primary age children (about 6 to 11 years) who are out of school and who speak one of these 
+            As a minimum, the app/game should support English, and at least one of the following languages:
+
+            <ul>
+                <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
path: src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
user: login: jo-elimu
body: ```suggestion
                <c:forEach var="language" items="${supportedLanguages}" varStatus="status">
```
created_at: 2025-05-04T10:52:50Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-05-02T10:03:48Z
body: ## [Codecov](https://app.codecov.io/gh/elimu-ai/webapp/pull/2172?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai) Report
Attention: Patch coverage is `25.00000%` with `6 lines` in your changes missing coverage. Please review.
> Project coverage is 15.40%. Comparing base [(`34daffd`)](https://app.codecov.io/gh/elimu-ai/webapp/commit/34daffd70c90b3e029c9b7d3c0b4a0d150ee9097?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai) to head [(`7de2e82`)](https://app.codecov.io/gh/elimu-ai/webapp/commit/7de2e82864b246f74c2252e94e3e72af9c4608fe?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai).
> Report is 25 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/elimu-ai/webapp/pull/2172?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai) | Patch % | Lines |
|---|---|---|
| [.../ai/elimu/web/contributions/PublishController.java](https://app.codecov.io/gh/elimu-ai/webapp/pull/2172?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fai%2Felimu%2Fweb%2Fcontributions%2FPublishController.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai#diff-c3JjL21haW4vamF2YS9haS9lbGltdS93ZWIvY29udHJpYnV0aW9ucy9QdWJsaXNoQ29udHJvbGxlci5qYXZh) | 25.00% | [6 Missing :warning: ](https://app.codecov.io/gh/elimu-ai/webapp/pull/2172?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai) |

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##               main    #2172      +/-   ##
============================================
- Coverage     15.40%   15.40%   -0.01%     
- Complexity      389      391       +2     
============================================
  Files           231      232       +1     
  Lines          6108     6122      +14     
  Branches        709      709              
============================================
+ Hits            941      943       +2     
- Misses         5116     5128      +12     
  Partials         51       51              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/elimu-ai/webapp/pull/2172?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=elimu-ai).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-02T10:03:55Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

"""
## Walkthrough

A new Spring MVC controller named `PublishController` has been added to handle GET requests to the `/contributions/publish` URL. This controller provides a list of all supported languages, excluding English, to the view by adding them as a model attribute. The corresponding JSP view (`publish.jsp`) has been updated to dynamically display the list of supported languages using a loop, replacing the previously hardcoded language names. Minor whitespace cleanup was also performed in the JSP file.

## Changes

| File(s)                                                                                         | Change Summary                                                                                                                   |
|------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| `src/main/java/ai/elimu/web/contributions/PublishController.java`                              | New controller added to provide supported languages (excluding English) to the `/contributions/publish` view.                   |
| `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp`                                        | Replaced static language list with dynamic iteration over `supportedLanguages`; cleaned up whitespace.                          |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant PublishController
    participant Model
    participant View (publish.jsp)

    User->>PublishController: GET /contributions/publish
    PublishController->>Model: Add supportedLanguages (all except ENG)
    PublishController->>View (publish.jsp): Return view name with model
    View (publish.jsp)->>User: Render page with dynamic language list
```

## Assessment against linked issues

| Objective                                                                                     | Addressed | Explanation                                                                                   |
|-----------------------------------------------------------------------------------------------|-----------|-----------------------------------------------------------------------------------------------|
| Add controller for `/contributions/publish` that provides all supported languages except ENG (#2132) | ✅        |                                                                                               |
| Make the list of languages available to the JSP for dynamic display (#2132)                   | ✅        |                                                                                               |

## Suggested reviewers

- jo-elimu
- nya-elimu
"""

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ce366bbae3981213ef8000990cd18b8ab131a270 and 7de2e82864b246f74c2252e94e3e72af9c4608fe.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/main/java/ai/elimu/web/contributions/PublishController.java` (1 hunks)
* `src/main/webapp/WEB-INF/jsp/contributions/publish.jsp` (3 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
* src/main/java/ai/elimu/web/contributions/PublishController.java

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (12)</summary>

* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_ui
* GitHub Check: test_rest
* GitHub Check: build (macos-latest, 17)
* GitHub Check: build (windows-latest, 17)
* GitHub Check: build (macos-latest, 21)
* GitHub Check: build (windows-latest, 21)
* GitHub Check: build (ubuntu-latest, 17)
* GitHub Check: build (ubuntu-latest, 21)

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=elimu-ai/webapp&utm_content=2172):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: lushlife99
created_at: 2025-05-02T10:31:27Z
body: Hello @jo-elimu 
I wasn't able to assign you as a reviewer for this PR, but I would really appreciate it if you could take a look when it's convenient for you.
Thank you very much!
author_association: CONTRIBUTOR
user: login: jo-elimu
created_at: 2025-05-03T08:00:11Z
body: @coderabbitai Can you help inform @lushlife99 how to import the `Language` enum from the https://github.com/elimu-ai/model repo?
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-05-03T08:01:32Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
> [!TIP]
> For best results, initiate chat on the files or code changes.

<details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Check if the webapp repo already has a dependency on the model repo in its build file (pom.xml)
find . -name "pom.xml" -type f -exec grep -l "ai.elimu.model" {} \;
```

Length of output: 68

---

🏁 Script executed:
```
#!/bin/bash
# Find the main pom.xml file in the repository
find . -name "pom.xml" -type f | head -n 1
```

Length of output: 42

---

🏁 Script executed:
```
#!/bin/bash
# Check the top-level directories to understand the repository structure
ls -la
```

Length of output: 901

---

🏁 Script executed:
```
#!/bin/bash
# Check if the model dependency is in the dependency tree
cat pom-dependency-tree.txt | grep -i "model"

# Look at the main pom.xml file
cat pom.xml | head -n 50

# Check the current Language enum implementation
cat src/main/java/ai/elimu/entity/enums/Language.java
```

Length of output: 491

---

🏁 Script executed:
```
#!/bin/bash
# Check the dependency details in the dependency tree for more information
cat pom-dependency-tree.txt

# Try to find the Maven repository where dependencies are cached
find ~/.m2 -name "model-*.jar" 2>/dev/null || echo "Model jar not found in local Maven repository"

# Check if there's a POM file that might contain information about dependencies
find . -name "pom.xml" -type f
```

Length of output: 9744

---

🏁 Script executed:
```
#!/bin/bash
# Check for import statements in PublishController.java to see how other model classes might be imported
cat src/main/java/ai/elimu/web/contributions/PublishController.java
```

Length of output: 881

---



</details>

@jo-elimu I can help inform @lushlife99 about the correct way to import the `Language` enum from the model repo.

@lushlife99 The webapp project already has a dependency on the elimu-ai/model repository (version 2.0.97), so you don't need to create a local `Language` enum. Instead, you should use the one provided by the model dependency.

Here's how to properly use it:

1. Remove the local `Language` enum you created at `src/main/java/ai/elimu/entity/enums/Language.java`

2. Import the Language enum from the model repo in your files:
   ```java
   import ai.elimu.model.v2.enums.Language;
   ```

3. Use it as you're already doing in the `PublishController.java` file:
   ```java
   Language[] languages = Arrays.stream(Language.values())
           .filter(lang -> lang != Language.ENG)
           .toArray(Language[]::new);
   ```

This approach has several advantages:
- Avoids code duplication
- Ensures consistency across the project
- Leverages the centralized definitions in the model repository
- Gives you access to all languages defined in the model repo, including `Language.ENG` which you're already filtering out

The `PublishController.java` file is already implementing this correctly, so you can use that as a reference.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: jo-elimu
URL: https://github.com/elimu-ai/webapp/pull/2172

Timestamp: 2025-05-03T08:01:30.192Z
Learning: The elimu-ai/webapp repository has a dependency on ai.elimu:model (version 2.0.97) that provides shared model classes and enums. The Language enum should be imported from ai.elimu.model.v2.enums.Language instead of creating local duplicates.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Check if the webapp repo already has a dependency on the model repo in its build file (pom.xml)
find . -name "pom.xml" -type f -exec grep -l "ai.elimu.model" {} \;
```

Length of output: 68

---

🏁 Script executed:
```
#!/bin/bash
# Find the main pom.xml file in the repository
find . -name "pom.xml" -type f | head -n 1
```

Length of output: 42

---

🏁 Script executed:
```
#!/bin/bash
# Check the top-level directories to understand the repository structure
ls -la
```

Length of output: 901

---

🏁 Script executed:
```
#!/bin/bash
# Check if the model dependency is in the dependency tree
cat pom-dependency-tree.txt | grep -i "model"

# Look at the main pom.xml file
cat pom.xml | head -n 50

# Check the current Language enum implementation
cat src/main/java/ai/elimu/entity/enums/Language.java
```

Length of output: 491

---

🏁 Script executed:
```
#!/bin/bash
# Check the dependency details in the dependency tree for more information
cat pom-dependency-tree.txt

# Try to find the Maven repository where dependencies are cached
find ~/.m2 -name "model-*.jar" 2>/dev/null || echo "Model jar not found in local Maven repository"

# Check if there's a POM file that might contain information about dependencies
find . -name "pom.xml" -type f
```

Length of output: 9744

---

🏁 Script executed:
```
#!/bin/bash
# Check for import statements in PublishController.java to see how other model classes might be imported
cat src/main/java/ai/elimu/web/contributions/PublishController.java
```

Length of output: 881

---



</details>

@jo-elimu I can help inform @lushlife99 about the correct way to import the `Language` enum from the model repo.

@lushlife99 The webapp project already has a dependency on the elimu-ai/model repository (version 2.0.97), so you don't need to create a local `Language` enum. Instead, you should use the one provided by the model dependency.

Here's how to properly use it:

1. Remove the local `Language` enum you created at `src/main/java/ai/elimu/entity/enums/Language.java`

2. Import the Language enum from the model repo in your files:
   ```java
   import ai.elimu.model.v2.enums.Language;
   ```

3. Use it as you're already doing in the `PublishController.java` file:
   ```java
   Language[] languages = Arrays.stream(Language.values())
           .filter(lang -> lang != Language.ENG)
           .toArray(Language[]::new);
   ```

This approach has several advantages:
- Avoids code duplication
- Ensures consistency across the project
- Leverages the centralized definitions in the model repository
- Gives you access to all languages defined in the model repo, including `Language.ENG` which you're already filtering out

The `PublishController.java` file is already implementing this correctly, so you can use that as a reference.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: jo-elimu
URL: https://github.com/elimu-ai/webapp/pull/2172

Timestamp: 2025-05-03T08:01:30.192Z
Learning: The elimu-ai/webapp repository has a dependency on ai.elimu:model (version 2.0.97) that provides shared model classes and enums. The Language enum should be imported from ai.elimu.model.v2.enums.Language instead of creating local duplicates.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->
author_association: CONTRIBUTOR
user: login: lushlife99
created_at: 2025-05-03T08:07:49Z
body: @jo-elimu 

I’ve committed the additional changes using the existing Language enum as suggested.
Please review at your convenience. I look forward to your feedback.
author_association: CONTRIBUTOR
user: login: jo-elimu
created_at: 2025-05-03T08:11:39Z
body: @lushlife99 You also need to push your commit to the branch.
author_association: MEMBER
user: login: lushlife99
created_at: 2025-05-03T11:20:49Z
body: @jo-elimu 

I pushed the changes you suggested!
Please review at your convenience. I look forward to your feedback.
author_association: CONTRIBUTOR
user: login: jo-elimu
created_at: 2025-05-04T02:38:18Z
body: @coderabbitai review the latest changes.
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-05-04T02:39:13Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
> [!TIP]
> For best results, initiate chat on the files or code changes.

<details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# First, get the latest commit information from the PR branch
git log -n 1 --pretty=format:"%H %s"

# Check which files have been changed in the latest commit
git show --name-status HEAD

# Check if there's still a local Language enum definition
find . -name "*.java" -exec grep -l "enum Language" {} \;

# Check PublishController.java to see how the Language enum is now imported and used
cat src/main/java/ai/elimu/web/contributions/PublishController.java

# Check publish.jsp to see how the Language values are used
cat src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
```

Length of output: 48542

---



</details>

@jo-elimu I've reviewed the latest changes to PR #2172, and they look good! The implementation has been properly updated to use the imported Language enum from the model repository.

Here's what was done correctly:

1. `PublishController.java` now properly imports the Language enum from the model repository:
   ```java
   import ai.elimu.model.v2.enums.Language;
   ```

2. The controller filters out English (ENG) from the list of supported languages and adds them to the model:
   ```java
   Language[] languages = Arrays.stream(Language.values())
           .filter(lang -> lang != Language.ENG)
           .toArray(Language[]::new);
   model.addAttribute("supportedLanguage", languages);
   ```

3. The JSP file (`publish.jsp`) now dynamically displays the languages by iterating through the model attribute:
   ```jsp
   <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
       ${language.englishName}<c:if test="${!status.last}">/</c:if>
   </c:forEach>
   ```

4. There is no local Language enum definition in the codebase, confirming that the imported enum is being used correctly.

The implementation is clean, follows best practices, and correctly addresses the requirements of loading the supported languages dynamically in the publish.jsp page.


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# First, get the latest commit information from the PR branch
git log -n 1 --pretty=format:"%H %s"

# Check which files have been changed in the latest commit
git show --name-status HEAD

# Check if there's still a local Language enum definition
find . -name "*.java" -exec grep -l "enum Language" {} \;

# Check PublishController.java to see how the Language enum is now imported and used
cat src/main/java/ai/elimu/web/contributions/PublishController.java

# Check publish.jsp to see how the Language values are used
cat src/main/webapp/WEB-INF/jsp/contributions/publish.jsp
```

Length of output: 48542

---



</details>

@jo-elimu I've reviewed the latest changes to PR #2172, and they look good! The implementation has been properly updated to use the imported Language enum from the model repository.

Here's what was done correctly:

1. `PublishController.java` now properly imports the Language enum from the model repository:
   ```java
   import ai.elimu.model.v2.enums.Language;
   ```

2. The controller filters out English (ENG) from the list of supported languages and adds them to the model:
   ```java
   Language[] languages = Arrays.stream(Language.values())
           .filter(lang -> lang != Language.ENG)
           .toArray(Language[]::new);
   model.addAttribute("supportedLanguage", languages);
   ```

3. The JSP file (`publish.jsp`) now dynamically displays the languages by iterating through the model attribute:
   ```jsp
   <c:forEach var="language" items="${supportedLanguage}" varStatus="status">
       ${language.englishName}<c:if test="${!status.last}">/</c:if>
   </c:forEach>
   ```

4. There is no local Language enum definition in the codebase, confirming that the imported enum is being used correctly.

The implementation is clean, follows best practices, and correctly addresses the requirements of loading the supported languages dynamically in the publish.jsp page.</answer></rawResChunk> -->
author_association: CONTRIBUTOR
user: login: lushlife99
created_at: 2025-05-05T07:47:10Z
body: @jo-elimu 
Thank you for the feedback. I've applied the suggested changes.
author_association: CONTRIBUTOR