Skip to content

Conversation

@adrianfish
Copy link
Contributor

@adrianfish adrianfish commented Dec 11, 2025

https://sakaiproject.atlassian.net/browse/SAK-51674

Summary by CodeRabbit

  • New Features

    • Added letter grading as a permanent grade-entry option alongside points and percentage; letter grades appear in course and assignment views.
  • Improvements

    • Grade display, validation, formatting, charts, statistics, imports/exports and bulk/quick-entry flows now respect the selected grade type.
    • Settings UI updated to choose grade type and configure letter-grade scales.
  • Chores

    • Data/serialization and UI wiring streamlined for consistent grade-type behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds a GradeType enum (POINTS, PERCENTAGE, LETTER) and migrates grade-type handling project-wide: APIs, persistence, business logic, UI, import/export, charts, and tests; introduces letter-grade support (mappings, validation, serialization), related fields, repository query, and new GradingService APIs.

Changes

Cohort / File(s) Change Summary
Core enum & constants
gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java, gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java
Added GradeType enum (POINTS, PERCENTAGE, LETTER); removed integer GRADE_TYPE_* constants.
API signatures & small model edits
gradebookng/api/.../Assignment.java, .../CategoryDefinition.java, .../GradeDefinition.java, .../GradebookInformation.java, .../GradebookHelper.java, .../GradingService.java, .../GraderPermission.java
Switched several fields/params/returns from Integer to GradeType; added Assignment.maxLetterGrade; CategoryDefinition.getTotalPoints(GradeType, Double) signature changed; validateAssignmentNameAndPoints(...) gained GradeType; GradingService surface updated (getGradeEntryType→GradeType, identifyStudentsWithInvalidGrades→Set, new methods: hardDeleteGradebook, getMaxLetterGrade, getMaxPoints); getStandardPermissions() returns immutable list.
Persistence model changes
gradebookng/api/model/AbstractGradeRecord.java, .../Gradebook.java, .../GradebookAssignment.java
Added letterEarned and persisted gradeType (enum) to grade records; Gradebook.gradeType changed to @Enumerated(EnumType.STRING) GradeType; default display flags adjusted; replaced constant checks with enum comparisons.
AssignmentGradeRecord & Category refactor
gradebookng/api/model/AssignmentGradeRecord.java, .../Category.java
AssignmentGradeRecord constructor now accepts pointsGrade and letterGrade; removed several transient/statistics fields and comparator logic; Category had statistic fields and calculation methods removed.
Repository & persistence manager
gradebookng/api/.../AssignmentGradeRecordRepository.java, gradebookng/impl/.../AssignmentGradeRecordRepositoryImpl.java, gradebookng/impl/.../GradingPersistenceManagerImpl.java
Added repository query findByGradableObject_IdAndGradableObject_Removed(Long, Boolean) and implementation; persistence manager branches retrieval by GradeType (LETTER vs numeric).
Business service & tool POM
gradebookng/tool/src/.../GradebookNgBusinessService.java, gradebookng/tool/pom.xml
Converted internal gradingType usage to GradeType across logic (concurrency, parsing, percentage/points conversion); added lessonbuilder-api dependency to tool POM.
Formatting utilities
gradebookng/tool/.../FormatHelper.java, .../CourseGradeFormatter.java
Added overloads formatGradeForDisplay(Double,String) → formatGradeForDisplay(Double, GradeType) and formatGradeForDisplay(String, GradeType); letter grades bypass numeric formatting; callers updated to pass GradeType.
UI data models & table components
gradebookng/tool/.../GbGradebookData.java, .../GbGradeTable.java, .../GbGradeTable.html, .../gradebook-gbgrade-table.js
GbGradebookData constructor extended to accept GradingService; added letter field and gradeType-aware serialization/formatting; GbGradeTable injects GradingService; template renders `${letter
Panels, pages & UI components
gradebookng/tool/src/java/.../panels/*, .../QuickEntryPage.java, .../BulkGradePanel.java, .../SettingsGradeEntryPanel.java, .../SettingsGradeEntryPanel.html
Replaced Integer/GradingConstants with GradeType in many panels; added LETTER radio option in settings UI; components updated for visibility, validation and formatting based on GradeType.
Grade update flow, charts & stats
gradebookng/tool/.../GradeUpdateAction.java, .../AssignmentGradeChart.java, .../AssignmentStatistics.java, .../AssignmentStatisticsPanel.java
GradeUpdateAction validates differently for LETTER vs numeric; charts/statistics derive gradeType and gradeMap from settings, handle letter-axis and mapping to numeric where needed; method signatures adapted for GradeType in helpers.
Import/export & preview
gradebookng/tool/.../importExport/*, .../ImportWizardModel.java
ImportWizardModel annotated @Data and includes GradeType gradeType; import/export steps propagate gradeType and use it for formatting and preview.
Tests
gradebookng/impl/src/test/.../GradingServiceTests.java, gradebookng/tool/src/test/.../TestGradebookNgBusinessService.java, .../TestFormatHelper.java
Tests updated to use GradeType and new FormatHelper overloads; added letter-grade test coverage.
Integrations & misc
msgcntr/.../DiscussionForumTool.java, .../MessageForumStatisticsBean.java, webapi/.../GradesController.java, webcomponents/bundle/.../grader.properties, gradebookng/bundle/.../gradebookng.properties
Replaced constant checks with GradeType enum in integrations; added i18n key for letter entry and chart label; added edit property to grader.properties.
Minor cleanup
gradebookng/impl/.../GradingAuthzImpl.java, .../LetterGradePercentMapping.java
Whitespace/formatting fixes and Iterator→enhanced-for refactors in mapping utilities.

Possibly related PRs

  • sakai#14036: touches GradeUpdateAction initialization and may overlap with GradeUpdateAction/BasePanel changes in this PR (potential code-level overlap on grade-update flow and panel initialization).

Suggested reviewers

  • ern
  • ottenhoff

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.79% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'SAK-51674 grading Add letter grading' directly summarizes the main change: adding letter grading support to the grading system.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java (1)

126-136: Add a null-check on gradeMap in standardizeInputGrade to avoid NPE

Unlike getValue, getGrade, and getGradeMapping, this method assumes gradeMap is non-null; if it ever isn’t (e.g., pre-initialization), gradeMap.keySet() will throw a NullPointerException.

A small defensive check keeps behavior consistent with the rest of the class:

     public String standardizeInputGrade(String inputGrade) {

-        String standardizedGrade = null;
-        for (String grade : gradeMap.keySet()) {
+        String standardizedGrade = null;
+        if (gradeMap == null) {
+            return null;
+        }
+        for (String grade : gradeMap.keySet()) {
             if (grade.equalsIgnoreCase(inputGrade)) {
                 standardizedGrade = grade;
                 break;
             }
         }
         return standardizedGrade;
     }
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/stats/AssignmentStatistics.java (1)

35-67: Guard against missing letter-grade mappings to avoid NPE in stats.

In calculateStatistics():

if (grade != null && grade.getGrade() != null) {
    stats.addValue(gradeType == GradeType.LETTER
        ? gradeMap.get(grade.getGrade())
        : Double.valueOf(grade.getGrade()));
}

If gradeType == GradeType.LETTER but gradeMap is null or does not contain the key for grade.getGrade(), gradeMap.get(...) returns null, which will NPE when auto-unboxed to double.

Consider a defensive approach, e.g.:

-            if (grade != null && grade.getGrade() != null) {
-                stats.addValue(gradeType == GradeType.LETTER ? gradeMap.get(grade.getGrade()) : Double.valueOf(grade.getGrade()));
-            }
+            if (grade != null && grade.getGrade() != null) {
+                if (gradeType == GradeType.LETTER) {
+                    if (gradeMap != null) {
+                        Double mapped = gradeMap.get(grade.getGrade());
+                        if (mapped != null) {
+                            stats.addValue(mapped);
+                        }
+                    }
+                } else {
+                    stats.addValue(Double.valueOf(grade.getGrade()));
+                }
+            }

Optionally, log or count unmapped letter grades so they’re visible operationally.

gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsCategoryPanel.java (1)

787-842: Category weight formatter likely lost the % sign for non-PERCENTAGE gradebooks.

In PercentConverter.convertToString:

final Double percentage = value * 100;
GradeType gradeType = SettingsCategoryPanel.this.businessService
    .getGradebookSettings(SettingsCategoryPanel.this.currentGradebookUid,
                          SettingsCategoryPanel.this.currentSiteId)
    .getGradeType();
return FormatHelper.formatGradeForDisplay(percentage, gradeType);

Category weights are always percentages, independent of the course’s grade entry type. Previously this used FormatHelper.formatDoubleAsPercentage(percentage), which:

  • Always rendered a percentage (e.g., 20%, 89.07%), and
  • Matches how updateRunningTotal still formats the total via formatDoubleAsPercentage(total * 100).

Now, when the gradebook is in POINTS mode, FormatHelper.formatGradeForDisplay(percentage, GradeType.POINTS) will (per your tests) format as a plain number without the % sign, so you likely get 20 in the cell while the running total still shows 20%. That’s a visible regression and inconsistent with the converter’s own Javadoc (“Convert to percentage for display”).

To retain previous behavior and consistency with updateRunningTotal, consider reverting to formatDoubleAsPercentage:

-        final Double percentage = value * 100;
-
-        GradeType gradeType = SettingsCategoryPanel.this.businessService
-            .getGradebookSettings(SettingsCategoryPanel.this.currentGradebookUid,
-                                  SettingsCategoryPanel.this.currentSiteId)
-            .getGradeType();
-
-        return FormatHelper.formatGradeForDisplay(percentage, gradeType);
+        final Double percentage = value * 100;
+        return FormatHelper.formatDoubleAsPercentage(percentage);

If you prefer to route through the new API, you could instead call FormatHelper.formatGradeForDisplay(percentage, GradeType.PERCENTAGE) explicitly, but the key point is to decouple this from the gradebook’s gradeType so weights always render as percentages.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java (1)

91-117: Confirm database migration strategy for existing GRADE_TYPE values in GB_GRADEBOOK_T.

This commit introduces a JPA entity mapping to the existing GB_GRADEBOOK_T table, converting the GRADE_TYPE column from a numeric storage format to EnumType.STRING:

@Column(name = "GRADE_TYPE", nullable = false)
@Enumerated(EnumType.STRING)
private GradeType gradeType = GradeType.POINTS;

The table is referenced in production code (e.g., messageforums), indicating existing data. Ensure:

  • A database migration script (or Hibernate auto-DDL configuration) will convert existing numeric GRADE_TYPE values (if any) to their corresponding enum names (POINTS, PERCENTAGE, LETTER); otherwise JPA will fail to deserialize legacy rows.
  • The new display defaults (courseLetterGradeDisplayed and courseAverageDisplayed both TRUE) are compatible with existing gradebook configurations and UX expectations.
🧹 Nitpick comments (14)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java (1)

18-18: Remove unused import.

The ArrayList import is no longer needed since line 45 now uses List.of() instead of constructing an ArrayList.

Apply this diff to remove the unused import:

-import java.util.ArrayList;
 import java.util.List;
gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java (1)

104-116: Enhanced for-loop in getGradeMapping looks good; consider entrySet/values for minor cleanup

The refactor to a typed enhanced for-loop improves readability and type safety. If you want to tighten it further, you could iterate over entrySet() or values() to avoid the extra map lookup inside the loop, but that’s purely cosmetic here given the tiny map size.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java (1)

73-74: Consider adding documentation for the new field.

The maxLetterGrade field is added without a Javadoc comment. For consistency with other fields like points (line 70-71) and dueDate (line 76-78) that have documentation, consider adding a brief description of this field's purpose.

+    /**
+     * the maximum letter grade for letter-grade entry mode, or null if not applicable.
+     */
     private String maxLetterGrade;
gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java (1)

72-82: Consider adding null-safety checks for input parameters.

The method follows the established pattern in this repository. However, other repository implementations in the codebase (e.g., ValidationAccountRepositoryImpl) include null checks that return early with an empty result when input parameters are null.

Optional improvement for defensive null handling:

 @Transactional(readOnly = true)
 public List<AssignmentGradeRecord> findByGradableObject_IdAndGradableObject_Removed(Long gradableObjectId, Boolean removed) {
+    if (gradableObjectId == null || removed == null) {
+        return new ArrayList<>();
+    }

     Session session = sessionFactory.getCurrentSession();
gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/TestGradebookNgBusinessService.java (1)

56-63: LGTM! Tests updated for new API signature.

The test correctly passes GradeType.POINTS to the updated formatGradeForDisplay method. Consider adding test cases for GradeType.PERCENTAGE and GradeType.LETTER to ensure comprehensive coverage of the new grade type functionality.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java (1)

108-134: Well-implemented grade type aware total points calculation.

The method correctly handles the different grade type scenarios:

  • For POINTS/PERCENTAGE: sums individual assignment points (returns 0.0 for empty lists)
  • For LETTER: calculates based on uniform per-assignment maximum

The validation for LETTER grade type with proper exception handling is appropriate.

Minor simplification using mapToDouble instead of Collectors.summingDouble:

         if (gradeType != GradeType.LETTER) {
             return getAssignmentList().stream()
                     .filter(a -> a.getPoints() != null)
-                    .collect(Collectors.summingDouble(Assignment::getPoints));
+                    .mapToDouble(Assignment::getPoints)
+                    .sum();
         } else {

This is slightly more idiomatic for primitive sum operations and avoids boxing overhead.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1)

50-62: Document and constrain the new skipPointsValidation flag.

The conditional around points validation looks correct, but the public API now allows callers to bypass a previously mandatory points > 0 check. Please:

  • Update the Javadoc to describe when skipPointsValidation may be true (e.g., letter-grade assignments with unused points).
  • Consider validating that callers only pass true in supported scenarios (e.g., via higher-level logic tied to GradeType), to avoid accidentally creating zero/negative‑points numeric items.
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (1)

287-311: Log full rubric association exceptions instead of only toString().

In the rubric association block, the catch currently logs e.toString():

} catch (Exception e) {
    log.warn("Failed to get rubric association for gradebook assignment {}: {}", assignment.getId(), e.toString());
}

This loses the stacktrace and makes troubleshooting harder. Prefer passing the exception itself:

-            } catch (Exception e) {
-                log.warn("Failed to get rubric association for gradebook assignment {}: {}", assignment.getId(), e.toString());
-            }
+            } catch (Exception e) {
+                log.warn("Failed to get rubric association for gradebook assignment {}", assignment.getId(), e);
+            }

This keeps SLF4J parameterization and preserves the full cause.

gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/util/TestFormatHelper.java (1)

259-387: Tests correctly exercise GradeType-aware formatting.

Updating the tests to pass GradeType.POINTS and adding testFormatGradeForDisplayWithLetterGrade align well with the new API and help protect POINTS/LETTER behavior across locales. You might later add a small test for GradeType.PERCENTAGE, but this is not blocking.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java (1)

94-99: Clean up stray extra semicolon on assignmentList.

The field declaration currently has a double semicolon:

@Transient
private List<GradebookAssignment> assignmentList = new ArrayList<>();;

It’s harmless but noisy; consider dropping the extra ; for clarity.

gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsCategoryPanel.java (1)

139-153: Consider caching maxPoints when iterating assignments in LETTER grade mode.

In the category parsing loop:

for (Assignment a : category.getAssignmentList()) {
    if (settings.getGradeType() != GradeType.LETTER) {
        points.add(new BigDecimal(a.getPoints()).setScale(2, RoundingMode.HALF_DOWN));
    } else {
        points.add(BigDecimal.valueOf(
            gradingService.getMaxPoints(settings.getSelectedGradingScaleBottomPercents()).orElse(0D)));
    }
}

gradingService.getMaxPoints(...) will return the same value for every assignment in the loop. You could call it once per settings (or per category) and reuse the result to avoid repeated service lookups:

-        for (Assignment a : category.getAssignmentList()) {
-            if (settings.getGradeType() != GradeType.LETTER) {
+        Double maxPoints = null;
+        for (Assignment a : category.getAssignmentList()) {
+            if (settings.getGradeType() != GradeType.LETTER) {
                 points.add(new BigDecimal(a.getPoints()).setScale(2, RoundingMode.HALF_DOWN));
-            } else {
-                points.add(BigDecimal.valueOf(gradingService.getMaxPoints(settings.getSelectedGradingScaleBottomPercents()).orElse(0D)));
+            } else {
+                if (maxPoints == null) {
+                    maxPoints = gradingService
+                        .getMaxPoints(settings.getSelectedGradingScaleBottomPercents())
+                        .orElse(0D);
+                }
+                points.add(BigDecimal.valueOf(maxPoints));
             }
         }

Not critical, but slightly more efficient and readable.

msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java (1)

4736-4753: GradeType switch correctly normalizes grading mode flags

Switching on GradeType to derive gradeByLetter/gradeByPoints/gradeByPercent is logically correct and ensures only one mode is active at a time. LETTER and PERCENTAGE are handled explicitly, and the default falls back to points, which matches current semantics.

If there is (now or in future) a dedicated GradeType.POINTS constant, consider adding an explicit case POINTS: (falling through to the default block) and optionally logging or asserting on unknown values to make behavior clearer and safer against future enum expansion.

gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java (1)

151-155: Empty catch block in test setup.

The empty catch block silently swallows any exceptions from siteService.getSite(). While acceptable in test setup, consider logging or using a fail-fast approach if the mock setup is critical.

         Site site = mock(Site.class);
         try {
             when(siteService.getSite(siteId)).thenReturn(site);
         } catch (Exception e) {
+            // Site mock setup - exception not expected in test context
         }
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java (1)

76-76: Raw type usage for grade collection.

Using a raw List without generics loses compile-time type safety. While the logic correctly handles the type divergence between LETTER (String) and numeric (Double) grades, consider using a more type-safe approach.

One alternative is to use List<Comparable<?>> or separate the logic paths earlier:

-        final List allGrades = new ArrayList();
+        final List<Comparable<?>> allGrades = new ArrayList<>();

However, the current approach is pragmatic given the different data types needed for letter vs. numeric grades.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cc5219 and 9c55ea0.

📒 Files selected for processing (57)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java (3 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java (2 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java (0 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java (5 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java (2 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AssignmentGradeRecord.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java (5 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java (3 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java (2 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java (1 hunks)
  • gradebookng/bundle/src/main/bundle/gradebookng.properties (2 hunks)
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java (0 hunks)
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java (2 hunks)
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java (1 hunks)
  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java (9 hunks)
  • gradebookng/tool/pom.xml (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java (9 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java (4 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java (3 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java (6 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.java (3 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java (14 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/ImportWizardModel.java (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/pages/QuickEntryPage.java (4 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (7 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AssignmentStatisticsPanel.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BulkGradePanel.java (3 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java (4 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/RubricGradePanel.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsCategoryPanel.java (12 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.html (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/StudentGradeSummaryPanel.html (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/UpdateUngradedItemsPanel.java (4 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/ZeroUngradedItemsPanel.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java (6 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/GradeImportConfirmationStep.java (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/GradeImportUploadStep.java (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/PreviewImportedGradesPanel.java (1 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/stats/AssignmentStatistics.java (4 hunks)
  • gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/TestGradebookNgBusinessService.java (4 hunks)
  • gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/util/TestFormatHelper.java (3 hunks)
  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js (1 hunks)
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java (3 hunks)
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/MessageForumStatisticsBean.java (3 hunks)
  • webapi/src/main/java/org/sakaiproject/webapi/controllers/GradesController.java (2 hunks)
  • webcomponents/bundle/src/main/bundle/grader.properties (1 hunks)
💤 Files with no reviewable changes (2)
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{html,jsp,vm,xhtml,xml}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.{html,jsp,vm,xhtml,xml}: Use Bootstrap 5.2 as the preferred UI framework for styling in Sakai frontends
Leverage Bootstrap 5 components for consistent UI/UX

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/StudentGradeSummaryPanel.html
  • gradebookng/tool/pom.xml
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.html
**/*.{html,jsp,vm,xhtml,xml,css,scss}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

Ensure all UI components work across different screen sizes (Responsive Design)

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/StudentGradeSummaryPanel.html
  • gradebookng/tool/pom.xml
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.html
**/*.{js,html,jsp,vm,xhtml,xml}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

Use the web components in the webcomponents/ directory when possible in Sakai frontends

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/StudentGradeSummaryPanel.html
  • gradebookng/tool/pom.xml
  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.html
**/*.{html,jsp,jspx,xml,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer kebab-case for values of HTML class and id attributes

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/StudentGradeSummaryPanel.html
  • gradebookng/tool/pom.xml
  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.html
**/*.java

📄 CodeRabbit inference engine (.cursor/rules/logging-rule.mdc)

**/*.java: Use SLF4J parameterized logging (logger.info("Value is: {}", value)) instead of string concatenation (logger.info("Value is: " + value))
Log messages and code comments should be in English. Log messages should never be translated.

**/*.java: Java: Never use local variable type inference (var). Always declare explicit types. Yes: Map<String, Integer> counts = new HashMap<>(); No: var counts = new HashMap<String, Integer>();
When proposing Java code, spell out full types in local variable declarations, for loops, and try-with-resources
When editing Java, prefer clarity over brevity; avoid introducing language features that aren't widely used in the repo
Treat any PR or suggestion containing Java var as non-compliant. Recommend replacing with explicit types before merge

**/*.java: Use Java 17 for trunk development (Java 11 was used for Sakai 22 and Sakai 23)
Do not use local variable type inference (var) in Java code. Always declare explicit types (e.g., List<String> names = new ArrayList<>(); not var names = new ArrayList<String>();). Enforced by Checkstyle rule during mvn validate

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/RubricGradePanel.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsGradeEntryPanel.java
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/UpdateUngradedItemsPanel.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/GradeImportUploadStep.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AssignmentStatisticsPanel.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/GradeImportConfirmationStep.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsCategoryPanel.java
  • webapi/src/main/java/org/sakaiproject/webapi/controllers/GradesController.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/MessageForumStatisticsBean.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/pages/QuickEntryPage.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/PreviewImportedGradesPanel.java
  • gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/TestGradebookNgBusinessService.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/ImportWizardModel.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/ZeroUngradedItemsPanel.java
  • gradebookng/tool/src/test/org/sakaiproject/gradebookng/business/util/TestFormatHelper.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BulkGradePanel.java
  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AssignmentGradeRecord.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/stats/AssignmentStatistics.java
**/*.js

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.js: Use clean, standard modern JavaScript in Sakai frontends
Update jQuery code to modern JavaScript when making changes, if the changes are minimal
Prefer ES6+ features (arrow functions, template literals, destructuring, etc.) in JavaScript
Write modular, reusable JavaScript components in Sakai frontends
Minimize use of global variables and functions (Avoid Global Scope) in JavaScript

Files:

  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Target evergreen browsers; assume ES2022+ features and browser APIs like fetch keepalive are present; avoid legacy branches, UA sniffing, or fallbacks unless a specific evergreen gap is documented
Replace jQuery with modern DOM APIs when touching code; new work should not add jQuery dependencies
Compose Lit components, ES modules, and encapsulated helpers; keep state local and explicit with modular code
Prefer module scope or class fields; expose intentional APIs instead of incidental globals; avoid global side channels

Files:

  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js
**/*/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Internal reactive state in Lit components should stay prefixed with _ and is only surfaced through getters/setters when required

Files:

  • gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js
🧠 Learnings (4)
📓 Common learnings
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 0
File: :0-0
Timestamp: 2025-10-07T15:11:27.298Z
Learning: In samigo’s Total Scores view (samigo/samigo-app/src/webapp/jsf/evaluation/totalScores.jsp), mailto links were hidden after commit dee05746 (PR #12312, SAK-49674) added a render check requiring email.fromEmailAddress to be non-empty; PR #14154 (SAK-52058) restores visibility by checking only description.email.
📚 Learning: 2025-09-27T15:41:26.287Z
Learnt from: csev
Repo: sakaiproject/sakai PR: 14094
File: lti/lti-api/src/java/org/sakaiproject/lti/beans/LtiContentBean.java:1-19
Timestamp: 2025-09-27T15:41:26.287Z
Learning: csev prefers to use Apache 2.0 license headers for new code he writes, even when the project standard is ECL 2.0, as the licenses are compatible.

Applied to files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/SettingsCategoryPanel.java
📚 Learning: 2025-10-07T15:11:27.298Z
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 0
File: :0-0
Timestamp: 2025-10-07T15:11:27.298Z
Learning: In samigo’s Total Scores view (samigo/samigo-app/src/webapp/jsf/evaluation/totalScores.jsp), mailto links were hidden after commit dee05746 (PR #12312, SAK-49674) added a render check requiring email.fromEmailAddress to be non-empty; PR #14154 (SAK-52058) restores visibility by checking only description.email.

Applied to files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html
📚 Learning: 2025-10-10T15:13:08.361Z
Learnt from: ern
Repo: sakaiproject/sakai PR: 14152
File: reset-pass/account-validator-impl/src/test/java/org/sakaiproject/accountvalidator/test/AccountValidationTestConfiguration.java:69-72
Timestamp: 2025-10-10T15:13:08.361Z
Learning: In the Sakai project, test-specific mock configurations should remain in the test class itself as part of the test logic. Only mock configurations that are common across multiple test classes should be centralized in test configuration classes.

Applied to files:

  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java
🧬 Code graph analysis (4)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1)
gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js (1)
  • points (846-846)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/ImportWizardModel.java (2)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java (1)
  • Data (30-127)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java (1)
  • Data (93-108)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (1)
lessonbuilder/api/src/java/org/sakaiproject/lessonbuildertool/api/LessonBuilderConstants.java (1)
  • LessonBuilderConstants (18-21)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java (2)
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java (1)
  • NumberUtil (35-143)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java (1)
  • GbGradebookData (59-759)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: maven-build
  • GitHub Check: sakai-deploy
  • GitHub Check: maven-build
  • GitHub Check: maven-build
  • GitHub Check: maven-build
  • GitHub Check: sakai-deploy

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java (1)

151-161: Potential NPE from autounboxing dropKeepEnabled in getPointsForCategory

dropKeepEnabled is a Boolean, but Line 152 uses it in if (!dropKeepEnabled), which triggers autounboxing. If dropKeepEnabled is ever null (which is plausible, given you’ve added a null-safe getDropKeepEnabled() earlier), this will throw a NullPointerException.

Consider switching this check to use the null-safe getter:

-    public Double getPointsForCategory() {
-        if (!dropKeepEnabled) {
+    public Double getPointsForCategory() {
+        if (!getDropKeepEnabled()) {
             return null;
         }

This aligns behavior with the rest of the class and removes the NPE risk.

🧹 Nitpick comments (4)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (1)

158-182: OnChangeAjaxBehavior attached for PERCENTAGE but has no effect.

The OnChangeAjaxBehavior is added for both POINTS and PERCENTAGE grade types (line 158: gradingType != GradeType.LETTER), but the scaling logic inside only executes for POINTS (line 167). For PERCENTAGE, the behavior is attached but does nothing, which adds a small overhead.

Consider either:

  • Moving the behavior attachment inside the if (gradingType == GradeType.POINTS) check to avoid unnecessary behavior for PERCENTAGE, or
  • Documenting why PERCENTAGE needs the behavior (e.g., for future extensibility)

Example refactor to eliminate the overhead:

-		if (gradingType != GradeType.LETTER) {
-			// onchange, might want to scale
-			points.add(new OnChangeAjaxBehavior() {
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				protected void onUpdate(final AjaxRequestTarget target) {
-
-					// conditional option to scale
-					if (gradingType == GradeType.POINTS) {
-
-						final Double existing = AddOrEditGradeItemPanelContent.this.existingPoints;
-						final Double current = points.getModelObject();
-
-						log.debug("existingPoints: {}", existing);
-						log.debug("currentPoints: {}", current);
-						AddOrEditGradeItemPanelContent.this.scaleGradesTriggered = existing != null && !existing.equals(current);
-
-						log.debug("scaleGradesTriggered: {}", AddOrEditGradeItemPanelContent.this.scaleGradesTriggered);
-
-						target.add(AddOrEditGradeItemPanelContent.this.scaleGradesContainer);
-					}
-				}
-			});
-		}
+		if (gradingType == GradeType.POINTS) {
+			// onchange, might want to scale
+			points.add(new OnChangeAjaxBehavior() {
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				protected void onUpdate(final AjaxRequestTarget target) {
+					final Double existing = AddOrEditGradeItemPanelContent.this.existingPoints;
+					final Double current = points.getModelObject();
+
+					log.debug("existingPoints: {}", existing);
+					log.debug("currentPoints: {}", current);
+					AddOrEditGradeItemPanelContent.this.scaleGradesTriggered = existing != null && !existing.equals(current);
+
+					log.debug("scaleGradesTriggered: {}", AddOrEditGradeItemPanelContent.this.scaleGradesTriggered);
+
+					target.add(AddOrEditGradeItemPanelContent.this.scaleGradesContainer);
+				}
+			});
+		}
gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java (3)

610-705: GradeType-based handling and concurrency checks look correct; minor cleanup possible

Using GradeType gradingType = gradebook.getGradeType(); and branching on GradeType.PERCENTAGE for the conversion logic is consistent with the enum migration, and the reintroduced letter-grade concurrency check restores protection for GradeType.LETTER. The numeric branch using NumberFormat and BigDecimal also preserves the SAK-42001 behavior.

You could slightly simplify and avoid an extra service call by reusing the local variable in the concurrency block:

if (gradingType != GradeType.LETTER) {
    ...
} else {
    ...
}

Functionally it’s equivalent but a bit clearer.


927-982: Grade comparison matrix now correctly driven by GradeType

Accepting a GradeType gradingType parameter and appending % only when gradingType == GradeType.PERCENTAGE keeps the comparison display consistent with the configured entry type while leaving points and letter grades unchanged. The security advisor usage and mapping over GbStudentGradeInfo remain intact.

If you find more formatting logic cropping up, consider centralizing the "%" decision into a helper (e.g., in FormatHelper) to avoid scattered grade-type branching.


2264-2272: Clarify intent of null category-type argument to calculateCategoryScore

The new call:

final Optional<CategoryScoreData> result =
    gradingService.calculateCategoryScore(gradebook.getId(), studentUuid, categoryId, isInstructor, null);

compiles, but it’s not obvious what null represents, especially since related bulk APIs here pass gradebook.getCategoryType() as the last parameter.

For consistency and to avoid surprises in the service implementation, consider either:

  • Passing gradebook.getCategoryType() instead of null, if that’s what the service expects, or
  • Adding a brief comment that null is intentional and means “let the service infer the category type from the gradebook”.

This will make the behavior clearer to future maintainers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c55ea0 and 6c7fb7b.

📒 Files selected for processing (8)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java (3 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1 hunks)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java (1 hunks)
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java (2 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java (9 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java (14 hunks)
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java
  • gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

📄 CodeRabbit inference engine (.cursor/rules/logging-rule.mdc)

**/*.java: Use SLF4J parameterized logging (logger.info("Value is: {}", value)) instead of string concatenation (logger.info("Value is: " + value))
Log messages and code comments should be in English. Log messages should never be translated.

**/*.java: Java: Never use local variable type inference (var). Always declare explicit types. Yes: Map<String, Integer> counts = new HashMap<>(); No: var counts = new HashMap<String, Integer>();
When proposing Java code, spell out full types in local variable declarations, for loops, and try-with-resources
When editing Java, prefer clarity over brevity; avoid introducing language features that aren't widely used in the repo
Treat any PR or suggestion containing Java var as non-compliant. Recommend replacing with explicit types before merge

**/*.java: Use Java 17 for trunk development (Java 11 was used for Sakai 22 and Sakai 23)
Do not use local variable type inference (var) in Java code. Always declare explicit types (e.g., List<String> names = new ArrayList<>(); not var names = new ArrayList<String>();). Enforced by Checkstyle rule during mvn validate

Files:

  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java
  • gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java
  • gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java
🧠 Learnings (1)
📓 Common learnings
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 0
File: :0-0
Timestamp: 2025-10-07T15:11:27.298Z
Learning: In samigo’s Total Scores view (samigo/samigo-app/src/webapp/jsf/evaluation/totalScores.jsp), mailto links were hidden after commit dee05746 (PR #12312, SAK-49674) added a render check requiring email.fromEmailAddress to be non-empty; PR #14154 (SAK-52058) restores visibility by checking only description.email.
🧬 Code graph analysis (2)
gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1)
gradebookng/tool/src/webapp/scripts/gradebook-gbgrade-table.js (1)
  • points (846-846)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (1)
lessonbuilder/api/src/java/org/sakaiproject/lessonbuildertool/api/LessonBuilderConstants.java (1)
  • LessonBuilderConstants (18-21)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: maven-build
  • GitHub Check: maven-build
  • GitHub Check: sakai-deploy
🔇 Additional comments (16)
gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/AddOrEditGradeItemPanelContent.java (6)

52-52: LGTM: Import additions support enum migration.

The GradeType enum and LessonBuilderConstants imports are properly used throughout the file.

Also applies to: 55-55


93-93: LGTM: Enum migration improves type safety.

Replacing Integer with the GradeType enum provides better type safety and code clarity.


102-102: LGTM: Diamond operator improves readability.

The diamond operator syntax is a standard Java modernization with no functional impact.


126-132: LGTM: Label logic correctly handles all grade types.

The conditional logic appropriately shows labels for PERCENTAGE and POINTS, while hiding the label for LETTER grade type where no numeric input is needed.


197-197: LGTM: Scale grades correctly hidden for letter grading.

Setting scaleGradesContainer visibility based on grade type ensures the scaling option is never shown for LETTER grades, where scaling doesn't apply.


295-311: Code correctly follows all guidelines and properly handles LETTER grade incompatibility.

The implementation:

  • Uses explicit type declarations (Optional<AssociationTransferBean>) instead of var
  • Employs parameterized logging: log.warn("Failed to get rubric association for gradebook assignment {}", assignment.getId(), e)
  • Uses null-safe comparison with StringUtils.equals
  • Consistently hides rubric UI for LETTER grades (matching pattern on lines 156 and 197 where points and scaling are similarly hidden)

The LETTER grade restriction is an intentional architectural pattern that prevents point-based features (including rubrics) from being configured for letter-graded assignments, which is sensible design.

gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/model/GbGradebookData.java (5)

327-329: LGTM! Conditional serialization based on grade type.

The serialization logic correctly branches to use getLetter() for letter grades and getScore() for numeric grades, ensuring appropriate data handling for each grade type.


418-420: LGTM! Clean enum-based grade type detection.

The settings serialization correctly uses the GradeType enum for grade entry type detection and includes letter grades in the settings map for UI consumption.

Also applies to: 439-439


475-475: LGTM! Grade type propagated through formatting.

The signature change correctly adds GradeType parameter and propagates it to FormatHelper.formatGradeForDisplay(), ensuring consistent grade formatting based on the configured grade type.

Also applies to: 485-485, 489-489


636-636: Method signatures and API usage are correct.

The code correctly calls category.getTotalPoints(settings.getGradeType(), gradingService.getMaxPoints(courseGradeMap).orElse(0D)):

  1. CategoryDefinition.getTotalPoints(GradeType gradeType, Double maxPoints) signature matches the usage, accepting grade type and a Double value for maximum points.
  2. GradingService.getMaxPoints(Map<String, Double> gradeMap) correctly returns Optional<Double>, which is properly unwrapped with orElse(0D) to provide a Double fallback.
  3. The implementation validates maxPoints when gradeType is LETTER (throws IllegalArgumentException if maxPoints ≤ 0), which aligns with the requirement for letter grade calculations.

No issues identified.


578-578: No action needed. The courseGradeMap parameter is correct.

The method signature confirms getMaxLetterGrade(Map<String, Double> gradeMap) expects a map with String keys (grade letters) and Double values (percentage thresholds). The courseGradeMap variable is correctly typed as Map<String, Double> and represents the active grading scale. The implementation finds the highest percentage threshold and returns its associated letter grade, making this the appropriate usage.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java (1)

52-135: Grade-type-aware total points and non-null assignmentList look correct

  • Initializing assignmentList to new ArrayList<>() is a good defensive move and avoids null-stream issues by default.
  • In getTotalPoints(GradeType gradeType, Double maxPoints), the split between non-LETTER (sum of non-null assignment points) and LETTER (validated maxPoints > 0D, then assignmentList.size() * maxPoints) is clear and matches the Javadoc.
  • Argument validation via IllegalArgumentException for invalid maxPoints in LETTER mode is appropriate and fails fast.

No issues from a correctness perspective here.

gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java (1)

51-63: LETTER-aware points validation is consistent and preserves name checks

The updated validateAssignmentNameAndPoints(Assignment assignmentDefinition, GradeType gradeType):

  • Correctly skips the points > 0 validation when gradeType == GradeType.LETTER, allowing letter-only assignments.
  • Preserves existing behavior for POINTS/PERCENTAGE (non-LETTER) by still enforcing points != null && points > 0.
  • Leaves validateGradeItemName(assignmentDefinition.getName()) untouched, so title validation semantics remain the same.

This change cleanly aligns validation with the new grade type model.

gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java (3)

83-83: GradeType import is appropriate

Importing GradeType here matches its later usage in this class and keeps references concise; no issues.


770-788: GradeType usage in saveExcuse aligns with percentage semantics

Switching the excuse logic to branch on GradeType.PERCENTAGE while leaving other types untouched preserves the existing “stored as points, convert to percentage for the service call” behavior and matches how saveGrade treats percentage entry. No functional concerns here.


1724-1745: Updated GradingService calls with siteId look consistent

Using the new signatures gradingService.addAssignment(gradebookUid, siteId, assignment) and gradingService.getAssignments(gradebookUid, siteId, SortType.SORT_BY_NONE) inside addAssignment matches the rest of this class’s service usage and ensures assignments are resolved in the correct site context. The surrounding sort-order logic is unchanged and remains correct.

…est/GradingServiceTests.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java (5)

141-155: User mock IDs and base Site mock in setup()

Stubbing getId() for user1User/user2User and adding a default Site mock for siteService.getSite(siteId) should support the newer grading/section logic that depends on user IDs and site lookup. Swallowing the exception around getSite is effectively harmless here (Mockito won’t throw), but if this ever did fail it might be clearer to call Assert.fail(...) as in createSiteMock so the test fails loudly rather than silently skipping the stub.


474-490: GradeType integration in grade definitions looks correct; assertion style nit

Using GradeType.POINTS when constructing the GradeDefinitions and then verifying the gradeEntryType via getGradesWithoutCommentsForStudentsForItems aligns with the new enum-based API and keeps the tests realistic. For enums, it’s a bit more idiomatic to use assertEquals(GradeType.POINTS, defs.get(0).getGradeEntryType()); rather than assertTrue(GradeType.POINTS == ...), purely for readability.

Also applies to: 780-781


657-704: testPercentageCalculationWithWeight is mostly tautological and comments are out of sync with the data

This test currently computes the percentage manually, writes it back into gradeDef with setGrade(String.valueOf((int) percentage)), and then asserts on that same value, so it doesn’t actually validate what gradingService returns in percentage mode. The explanatory comments also still refer to 10/20 while the data used is 15/30. Consider asserting on the value coming from gradingService before mutating gradeDef, and updating the comments to match the actual numbers, so the test genuinely exercises the new percentage-grade behavior rather than confirming its own calculation.


784-858: Letter-grading flow test is solid; a couple of optional assertions could deepen coverage

The letterGrading test does a nice job of wiring up gradebook settings, per-user permissions, section membership, letter grades on an assignment, and the resulting CourseGradeTransferBean points mapping (83/73 out of 100), which directly exercises the new letter-grade support. If you want to push coverage a bit further, you might also:

  • Assert that gradingService.getGradebookInformation(...).getGradeType() is GradeType.LETTER after the settings update, and/or
  • Assert that the GradeDefinitions returned for this assignment have GradeType.LETTER as gradeEntryType,

to fully exercise the enum across both assignment-level and course-grade flows.


879-902: Local var usage conflicts with Sakai Java style guidelines

This helper method still uses var for the CategoryDefinition instances and the cats list, which conflicts with the stated guideline to avoid local variable type inference and use explicit types. As per coding guidelines, consider cleaning this up the next time you touch this block, e.g.:

-        var cd1 = new CategoryDefinition();
+        CategoryDefinition cd1 = new CategoryDefinition();
@@
-        var cd2 = new CategoryDefinition();
+        CategoryDefinition cd2 = new CategoryDefinition();
@@
-        var cats = new ArrayList<CategoryDefinition>();
+        List<CategoryDefinition> cats = new ArrayList<CategoryDefinition>();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7fb7b and ccb33a5.

📒 Files selected for processing (1)
  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

📄 CodeRabbit inference engine (.cursor/rules/logging-rule.mdc)

**/*.java: Use SLF4J parameterized logging (logger.info("Value is: {}", value)) instead of string concatenation (logger.info("Value is: " + value))
Log messages and code comments should be in English. Log messages should never be translated.

**/*.java: Java: Never use local variable type inference (var). Always declare explicit types. Yes: Map<String, Integer> counts = new HashMap<>(); No: var counts = new HashMap<String, Integer>();
When proposing Java code, spell out full types in local variable declarations, for loops, and try-with-resources
When editing Java, prefer clarity over brevity; avoid introducing language features that aren't widely used in the repo
Treat any PR or suggestion containing Java var as non-compliant. Recommend replacing with explicit types before merge

**/*.java: Use Java 17 for trunk development (Java 11 was used for Sakai 22 and Sakai 23)
Do not use local variable type inference (var) in Java code. Always declare explicit types (e.g., List<String> names = new ArrayList<>(); not var names = new ArrayList<String>();). Enforced by Checkstyle rule during mvn validate

Files:

  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 0
File: :0-0
Timestamp: 2025-10-07T15:11:27.298Z
Learning: In samigo’s Total Scores view (samigo/samigo-app/src/webapp/jsf/evaluation/totalScores.jsp), mailto links were hidden after commit dee05746 (PR #12312, SAK-49674) added a render check requiring email.fromEmailAddress to be non-empty; PR #14154 (SAK-52058) restores visibility by checking only description.email.
📚 Learning: 2025-10-07T15:11:27.298Z
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 0
File: :0-0
Timestamp: 2025-10-07T15:11:27.298Z
Learning: In samigo’s Total Scores view (samigo/samigo-app/src/webapp/jsf/evaluation/totalScores.jsp), mailto links were hidden after commit dee05746 (PR #12312, SAK-49674) added a render check requiring email.fromEmailAddress to be non-empty; PR #14154 (SAK-52058) restores visibility by checking only description.email.

Applied to files:

  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java
📚 Learning: 2025-10-10T15:13:08.361Z
Learnt from: ern
Repo: sakaiproject/sakai PR: 14152
File: reset-pass/account-validator-impl/src/test/java/org/sakaiproject/accountvalidator/test/AccountValidationTestConfiguration.java:69-72
Timestamp: 2025-10-10T15:13:08.361Z
Learning: In the Sakai project, test-specific mock configurations should remain in the test class itself as part of the test logic. Only mock configurations that are common across multiple test classes should be centralized in test configuration classes.

Applied to files:

  • gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: maven-build
  • GitHub Check: maven-build
  • GitHub Check: sakai-deploy
🔇 Additional comments (1)
gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java (1)

31-45: New wiring for configuration, permissions, and section awareness looks consistent

Injecting ServerConfigurationService, GradingPermissionService, and SectionAwareness (plus the related model types) into this test class matches their later usage in the letter-grading and course-grade/permissions flows, so the additional imports and @Autowired fields look correct from a test wiring perspective.

Also applies to: 55-57, 83-89

@adrianfish adrianfish marked this pull request as draft December 11, 2025 15:51
@adrianfish
Copy link
Contributor Author

Simple cypress fix after mergin

@adrianfish adrianfish marked this pull request as ready for review December 11, 2025 16:30
@adrianfish adrianfish requested a review from ern December 12, 2025 10:40
@adrianfish
Copy link
Contributor Author

Screenshot 2025-12-12 at 10 51 47 Screenshot 2025-12-12 at 10 51 27 Screenshot 2025-12-12 at 10 50 49 Screenshot 2025-12-12 at 10 49 49

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant