Skip to content

Conversation

@krusche
Copy link
Member

@krusche krusche commented Jan 26, 2026

Summary

This PR improves consistency across the codebase for handling scores and participations data. It introduces a reusable helper function for extracting competencies from exercises, modernizes UI components to use Angular signals, and adds comprehensive test coverage.

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines and the REST API guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding guidelines.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I translated all newly inserted strings into English and German.

Motivation and Context

The existing code for handling scores and participations across different exercise types lacked consistency. Competency extraction logic was duplicated across components, and some UI components used older patterns instead of modern Angular signals. This made the codebase harder to maintain and extend.

Description

Server Changes:

  • Updated ParticipationRetrievalResource to use consistent authorization checks that align with existing patterns in the codebase

Client Changes:

  • Added getExerciseCompetencies() helper function in exercise.model.ts for consistent extraction of competencies from exercise competency links
  • Updated all exercise detail components (programming, quiz, text, modeling, file-upload) to use the new helper function
  • Refactored NonProgrammingExerciseDetailCommonActionsComponent to use Angular signals (input(), input.required(), computed())
  • Added canAccessParticipationsAndScores computed signal for consistent access control logic
  • Updated programming exercise detail component to preserve exercise properties when loading participations
  • Added missing translation for inconsistenciesFoundAlert in both English and German

Test Coverage:

  • Added comprehensive tests for getExerciseCompetencies() helper function
  • Added competency display tests for all exercise detail components
  • Updated existing tests to align with the refactored code

Steps for Testing

Prerequisites:

  • 1 Instructor
  • 1 Course with exercises of different types (programming, quiz, text, modeling, file-upload)
  • Exercises should have competencies linked
  1. Log in to Artemis as an instructor
  2. Navigate to a course and open the exercise details page for each exercise type
  3. Verify that competencies are displayed correctly in the problem section (except quizzes)
  4. If you add new competencies in the edit exercise screen, please notice that for programming exercises you need to switch to the advanced mode
  5. Navigate to the programming exercise detail page
  6. Verify that the "Scores" and "Participations" buttons are visible and functional
  7. Click on "Scores" and verify you can see the score overview
  8. Click on "Participations" and verify you can see the participations list

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Client

Class/File Line Coverage Lines Expects Ratio
non-programming-exercise-detail-common-actions.component.ts 95.12% 130 14 10.8
exercise.model.ts 77.52% 231 10 4.3
utils.ts 0.00% 136 ? ?
programming-exercise-detail.component.ts 89.29% 778 41 5.3
quiz-exercise-detail.component.ts 100.00% 115 14 12.2

Server

Class/File Line Coverage Lines
ParticipationRetrievalResource.java 92.45% 134

Last updated: 2026-01-26 20:16:08 UTC

Screenshots

This PR contains internal code refactoring (migrating to Angular signals, extracting helper functions) without visual design changes. The client changes affect internal access control logic and code organization, not the visual appearance of the UI.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added display of competency links in exercise detail views across different exercise types.
  • Bug Fixes

    • Restricted access to exercise participations and scores data for exam exercises to instructors only (previously allowed teaching assistants).
  • Tests

    • Expanded test coverage for exam exercise access permissions and competency link display functionality.

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

@krusche krusche requested review from a team as code owners January 26, 2026 19:54
@github-project-automation github-project-automation bot moved this to Work In Progress in Artemis Development Jan 26, 2026
@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) exercise Pull requests that affect the corresponding module fileupload Pull requests that affect the corresponding module modeling Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module quiz Pull requests that affect the corresponding module text Pull requests that affect the corresponding module labels Jan 26, 2026
@krusche krusche added this to the 8.8.0 milestone Jan 26, 2026
@krusche krusche changed the title Development: Make scores and participations code more consistent Development: Make scores and participations code more consistent Jan 26, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Walkthrough

Restricts exam-exercise participation retrieval to instructors (TAs allowed only for course exercises), migrates a non-programming exercise detail component to Angular signals with a computed access predicate, adds a shared getExerciseCompetencies utility, updates multiple detail components/tests to use it, and adds related i18n keys and integration tests.

Changes

Cohort / File(s) Summary
Participation access control
src/main/java/.../ParticipationRetrievalResource.java
Adjusted getAllParticipationsForExercise permission check: course exercises require TEACHING_ASSISTANT, exam exercises require INSTRUCTOR.
Non-programming exercise → Angular signals
src/main/webapp/app/exercise/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts, .html, .spec.ts
Converted @Input()s (exercise, course, isExamExercise) to signal-based inputs, added canAccessParticipationsAndScores computed signal, updated template checks and tests to use fixture.componentRef.setInput.
Shared competency extraction
src/main/webapp/app/exercise/shared/entities/exercise/exercise.model.ts, .spec.ts
Added exported getExerciseCompetencies(exercise) to map/filter competencyLinks and tests covering edge cases.
Competency rendering & tests across details
src/main/webapp/app/exercise/util/utils.ts, src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts, .html, .spec.ts, src/main/webapp/app/quiz/manage/detail/quiz-exercise-detail.component.ts, .spec.ts, src/main/webapp/app/text/manage/detail/text-exercise-detail.component.spec.ts, src/main/webapp/app/fileupload/manage/exercise-details/file-upload-exercise-detail.component.spec.ts, src/main/webapp/app/modeling/manage/detail/modeling-exercise-detail.component.spec.ts
Replaced direct competencyLinks usage with getExerciseCompetencies(...), conditionally render competency detail when non-empty, and added/updated unit tests verifying competency link text aggregation and absence cases.
Programming exercise detail refactor
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts, .spec.ts
Switched route handling to activatedRoute.snapshot.data with a new handleRouteData() initializer, added canAccessParticipationsAndScores flag, moved sharing-status subscription, removed activatedRoute subscription cleanup.
i18n
src/main/webapp/i18n/en/consistencyCheck.json, src/main/webapp/i18n/de/consistencyCheck.json
Added inconsistenciesFoundAlert translation key in English and German files.
Integration tests
src/test/java/.../ParticipationIntegrationTest.java
Added tests: tutor forbidden (403) and instructor success (200) when fetching participations for exam exercises.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement, refactoring

Suggested reviewers

  • tobias-lippert
  • ole-ve
  • dfuchss
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title "Development: Make scores and participations code more consistent" accurately summarizes the main objective of standardizing and consolidating scores and participations handling across the codebase.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/consistent-scores-participations-code

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: 3

Caution

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

⚠️ Outside diff range comments (2)
src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationRetrievalResource.java (1)

110-114: Duplicate submission count computation causes unnecessary DB query.

Lines 110-111 compute and set the submission count inside the else block, and lines 113-114 do the exact same computation unconditionally after the if-else block. When withLatestResults is false, this results in two identical database queries and redundant iteration over participations.

🔧 Proposed fix
     else {
         if (exercise.isTeamMode()) {
             participations = studentParticipationRepository.findWithTeamInformationByExerciseId(exerciseId);
         }
         else {
             participations = studentParticipationRepository.findByExerciseId(exerciseId);
         }
-
-        Map<Long, Integer> submissionCountMap = studentParticipationRepository.countSubmissionsPerParticipationByExerciseIdAsMap(exerciseId);
-        participations.forEach(participation -> participation.setSubmissionCount(submissionCountMap.get(participation.getId())));
     }
     Map<Long, Integer> submissionCountMap = studentParticipationRepository.countSubmissionsPerParticipationByExerciseIdAsMap(exerciseId);
     participations.forEach(participation -> participation.setSubmissionCount(submissionCountMap.get(participation.getId())));
src/main/webapp/app/exercise/util/utils.ts (1)

65-81: Avoid rendering missing competency titles.
CourseCompetency.title is optional; joining directly can show "undefined" or stray separators. Consider filtering titles first.

🐛 Proposed fix
-    if (hasCompetencies) {
-        details.push({
-            title: 'artemisApp.competency.link.title',
-            type: DetailType.Text,
-            data: { text: competencies.map((competency) => competency.title).join(', ') },
-        });
-    }
+    if (hasCompetencies) {
+        const competencyTitles = competencies
+            .map((competency) => competency.title)
+            .filter((title): title is string => title !== undefined && title !== '');
+        details.push({
+            title: 'artemisApp.competency.link.title',
+            type: DetailType.Text,
+            data: { text: competencyTitles.join(', ') },
+        });
+    }
🤖 Fix all issues with AI agents
In
`@src/main/webapp/app/exercise/shared/entities/exercise/exercise.model.spec.ts`:
- Around line 1-3: The Vitest config is missing the exercise test pattern so
tests under src/main/webapp/app/exercise/... aren't run; update the include
array in vitest.config.ts to add the pattern
'src/main/webapp/app/exercise/**/*.spec.ts' so files like
src/main/webapp/app/exercise/shared/entities/exercise/exercise.model.spec.ts
(testing Exercise and getExerciseCompetencies) are picked up by the test runner.

In `@src/main/webapp/app/exercise/shared/entities/exercise/exercise.model.ts`:
- Around line 304-305: The getExerciseCompetencies function currently only
filters out undefined values and can let null competencies through; update the
filter predicate in getExerciseCompetencies to exclude both undefined and null
(e.g., use competency != null or explicit checks competency !== undefined &&
competency !== null) while keeping the type guard signature (competency is
CourseCompetency) so the returned array is correctly typed as
CourseCompetency[].

In
`@src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts`:
- Around line 256-258: The teamBaseResource for exam exercises mistakenly uses
this.programmingExercise.exerciseGroup?.exam?.id as the final path segment;
update the URL construction in programming-exercise-detail.component.ts (the
teamBaseResource assignment) to use the exercise ID instead (the same exerciseId
used in the non-exam path, e.g., this.programmingExercise.id or the existing
exerciseId variable) so the last segment is the exercise's ID rather than the
exam ID.
🧹 Nitpick comments (7)
src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationRetrievalResource.java (1)

90-97: Consider adding a defensive else clause.

The if-else-if structure lacks a final else clause. While exercises should always be either course or exam exercises, adding an else clause that throws an exception would provide a safety net against unexpected states.

🛡️ Suggested defensive guard
 if (exercise.isCourseExercise()) {
     // teaching assistants can access scores and participations in course exercises
     authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.TEACHING_ASSISTANT, exercise, null);
 }
 else if (exercise.isExamExercise()) {
     // only instructors can access scores and participations in exam exercises
     authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, exercise, null);
 }
+else {
+    throw new IllegalStateException("Exercise must be either a course exercise or an exam exercise");
+}
src/main/webapp/app/modeling/manage/detail/modeling-exercise-detail.component.spec.ts (1)

122-125: Avoid object spread per app TS guidelines.
Use Object.assign (or explicit assignment) instead of { ...obj } in app code. As per coding guidelines, consider:

♻️ Proposed refactor
-            const exerciseWithCompetencies = {
-                ...modelingExercise,
-                competencyLinks: [{ competency: competency1 } as CompetencyExerciseLink, { competency: competency2 } as CompetencyExerciseLink],
-            } as ModelingExercise;
+            const exerciseWithCompetencies = Object.assign({}, modelingExercise, {
+                competencyLinks: [{ competency: competency1 } as CompetencyExerciseLink, { competency: competency2 } as CompetencyExerciseLink],
+            }) as ModelingExercise;
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts (1)

168-177: Consider using Angular signals for component state.

Per coding guidelines, Angular components should use signals for component state. The canAccessParticipationsAndScores property could be a computed signal that automatically updates when its dependencies change.

♻️ Suggested refactor using signals
// Convert to signal-based state
private programmingExerciseSignal = signal<ProgrammingExercise | undefined>(undefined);
private isExamExerciseSignal = signal(false);

canAccessParticipationsAndScores = computed(() => {
    const exercise = this.programmingExerciseSignal();
    const isExam = this.isExamExerciseSignal();
    return (exercise?.isAtLeastTutor && !isExam) || !!exercise?.isAtLeastInstructor;
});
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.spec.ts (1)

327-387: Test design could be improved to exercise actual component behavior.

The computeCanAccessParticipationsAndScores helper manually replicates the logic from handleRouteData. If the component logic changes, these tests would still pass despite being out of sync. Consider testing through ngOnInit with proper route data setup, similar to the course/exam exercise tests.

♻️ Suggested approach
describe('canAccessParticipationsAndScores', () => {
    it('should return true for course exercise when user is at least tutor', () => {
        const programmingExercise = new ProgrammingExercise(new Course(), undefined);
        programmingExercise.id = 1;
        programmingExercise.isAtLeastTutor = true;
        programmingExercise.isAtLeastInstructor = false;

        const route = TestBed.inject(ActivatedRoute);
        route.snapshot.data = { programmingExercise };

        comp.ngOnInit();

        expect(comp.canAccessParticipationsAndScores).toBeTrue();
    });
    // ... similar for other test cases
});
src/main/webapp/app/exercise/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.html (2)

11-11: Consider removing redundant course() check.

Since course is defined as input.required<Course>() in the component, it will always have a value. The course() && guard is unnecessary—Angular would throw an error if a required input is not provided.

You could simplify to just canAccessParticipationsAndScores():

♻️ Suggested simplification
-        `@if` (course() && canAccessParticipationsAndScores()) {
+        `@if` (canAccessParticipationsAndScores()) {

Apply the same change at line 31.

Also applies to: 31-31


43-43: Minor inconsistency: exercise.course vs course() signal.

This condition uses exercise.course (checking the entity property) while lines 11 and 31 use course() (the input signal). The exercise.course check combined with !isExamExercise() appears redundant—if it's not an exam exercise, it must be a course exercise.

If the intent is to verify the exercise has its course property populated, the current approach is acceptable. Otherwise, consider aligning with the course() signal usage for consistency.

src/main/webapp/app/exercise/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts (1)

76-89: Consider adding defensive checks for undefined IDs.

The code uses non-null assertions (!) on course.id and relies on optional chaining for exercise.exerciseGroup?.exam?.id and exercise.exerciseGroup?.id. If any of these are undefined, the constructed URLs will contain undefined string segments, leading to broken routes.

While these IDs are expected to exist in practice, consider adding a guard or logging to catch misconfigured data early:

♻️ Optional defensive check
ngOnInit(): void {
    const exercise = this.exercise();
    const course = this.course();
    
    if (!course.id || !exercise.id) {
        // Log warning or handle gracefully - this shouldn't happen in normal flow
        return;
    }
    
    if (!this.isExamExercise()) {
        // ... existing code
    } else {
        if (!exercise.exerciseGroup?.exam?.id || !exercise.exerciseGroup?.id) {
            return;
        }
        // ... existing code
    }
    // ...
}

Based on learnings, internal safety checks for impossible runtime states (like missing route configuration) can be implemented without user-facing error notifications, as these are development-time safeguards.

@github-project-automation github-project-automation bot moved this from Work In Progress to Ready For Review in Artemis Development Jan 26, 2026
@github-actions
Copy link

@krusche Test coverage has been automatically updated in the PR description.

- Filter out null competencies in getExerciseCompetencies (defensive)
- Fix pre-existing teamBaseResource bug for exam exercises

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@github-actions
Copy link

@krusche Test coverage has been automatically updated in the PR description.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ✅SkippedFailedTime ⏱
End-to-End (E2E) Test Report1 ran1 passed0 skipped0 failed2s 319ms
TestResultTime ⏱
No test annotations available

@helios-aet helios-aet bot temporarily deployed to artemis-test4.artemis.cit.tum.de January 26, 2026 20:30 Inactive
Copy link
Contributor

@SamuelRoettgermann SamuelRoettgermann left a comment

Choose a reason for hiding this comment

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

Tested on TS4

  1. It's not possible to link competencies to programming exercises (I'm not sure if it was ever possible to link competencies to programming exercises, though)
  2. Regarding testing step 3: While the linked competencies are shown, the associated priority (low / medium / high) is not
  3. Regarding testing step 3: For quiz exercises the linked competencies are in the Mode section, not the (non-existent) Problem section

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report223 ran220 passed1 skipped2 failed1h 33m 32s 18ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/course/CourseMessages.spec.ts
ts.Course messages › Channel messages › Write/edit/delete message in channel › Student should be able to write message in channel❌ failure2m 18s 328ms
e2e/exam/test-exam/TestExamStudentExams.spec.ts
ts.Test Exam - student exams › Check exam participants and their submissions › Open the list of exam students❌ failure7m 1s 459ms

@helios-aet helios-aet bot temporarily deployed to artemis-test1.artemis.cit.tum.de January 27, 2026 21:22 Inactive
Copy link

@maxgutke maxgutke left a comment

Choose a reason for hiding this comment

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

I tested this PR on TS 1. Everything worked as expected: For every exercise type the competency is shown and the "Scores" and "Pacrticipations" buttons worked just fine

Image

@krusche
Copy link
Member Author

krusche commented Jan 27, 2026

Tested on TS4

  1. It's not possible to link competencies to programming exercises (I'm not sure if it was ever possible to link competencies to programming exercises, though)
  2. Regarding testing step 3: While the linked competencies are shown, the associated priority (low / medium / high) is not
  3. Regarding testing step 3: For quiz exercises the linked competencies are in the Mode section, not the (non-existent) Problem section

The 2. and 3. point was like that before and is out of scope. For your 1. point, I am confused, I tested this and it was possible, however you have to switch to the advance mode. Could you try again and let me know if it does not work? In that case, please provide a screenshot

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

Labels

client Pull requests that update TypeScript code. (Added Automatically!) exercise Pull requests that affect the corresponding module fileupload Pull requests that affect the corresponding module modeling Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module quiz Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) tests text Pull requests that affect the corresponding module

Projects

Status: Ready For Review

Development

Successfully merging this pull request may close these issues.

4 participants