diff --git a/.ci/translation-file-checker/translation_file_checker.py b/.ci/translation-file-checker/translation_file_checker.py new file mode 100644 index 000000000000..48b25849a1ac --- /dev/null +++ b/.ci/translation-file-checker/translation_file_checker.py @@ -0,0 +1,245 @@ +#! /usr/bin/env python3 + +import argparse +import json +import sys +from collections import Counter +from dataclasses import dataclass +from functools import reduce +from pathlib import Path +from typing import Any, Self + + +@dataclass +class Config: + are_files: bool + german_translations: Path + english_translations: Path + + +@dataclass +class Diff: + all_german_keys: list[str] + only_german: set[str] + all_english_keys: list[str] + only_english: set[str] + + def duplicates(self) -> tuple[list[str], list[str]]: + def _duplicates(items: list[str]) -> list[str]: + return [k for k, count in Counter(items).items() if count > 1] + + return _duplicates(self.all_german_keys), _duplicates(self.all_english_keys) + + def __add__(self, other: Self) -> Self: + return Diff( + all_german_keys=self.all_german_keys + other.all_german_keys, + only_german=self.only_german.union(other.only_german), + all_english_keys=self.all_english_keys + other.all_english_keys, + only_english=self.only_english.union(other.only_english), + ) + + def __str__(self) -> str: + german = list(self.only_german) + german.sort() + only_german = "\n".join(german) + + english = list(self.only_english) + english.sort() + only_english = "\n".join(english) + + return ( + f"Missing English translation keys:\n{only_german}\n\n" + f"Missing German translation keys:\n{only_english}" + ) + + def __len__(self) -> int: + return len(self.only_english) + len(self.only_german) + + +def main(argv: list[str] | None = None) -> int: + if argv is None: + argv = sys.argv[1:] + + config = _config_from_args(argv) + + if config.are_files: + diff = _compare_files(config.german_translations, config.english_translations) + else: + diff = _compare_directories( + config.german_translations, config.english_translations + ) + + if len(diff) > 0: + print(f"\n{diff}") + + duplicates_german, duplicates_english = diff.duplicates() + duplicates_exist = len(duplicates_german) > 0 or len(duplicates_english) > 0 + + if duplicates_exist: + _log_duplicate_keys(duplicates_german, duplicates_english) + + if len(diff) == 0 and not duplicates_exist: + return 0 + else: + return 1 + + +def _config_from_args(argv: list[str]) -> Config: + arg_parser = _build_arg_parser() + args = arg_parser.parse_args(argv) + + german_translations: Path = args.german_files + english_translations: Path = args.english_files + + if german_translations.is_file() and english_translations.is_file(): + are_files = True + elif german_translations.is_dir() and english_translations.is_dir(): + are_files = False + else: + err = "The paths to the translations must either both be files or both be directories." + raise ValueError(err) + + return Config( + are_files=are_files, + german_translations=german_translations, + english_translations=english_translations, + ) + + +def _build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser() + + parser.add_argument( + "--german-files", + type=Path, + required=True, + help="Directory containing the German translation files.", + ) + parser.add_argument( + "--english-files", + type=Path, + required=True, + help="Directory containing the English translation files.", + ) + + return parser + + +def _compare_directories(german_dir: Path, english_dir: Path) -> Diff: + def json_files(directory: Path) -> set[Path]: + return { + file + for file in directory.iterdir() + if file.is_file() and file.suffix == ".json" + } + + german_files = json_files(german_dir) + english_files = json_files(english_dir) + + # We assume here that the German and English translation keys are defined in + # identically named files. If they are not, missing files will be reported and + # the reduction of Diffs below will report missing keys. + # This way we continue to enforce the same structure in both German and English + # translations, which eases maintenance. + file_pairs = _find_file_pairs(german_files, english_files) + + return reduce( + lambda d1, d2: d1 + d2, + (_compare_files(german, english) for german, english in file_pairs), + ) + + +def _find_file_pairs( + german_files: set[Path], english_files: set[Path] +) -> list[tuple[Path, Path]]: + pairs: dict[str, tuple[Path | None, Path | None]] = {} + + for german_file in german_files: + pairs[german_file.name] = (german_file, None) + + for english_file in english_files: + pair = pairs.get(english_file.name, (None, None)) + pairs[english_file.name] = (pair[0], english_file) + + for german, english in pairs.values(): + if german is None: + print("Missing German translation file: " + english.name, file=sys.stderr) + elif english is None: + print("Missing English translation file: " + german.name, file=sys.stderr) + + return list(v for v in pairs.values() if v[0] is not None and v[1] is not None) + + +def _compare_files(german: Path, english: Path) -> Diff: + german_keys = _flat_json_keys(german) + english_keys = _flat_json_keys(english) + + return Diff( + all_german_keys=list(german_keys), + only_german=german_keys - english_keys, + all_english_keys=list(english_keys), + only_english=english_keys - german_keys, + ) + + +def _flat_json_keys(json_file: Path) -> set[str]: + """ + Flattens a JSON file into a flat set of keys. + + E.g. + + .. code-block:: json + + { + "a": { + "v": 10, + "b": { + "c": null, + "d": 1.0 + } + } + } + + will result in + + .. code-block:: + + "a.v", "a.b.c", "a.b.d" + """ + with json_file.open(mode="r") as f: + data = json.load(f) + + result = set() + + def flatten(prefix: str, item: dict[str, Any] | list[Any] | str | float) -> None: + if isinstance(item, dict): + for k, v in item.items(): + flatten(f"{prefix}.{k}", v) + elif isinstance(item, list): + for idx, _ in enumerate(item): + result.add(f"{prefix}.[{idx}]") + else: + result.add(prefix) + + flatten("", data) + + return result + + +def _log_duplicate_keys( + duplicates_german: list[str], duplicates_english: list[str] +) -> None: + if len(duplicates_german) > 0 or len(duplicates_english) > 0: + print("\nThere are duplicated translation keys!") + + if len(duplicates_german) > 0: + duplicates_german.sort() + print("German:\n" + "\n".join(duplicates_german)) + + if len(duplicates_english) > 0: + duplicates_english.sort() + print("English:\n" + "\n".join(duplicates_english)) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 81aae4fe9457..18aa94ff1059 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ version: 2 updates: - + # Check for version updates for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: @@ -9,8 +9,74 @@ updates: time: "08:30" reviewers: - "bassner" - - "Mtze" - open-pull-requests-limit: 2 + - "krusche" + open-pull-requests-limit: 5 + labels: + - "ready for review" + - "dependencies" + + # Check for version updates for npm dependencies + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + reviewers: + - "bassner" + - "krusche" + # Enable security updates + open-pull-requests-limit: 5 + labels: + - "ready for review" + - "dependencies" + + # Check for Gradle updates + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + reviewers: + - "bassner" + - "krusche" + open-pull-requests-limit: 5 + labels: + - "ready for review" + - "dependencies" + + # Check for version updates for Python dependencies (docs) + - package-ecosystem: "pip" + directory: "/docs" + schedule: + interval: "weekly" + reviewers: + - "bassner" + - "krusche" + open-pull-requests-limit: 5 + labels: + - "ready for review" + - "dependencies" + + # Check for version updates for Python dependencies (coverage) + - package-ecosystem: "pip" + directory: "/supporting_scripts/generate_code_cov_table" + schedule: + interval: "weekly" + reviewers: + - "bassner" + - "krusche" + open-pull-requests-limit: 5 + labels: + - "ready for review" + - "dependencies" + + # Check for version updates for Python dependencies (course setup) + - package-ecosystem: "pip" + directory: "/supporting_scripts/course-scripts/quick-course-setup" + schedule: + interval: "weekly" + reviewers: + - "bassner" + - "krusche" + open-pull-requests-limit: 5 labels: - "ready for review" - "dependencies" diff --git a/.github/labeler.yml b/.github/labeler.yml index d9295deb1372..330170653e37 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -33,3 +33,94 @@ config-change: template: - changed-files: - any-glob-to-any-file: src/main/resources/templates/**/* + +# module labels +assessment: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/assessment/**/* + +athena: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/athena/**/* + +atlas: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/atlas/**/* + +buildagent: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/buildagent/**/* + +communication: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/communication/**/* + +core: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/core/**/* + +exam: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/exam/**/* + +exercise: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/exercise/**/* + +fileupload: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/fileupload/**/* + +iris: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/iris/**/* + +lecture: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/lecture/**/* + +lti: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/lti/**/* + +modeling: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/modeling/**/* + +plagiarism: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/plagiarism/**/* + +programming: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/programming/**/* + +quiz: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/quiz/**/* + +text: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/text/**/* + +tutorialgroup: + - changed-files: + - any-glob-to-any-file: + - src/(main|test)/java/de/tum/cit/aet/artemis/tutorialgroup/**/* diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 214a4a08e343..da1a93e4404a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,6 +56,8 @@ jobs: distribution: 'temurin' java-version: '${{ env.java }}' cache: 'gradle' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Production Build run: ./gradlew -Pprod -Pwar clean bootWar - name: Upload Artifact diff --git a/.github/workflows/check-translation-keys.yml b/.github/workflows/check-translation-keys.yml new file mode 100644 index 000000000000..a14ed3f43b9a --- /dev/null +++ b/.github/workflows/check-translation-keys.yml @@ -0,0 +1,24 @@ +name: Check if German and English translations are consistent + +on: + pull_request: + paths: + - 'src/main/webapp/i18n/**' + push: + paths: + - 'src/main/webapp/i18n/**' + +jobs: + build: + name: Check if translation keys are consistent + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Check if translation keys match + run: > + python .ci/translation-file-checker/translation_file_checker.py + --german-files src/main/webapp/i18n/de/ + --english-files src/main/webapp/i18n/en/ diff --git a/.github/workflows/pullrequest-labeler.yml b/.github/workflows/pullrequest-labeler.yml index 90c20cebd161..15b9a147a99a 100644 --- a/.github/workflows/pullrequest-labeler.yml +++ b/.github/workflows/pullrequest-labeler.yml @@ -8,3 +8,4 @@ jobs: - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" + sync-labels: true diff --git a/.whitesource b/.whitesource index 9c7ae90b4ec3..e05f3ec9dce7 100644 --- a/.whitesource +++ b/.whitesource @@ -1,6 +1,8 @@ { "scanSettings": { - "baseBranches": [] + "enableScan": true, + "baseBranches": ["develop"], + "scanDependabotPR": false }, "checkRunSettings": { "vulnerableCheckRunConclusionLevel": "failure", @@ -8,7 +10,14 @@ "useMendCheckNames": true }, "issueSettings": { - "minSeverityLevel": "LOW", + "minSeverityLevel": "MEDIUM", "issueType": "DEPENDENCY" + }, + "remediateSettings": { + "workflowRules": { + "enabled": true, + "minVulnerabilityScore": 1.5, + "maxVulnerabilityScore": 10 + } } -} \ No newline at end of file +} diff --git a/docs/dev/setup/jenkins-gitlab.rst b/docs/dev/setup/jenkins-gitlab.rst index 59fe3b0bde26..3adbbabac478 100644 --- a/docs/dev/setup/jenkins-gitlab.rst +++ b/docs/dev/setup/jenkins-gitlab.rst @@ -485,48 +485,48 @@ do either do it manually or using the following command: 2. You can now first build and deploy Jenkins, then you can also start the other services which weren't started yet: - .. code:: bash + .. code:: bash - JAVA_OPTS=-Djenkins.install.runSetupWizard=false docker compose -f docker/.yml up --build -d jenkins - docker compose -f docker/.yml up -d + JAVA_OPTS=-Djenkins.install.runSetupWizard=false docker compose -f docker/.yml up --build -d jenkins + docker compose -f docker/.yml up -d Jenkins is then reachable under ``http://localhost:8082/`` and you can login using the credentials specified in ``jenkins-casc-config-gitlab.yml`` (defaults to ``artemis_admin`` as both username and password). 3. The `application-local.yml` must be adapted with the values configured in ``jenkins-casc-config-gitlab.yml``: -.. code:: yaml - - artemis: - user-management: - use-external: false - internal-admin: - username: artemis_admin - password: artemis_admin - version-control: - url: http://localhost:8081 - user: artemis_admin - password: artemis_admin - continuous-integration: - user: artemis_admin - password: artemis_admin - url: http://localhost:8082 - vcs-credentials: artemis_gitlab_admin_credentials - artemis-authentication-token-key: artemis_notification_plugin_token - artemis-authentication-token-value: artemis_admin + .. code:: yaml -5. Open the ``src/main/resources/config/application-jenkins.yml`` and change the following: + artemis: + user-management: + use-external: false + internal-admin: + username: artemis_admin + password: artemis_admin + version-control: + url: http://localhost:8081 + user: artemis_admin + password: artemis_admin + continuous-integration: + user: artemis_admin + password: artemis_admin + url: http://localhost:8082 + vcs-credentials: artemis_gitlab_admin_credentials + artemis-authentication-token-key: artemis_notification_plugin_token + artemis-authentication-token-value: artemis_admin + +4. Open the ``src/main/resources/config/application-jenkins.yml`` and change the following: Again, if you are using a development setup, the template in the beginning of this page already contains the correct values. -.. code:: yaml + .. code:: yaml - jenkins: - internal-urls: - ci-url: http://jenkins:8080 - vcs-url: http://gitlab:80 + jenkins: + internal-urls: + ci-url: http://jenkins:8080 + vcs-url: http://gitlab:80 -6. You're done. You can now run Artemis with the GitLab/Jenkins environment. +5. You're done. You can now run Artemis with the GitLab/Jenkins environment. Manual Jenkins Server Setup """"""""""""""""""""""""""" @@ -691,6 +691,18 @@ Start Jenkins user: your.chosen.username password: your.chosen.password +11. In a local setup, you have to disable CSRF otherwise some API endpoints will return HTTP Status 403 Forbidden. + This is done be executing the following command: + ``docker compose -f docker/.yml exec -T jenkins dd of=/var/jenkins_home/init.groovy < docker/jenkins/jenkins-disable-csrf.groovy`` + + The last step is to disable the ``use-crumb`` option in ``application-local.yml``: + + .. code:: yaml + + jenkins: + use-crumb: false + + Required Jenkins Plugins """""""""""""""""""""""" @@ -858,6 +870,7 @@ GitLab Repository Access continuous-integration: vcs-credentials: the.id.of.the.username.and.password.credentials.from.jenkins + Upgrading Jenkins """"""""""""""""" diff --git a/docs/dev/setup/server.rst b/docs/dev/setup/server.rst index a702b0316161..b37b82c555ab 100644 --- a/docs/dev/setup/server.rst +++ b/docs/dev/setup/server.rst @@ -49,15 +49,6 @@ You can override the following configuration options in this file. user: # e.g. ga12abc token: # Enter a valid token generated by the CI system or leave this empty to use the fallback authentication user + password password: - # Some CI systems, like Jenkins, offer a specific token that gets checked against any incoming notifications - # from a VCS trying to trigger a build plan. Only if the notification request contains the correct token, the plan - # is triggered. This can be seen as an alternative to sending an authenticated request to a REST API and then - # triggering the plan. - # In the case of Artemis, this is only really needed for the Jenkins + GitLab setup, since the GitLab plugin in - # Jenkins only allows triggering the Jenkins jobs using such a token. Furthermore, in this case, the value of the - # hudson.util.Secret is stored in the build plan, so you also have to specify this encrypted string here and NOT the actual token value itself! - # You can get this by GETting any job.xml for a job with an activated GitLab step and your token value of choice. - secret-push-token: # Key of the saved credentials for the VCS service # Jenkins: You have to specify the key from the credentials page in Jenkins under which the user and # password for the VCS are stored diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePoints.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePointsDTO.java similarity index 55% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePoints.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePointsDTO.java index 7bf75dd61332..8bd95184f5a3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePoints.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/MaxAndReachablePointsDTO.java @@ -3,6 +3,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record MaxAndReachablePoints(double maxPoints, double reachablePoints, double reachablePresentationPoints) { +public record MaxAndReachablePointsDTO(double maxPoints, double reachablePoints, double reachablePresentationPoints) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistribution.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistribution.java deleted file mode 100644 index b5baf193d413..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistribution.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.tum.cit.aet.artemis.assessment.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ScoreDistribution { - - private final long amount; - - private final double score; - - public ScoreDistribution(Long amount, Double score) { - this.amount = amount; - this.score = score != null ? score : 0; - } - - public long getAmount() { - return amount; - } - - public double getScore() { - return score; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistributionDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistributionDTO.java new file mode 100644 index 000000000000..a1b41604d3b6 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/ScoreDistributionDTO.java @@ -0,0 +1,11 @@ +package de.tum.cit.aet.artemis.assessment.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record ScoreDistributionDTO(long amount, double score) { + + public ScoreDistributionDTO(Long amount, Double score) { + this(amount != null ? amount : 0, score != null ? score : 0.0); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffort.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffort.java deleted file mode 100644 index e997a0c0eee3..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffort.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.tum.cit.aet.artemis.assessment.dto; - -/** - * A data entry used by the tutor effort statistics page. It represents the respective information in terms of - * number of submissions assessed as well as time spent for each tutor in a particular exercise. - */ -// TODO convert to record -public class TutorEffort { - - private Long userId; - - private int numberOfSubmissionsAssessed; - - private double totalTimeSpentMinutes; - - private Long exerciseId; - - private Long courseId; - - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - public int getNumberOfSubmissionsAssessed() { - return numberOfSubmissionsAssessed; - } - - public void setNumberOfSubmissionsAssessed(int numberOfSubmissionsAssessed) { - this.numberOfSubmissionsAssessed = numberOfSubmissionsAssessed; - } - - public double getTotalTimeSpentMinutes() { - return totalTimeSpentMinutes; - } - - public void setTotalTimeSpentMinutes(double totalTimeSpentMinutes) { - this.totalTimeSpentMinutes = totalTimeSpentMinutes; - } - - public Long getExerciseId() { - return exerciseId; - } - - public void setExerciseId(Long exerciseId) { - this.exerciseId = exerciseId; - } - - public Long getCourseId() { - return courseId; - } - - public void setCourseId(Long courseId) { - this.courseId = courseId; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffortDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffortDTO.java new file mode 100644 index 000000000000..eba5f87d2807 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/TutorEffortDTO.java @@ -0,0 +1,11 @@ +package de.tum.cit.aet.artemis.assessment.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * A data entry used by the tutor effort statistics page. It represents the respective information in terms of + * number of submissions assessed as well as time spent for each tutor in a particular exercise. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record TutorEffortDTO(Long userId, int numberOfSubmissionsAssessed, double totalTimeSpentMinutes, Long exerciseId, Long courseId) { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntry.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntry.java deleted file mode 100644 index ab754a76440b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntry.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.tum.cit.aet.artemis.assessment.dto.dashboard; - -import com.fasterxml.jackson.annotation.JsonInclude; - -// Custom object for sql query -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record ExerciseMapEntry(long exerciseId, long value) { -} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntryDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntryDTO.java new file mode 100644 index 000000000000..87d246196595 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseMapEntryDTO.java @@ -0,0 +1,12 @@ +package de.tum.cit.aet.artemis.assessment.dto.dashboard; + +import com.fasterxml.jackson.annotation.JsonInclude; + +// Custom object for sql query +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record ExerciseMapEntryDTO(long exerciseId, long value) { + + public ExerciseMapEntryDTO(Long exerciseId, Long value) { + this(exerciseId != null ? exerciseId : 0, value != null ? value : 0); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCount.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCountDTO.java similarity index 75% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCount.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCountDTO.java index efa3cecea370..a05067d3c2ec 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCount.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ExerciseRatingCountDTO.java @@ -6,5 +6,6 @@ * Class used to hold tutor average rating and number of tutor ratings in an exercise */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record ExerciseRatingCount(Double averageRating, Long numberOfRatings) { +public record ExerciseRatingCountDTO(Double averageRating, Long numberOfRatings) { + } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCount.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCount.java deleted file mode 100644 index 01f89a594904..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCount.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.tum.cit.aet.artemis.assessment.dto.dashboard; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -// we have to use upper case Boolean here, because in Result.rated can also take the value null -public record ResultCount(Boolean rated, long count) { -} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCountDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCountDTO.java new file mode 100644 index 000000000000..ae53624de905 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/dashboard/ResultCountDTO.java @@ -0,0 +1,11 @@ +package de.tum.cit.aet.artemis.assessment.dto.dashboard; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record ResultCountDTO(boolean rated, long count) { + + public ResultCountDTO(Boolean rated, Long count) { + this(rated != null ? rated : false, count != null ? count : 0); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSum.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSumDTO.java similarity index 59% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSum.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSumDTO.java index dad7ccd6ce8c..a6f599d65fd1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSum.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/StudentScoreSumDTO.java @@ -9,5 +9,9 @@ * @param sumPointsAchieved the sum of points achieved by the student */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record StudentScoreSum(long userId, double sumPointsAchieved) { +public record StudentScoreSumDTO(long userId, double sumPointsAchieved) { + + public StudentScoreSumDTO(Long userId, Double sumPointsAchieved) { + this(userId != null ? userId : 0, sumPointsAchieved != null ? sumPointsAchieved : 0.0); + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSum.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSumDTO.java similarity index 82% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSum.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSumDTO.java index 05e3e4baddfb..5df9c5ac761f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSum.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/score/TeamScoreSumDTO.java @@ -9,5 +9,5 @@ * @param sumPointsAchieved the sum of points achieved by the team */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TeamScoreSum(long teamId, double sumPointsAchieved) { +public record TeamScoreSumDTO(long teamId, double sumPointsAchieved) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequests.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequestsDTO.java similarity index 67% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequests.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequestsDTO.java index fdb8e1022de3..656597da1232 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequests.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAnsweredMoreFeedbackRequestsDTO.java @@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -// Custom object for sql query +// Custom object for sql query, we cannot use primitive types here, because otherwise Hibernate gets confused @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TutorLeaderboardAnsweredMoreFeedbackRequests(Long userId, Long answeredRequests, Double points) { +public record TutorLeaderboardAnsweredMoreFeedbackRequestsDTO(Long userId, Long answeredRequests, Double points) { - public TutorLeaderboardAnsweredMoreFeedbackRequests() { + public TutorLeaderboardAnsweredMoreFeedbackRequestsDTO() { this(0L, 0L, 0.0); } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessments.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessmentsDTO.java similarity index 75% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessments.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessmentsDTO.java index 68c81198695f..670f2411d84e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessments.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardAssessmentsDTO.java @@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -// Custom object for sql query +// Custom object for sql query, we cannot use primitive types here, because otherwise Hibernate gets confused @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TutorLeaderboardAssessments(Long userId, Long assessments, Double points, Double averageScore, Double averageRating, Long numberOfRatings) { +public record TutorLeaderboardAssessmentsDTO(Long userId, Long assessments, Double points, Double averageScore, Double averageRating, Long numberOfRatings) { - public TutorLeaderboardAssessments() { + public TutorLeaderboardAssessmentsDTO() { this(0L, 0L, 0.0, 0.0, 0.0, 0L); } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponses.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponsesDTO.java similarity index 68% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponses.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponsesDTO.java index 8a21dc808767..b0818246186e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponses.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintResponsesDTO.java @@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -// Custom object for sql query +// Custom object for sql query, we cannot use primitive types here, because otherwise Hibernate gets confused @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TutorLeaderboardComplaintResponses(Long userId, Long complaintResponses, Double points) { +public record TutorLeaderboardComplaintResponsesDTO(Long userId, Long complaintResponses, Double points) { - public TutorLeaderboardComplaintResponses() { + public TutorLeaderboardComplaintResponsesDTO() { this(0L, 0L, 0.0); } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaints.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintsDTO.java similarity index 72% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaints.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintsDTO.java index 0bb734b1c3f3..43d431b5f81e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaints.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardComplaintsDTO.java @@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -// Custom object for sql query +// Custom object for sql query, we cannot use primitive types here, because otherwise Hibernate gets confused @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TutorLeaderboardComplaints(Long userId, Long allComplaints, Long acceptedComplaints, Double points) { +public record TutorLeaderboardComplaintsDTO(Long userId, Long allComplaints, Long acceptedComplaints, Double points) { - public TutorLeaderboardComplaints() { + public TutorLeaderboardComplaintsDTO() { this(0L, 0L, 0L, 0.0); } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequests.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequestsDTO.java similarity index 71% rename from src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequests.java rename to src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequestsDTO.java index fe898e65a444..e3fa9c394196 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequests.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/tutor/TutorLeaderboardMoreFeedbackRequestsDTO.java @@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -// Custom object for sql query +// Custom object for sql query, we cannot use primitive types here, because otherwise Hibernate gets confused @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record TutorLeaderboardMoreFeedbackRequests(Long userId, Long allRequests, Long notAnsweredRequests, Double points) { +public record TutorLeaderboardMoreFeedbackRequestsDTO(Long userId, Long allRequests, Long notAnsweredRequests, Double points) { - public TutorLeaderboardMoreFeedbackRequests() { + public TutorLeaderboardMoreFeedbackRequestsDTO() { this(0L, 0L, 0L, 0.0); } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintRepository.java index dc22a5631e17..627bae47f858 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintRepository.java @@ -17,11 +17,11 @@ import de.tum.cit.aet.artemis.assessment.domain.Complaint; import de.tum.cit.aet.artemis.assessment.domain.ComplaintType; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequests; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponses; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaints; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequests; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequestsDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; /** @@ -151,7 +151,7 @@ SELECT COUNT(c) * @return list of exercise ids with the number of complaints based on the complaint type */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( c.result.participation.exercise.id, COUNT(DISTINCT c) ) @@ -160,7 +160,7 @@ SELECT COUNT(c) AND c.complaintType = :complaintType GROUP BY c.result.participation.exercise.id """) - List countComplaintsByExerciseIdsAndComplaintType(@Param("exerciseIds") Set exerciseIds, @Param("complaintType") ComplaintType complaintType); + List countComplaintsByExerciseIdsAndComplaintType(@Param("exerciseIds") Set exerciseIds, @Param("complaintType") ComplaintType complaintType); /** * This method counts the number of complaints by complaint type associated to an exercise id ignoring test runs @@ -170,7 +170,7 @@ SELECT COUNT(c) * @return list of exercise ids with the number of complaints based on the complaint type */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( c.result.participation.exercise.id, COUNT(DISTINCT c) ) @@ -180,7 +180,7 @@ SELECT COUNT(c) AND c.result.participation.testRun = FALSE GROUP BY c.result.participation.exercise.id """) - List countComplaintsByExerciseIdsAndComplaintTypeIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds, + List countComplaintsByExerciseIdsAndComplaintTypeIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds, @Param("complaintType") ComplaintType complaintType); /** @@ -264,7 +264,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaints */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaints( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO( r.assessor.id, COUNT(c), SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END), @@ -280,7 +280,7 @@ SELECT COUNT(c) AND r.assessor.id IS NOT NULL GROUP BY r.assessor.id """) - List findTutorLeaderboardComplaintsByCourseId(@Param("courseId") long courseId); + List findTutorLeaderboardComplaintsByCourseId(@Param("courseId") long courseId); // Valid JPQL syntax. Only SCA fails to properly detect the types. /** @@ -290,7 +290,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaints */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaints( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO( r.assessor.id, COUNT(c), SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END), @@ -306,7 +306,7 @@ SELECT COUNT(c) AND r.assessor.id IS NOT NULL GROUP BY r.assessor.id """) - List findTutorLeaderboardComplaintsByExerciseId(@Param("exerciseId") long exerciseId); + List findTutorLeaderboardComplaintsByExerciseId(@Param("exerciseId") long exerciseId); // Valid JPQL syntax. Only SCA fails to properly detect the types. /** @@ -316,7 +316,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaints */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaints( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO( r.assessor.id, COUNT(c), SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END), @@ -333,7 +333,7 @@ SELECT COUNT(c) AND r.assessor.id IS NOT NULL GROUP BY r.assessor.id """) - List findTutorLeaderboardComplaintsByExamId(@Param("examId") long examId); + List findTutorLeaderboardComplaintsByExamId(@Param("examId") long examId); /** * Get the number of complaintResponses for all tutors assessments of a course @@ -342,7 +342,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaintResponses */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponses( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO( cr.reviewer.id, COUNT(c), SUM(e.maxPoints) @@ -358,7 +358,7 @@ SELECT COUNT(c) AND c.accepted IS NOT NULL GROUP BY cr.reviewer.id """) - List findTutorLeaderboardComplaintResponsesByCourseId(@Param("courseId") long courseId); + List findTutorLeaderboardComplaintResponsesByCourseId(@Param("courseId") long courseId); /** * Get the number of complaintResponses for all tutors assessments of an exercise @@ -367,7 +367,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaintResponses */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponses( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO( cr.reviewer.id, COUNT(c), SUM(e.maxPoints) @@ -383,7 +383,7 @@ SELECT COUNT(c) AND c.accepted IS NOT NULL GROUP BY cr.reviewer.id """) - List findTutorLeaderboardComplaintResponsesByExerciseId(@Param("exerciseId") long exerciseId); + List findTutorLeaderboardComplaintResponsesByExerciseId(@Param("exerciseId") long exerciseId); /** * Get the number of complaintResponses for all tutors assessments of an exam @@ -392,7 +392,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardComplaintResponses */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponses( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO( cr.reviewer.id, COUNT(c), SUM(e.maxPoints) @@ -409,7 +409,7 @@ SELECT COUNT(c) AND c.accepted IS NOT NULL GROUP BY cr.reviewer.id """) - List findTutorLeaderboardComplaintResponsesByExamId(@Param("examId") long examId); + List findTutorLeaderboardComplaintResponsesByExamId(@Param("examId") long examId); // Valid JPQL syntax. Only SCA fails to properly detect the types. /** @@ -419,7 +419,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardMoreFeedbackRequests */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequests( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO( r.assessor.id, COUNT(c), SUM( CASE WHEN (c.accepted IS NULL) THEN 1L ELSE 0L END), @@ -434,7 +434,7 @@ SELECT COUNT(c) AND r.completionDate IS NOT NULL GROUP BY r.assessor.id """) - List findTutorLeaderboardMoreFeedbackRequestsByCourseId(@Param("courseId") long courseId); + List findTutorLeaderboardMoreFeedbackRequestsByCourseId(@Param("courseId") long courseId); // Valid JPQL syntax. Only SCA fails to properly detect the types. /** @@ -444,7 +444,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardMoreFeedbackRequests */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequests( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO( r.assessor.id, COUNT(c), SUM( CASE WHEN (c.accepted IS NULL) THEN 1L ELSE 0L END), @@ -460,7 +460,7 @@ SELECT COUNT(c) AND r.completionDate IS NOT NULL GROUP BY r.assessor.id """) - List findTutorLeaderboardMoreFeedbackRequestsByExerciseId(@Param("exerciseId") long exerciseId); + List findTutorLeaderboardMoreFeedbackRequestsByExerciseId(@Param("exerciseId") long exerciseId); /** * Get the number of Feedback Request Responses for all tutors assessments of a course @@ -469,7 +469,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardAnsweredMoreFeedbackRequests */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequests( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequestsDTO( cr.reviewer.id, COUNT(c), SUM(e.maxPoints) @@ -485,7 +485,7 @@ SELECT COUNT(c) AND c.accepted = TRUE GROUP BY cr.reviewer.id """) - List findTutorLeaderboardAnsweredMoreFeedbackRequestsByCourseId(@Param("courseId") long courseId); + List findTutorLeaderboardAnsweredMoreFeedbackRequestsByCourseId(@Param("courseId") long courseId); /** * Get the number of Feedback Request Responses for all tutors assessments of an exercise @@ -494,7 +494,7 @@ SELECT COUNT(c) * @return list of TutorLeaderboardAnsweredMoreFeedbackRequests */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequests( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequestsDTO( cr.reviewer.id, COUNT(c), SUM(e.maxPoints) @@ -510,7 +510,7 @@ SELECT COUNT(c) AND c.accepted = TRUE GROUP BY cr.reviewer.id """) - List findTutorLeaderboardAnsweredMoreFeedbackRequestsByExerciseId(@Param("exerciseId") long exerciseId); + List findTutorLeaderboardAnsweredMoreFeedbackRequestsByExerciseId(@Param("exerciseId") long exerciseId); default Complaint findWithEagerAssessorByIdElseThrow(Long complaintId) { return getValueElseThrow(findByIdWithEagerAssessor(complaintId), complaintId); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintResponseRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintResponseRepository.java index 1410da0c1f07..d71043776e85 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintResponseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ComplaintResponseRepository.java @@ -14,7 +14,7 @@ import de.tum.cit.aet.artemis.assessment.domain.ComplaintResponse; import de.tum.cit.aet.artemis.assessment.domain.ComplaintType; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; /** @@ -66,7 +66,7 @@ SELECT COUNT (DISTINCT cr) * @return List of exercise ids with their number of complaints */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( cr.complaint.result.participation.exercise.id, COUNT(DISTINCT cr) ) @@ -77,7 +77,7 @@ SELECT COUNT (DISTINCT cr) AND cr.complaint.result.participation.testRun = FALSE GROUP BY cr.complaint.result.participation.exercise.id """) - List countComplaintsByExerciseIdsAndComplaintComplaintTypeIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds, + List countComplaintsByExerciseIdsAndComplaintComplaintTypeIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds, @Param("complaintType") ComplaintType complaintType); /** @@ -88,7 +88,7 @@ List countComplaintsByExerciseIdsAndComplaintComplaintTypeIgno * @return list of exercise ids with their number of complaints based on the complaint type */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( cr.complaint.result.participation.exercise.id, COUNT(DISTINCT cr) ) @@ -98,7 +98,8 @@ List countComplaintsByExerciseIdsAndComplaintComplaintTypeIgno AND cr.complaint.complaintType = :complaintType GROUP BY cr.complaint.result.participation.exercise.id """) - List countComplaintsByExerciseIdsAndComplaintComplaintType(@Param("exerciseIds") Set exerciseIds, @Param("complaintType") ComplaintType complaintType); + List countComplaintsByExerciseIdsAndComplaintComplaintType(@Param("exerciseIds") Set exerciseIds, + @Param("complaintType") ComplaintType complaintType); /** * Delete all complaint responses that belong to the given result diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/FeedbackRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/FeedbackRepository.java index 99e6a15652cc..12f4e658b7fc 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/FeedbackRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/FeedbackRepository.java @@ -38,9 +38,9 @@ public interface FeedbackRepository extends ArtemisJpaRepository List findFeedbackByGradingInstructionIds(@Param("gradingInstructionsIds") List gradingInstructionsIds); @Query(""" - SELECT COUNT(*) > 0 - FROM Feedback feedback - WHERE feedback.gradingInstruction.id IN :gradingInstructionsIds + SELECT COUNT(*) > 0 + FROM Feedback feedback + WHERE feedback.gradingInstruction.id IN :gradingInstructionsIds """) boolean hasFeedbackFromGradingInstructionIds(@Param("gradingInstructionsIds") List gradingInstructionsIds); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/LongFeedbackTextRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/LongFeedbackTextRepository.java index cf14579e726e..6ad61c4ef7ff 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/LongFeedbackTextRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/LongFeedbackTextRepository.java @@ -23,7 +23,7 @@ public interface LongFeedbackTextRepository extends ArtemisJpaRepository findByFeedbackIds(@Param("feedbackIds") List feedbackIds); @Query(""" diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ParticipantScoreRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ParticipantScoreRepository.java index 07dbcb9a10fa..14598bad30b1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ParticipantScoreRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ParticipantScoreRepository.java @@ -19,7 +19,7 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.assessment.domain.ParticipantScore; -import de.tum.cit.aet.artemis.assessment.dto.ScoreDistribution; +import de.tum.cit.aet.artemis.assessment.dto.ScoreDistributionDTO; import de.tum.cit.aet.artemis.assessment.service.ParticipantScoreScheduleService; import de.tum.cit.aet.artemis.core.dto.CourseManagementOverviewExerciseStatisticsDTO; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; @@ -44,6 +44,10 @@ public interface ParticipantScoreRepository extends ArtemisJpaRepository findAllOutdated(); + @Override + @EntityGraph(type = LOAD, attributePaths = { "exercise", "lastResult", "lastRatedResult" }) + List findAll(); + @EntityGraph(type = LOAD, attributePaths = { "exercise", "lastResult", "lastRatedResult" }) List findAllByExercise(Exercise exercise); @@ -88,6 +92,7 @@ SELECT AVG(p.lastScore) * @param resultId the id of the result to be removed * @see ParticipantScoreScheduleService */ + @Transactional // ok because of delete default void clearAllByResultId(Long resultId) { this.clearLastResultByResultId(resultId); this.clearLastRatedResultByResultId(resultId); @@ -112,13 +117,13 @@ SELECT MAX(ps.lastModifiedDate) AS latestModifiedDate List getAggregatedExerciseScoresInformation(@Param("exercises") Set exercises); @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.ScoreDistribution(count(p.id), p.lastRatedScore) + SELECT new de.tum.cit.aet.artemis.assessment.dto.ScoreDistributionDTO(count(p.id), p.lastRatedScore) FROM ParticipantScore p WHERE p.exercise.id = :exerciseId GROUP BY p.id ORDER BY p.lastRatedScore ASC """) - List getScoreDistributionForExercise(@Param("exerciseId") Long exerciseId); + List getScoreDistributionForExercise(@Param("exerciseId") Long exerciseId); /** * Delete all participant scores for a given exercise diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/RatingRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/RatingRepository.java index baa736e4f7b2..13a107e5d9b4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/RatingRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/RatingRepository.java @@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.assessment.domain.Rating; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCount; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCountDTO; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; /** @@ -38,8 +38,8 @@ public interface RatingRepository extends ArtemisJpaRepository { // Valid JPQL syntax, only SCA is not able to parse it @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCount( - CAST(SUM(ra.rating) AS double) / SUM(CASE WHEN ra.rating IS NOT NULL THEN 1 ELSE 0 END), + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCountDTO( + CAST(CAST(SUM(ra.rating) AS double) / SUM(CASE WHEN ra.rating IS NOT NULL THEN 1 ELSE 0 END) AS double), SUM(CASE WHEN ra.rating IS NOT NULL THEN 1 ELSE 0 END)) FROM Result r JOIN r.participation p @@ -48,7 +48,7 @@ public interface RatingRepository extends ArtemisJpaRepository { WHERE r.completionDate IS NOT NULL AND e.id = :exerciseId """) - ExerciseRatingCount averageRatingByExerciseId(@Param("exerciseId") long exerciseId); + ExerciseRatingCountDTO averageRatingByExerciseId(@Param("exerciseId") long exerciseId); /** * Count all ratings given to submissions for the given course. diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ResultRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ResultRepository.java index 5aca69d700a4..87c381b777d9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ResultRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/ResultRepository.java @@ -27,8 +27,8 @@ import de.tum.cit.aet.artemis.assessment.domain.GradingCriterion; import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.dto.ResultWithPointsPerGradingCriterionDTO; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ResultCount; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessments; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ResultCountDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.DomainObject; import de.tum.cit.aet.artemis.core.domain.User; @@ -200,7 +200,7 @@ default Optional findFirstByParticipationIdAndRatedWithSubmissionOrderBy LEFT JOIN FETCH r.feedbacks LEFT JOIN FETCH r.assessor LEFT JOIN FETCH r.assessmentNote - LEFT JOIN FETCH r.participation p + LEFT JOIN FETCH TREAT (r.participation AS StudentParticipation ) p LEFT JOIN FETCH p.team t LEFT JOIN FETCH t.students WHERE r.id = :resultId @@ -214,7 +214,7 @@ default Optional findFirstByParticipationIdAndRatedWithSubmissionOrderBy * @return a list with 3 elements: count of rated (in time) and unrated (late) assessments of a course and count of assessments without rating (null) */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ResultCount(r.rated, COUNT(r)) + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ResultCountDTO(r.rated, COUNT(r)) FROM Result r JOIN r.participation p WHERE r.completionDate IS NOT NULL @@ -222,7 +222,7 @@ default Optional findFirstByParticipationIdAndRatedWithSubmissionOrderBy AND p.exercise.id IN :exerciseIds GROUP BY r.rated """) - List countAssessmentsByExerciseIdsAndRated(@Param("exerciseIds") Set exerciseIds); + List countAssessmentsByExerciseIdsAndRated(@Param("exerciseIds") Set exerciseIds); /** * Load a result from the database by its id together with the associated submission and the list of feedback items. @@ -235,7 +235,7 @@ default Optional findFirstByParticipationIdAndRatedWithSubmissionOrderBy FROM Result r LEFT JOIN FETCH r.submission LEFT JOIN FETCH r.feedbacks - LEFT JOIN FETCH r.participation p + LEFT JOIN FETCH TREAT (r.participation AS StudentParticipation ) p LEFT JOIN FETCH p.team t LEFT JOIN FETCH t.students WHERE r.id = :resultId @@ -338,7 +338,7 @@ SELECT COUNT(DISTINCT p) AND r.rated = TRUE AND r.completionDate IS NOT NULL AND (p.exercise.dueDate IS NULL OR r.submission.submissionDate <= p.exercise.dueDate) - """) + """) long countNumberOfAssessmentsByTypeForExerciseBeforeDueDate(@Param("exerciseId") long exerciseId, @Param("types") List types); @Query(""" @@ -570,14 +570,13 @@ else if (Boolean.FALSE.equals(ratedCount.rated())) { return new DueDateStat(inTime, late); } - // Valid JPQL syntax, only SCA is not able to parse it @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessments( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO( r.assessor.id, COUNT(r), SUM(e.maxPoints), AVG(r.score), - CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END), + CAST(CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) AS double), SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) ) FROM Result r @@ -589,18 +588,15 @@ else if (Boolean.FALSE.equals(ratedCount.rated())) { AND e.id IN :exerciseIds GROUP BY r.assessor.id """) - List findTutorLeaderboardAssessmentByCourseId(@Param("exerciseIds") Set exerciseIds); - - // Alternative which might be faster, in particular for complaints in the other repositories - // Valid JPQL syntax, only SCA is not able to parse it + List findTutorLeaderboardAssessmentByCourseId(@Param("exerciseIds") Set exerciseIds); @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessments( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO( r.assessor.id, COUNT(r), SUM(e.maxPoints), AVG(r.score), - CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END), + CAST(CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) AS double), SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) ) FROM Result r @@ -612,15 +608,16 @@ else if (Boolean.FALSE.equals(ratedCount.rated())) { AND e.id = :exerciseId GROUP BY r.assessor.id """) - List findTutorLeaderboardAssessmentByExerciseId(@Param("exerciseId") long exerciseId); + List findTutorLeaderboardAssessmentByExerciseId(@Param("exerciseId") long exerciseId); + // Valid JPQL syntax, only SCA is not able to parse it due to mixing primitive and object types @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessments( + SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO( r.assessor.id, COUNT(r), SUM(e.maxPoints), AVG(r.score), - CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END), + CAST(CAST(SUM(rating.rating) AS double) / SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) AS double), SUM(CASE WHEN rating.rating IS NOT NULL THEN 1 ELSE 0 END) ) FROM Result r @@ -634,7 +631,7 @@ else if (Boolean.FALSE.equals(ratedCount.rated())) { AND ex.id = :examId GROUP BY r.assessor.id """) - List findTutorLeaderboardAssessmentByExamId(@Param("examId") long examId); + List findTutorLeaderboardAssessmentByExamId(@Param("examId") long examId); /** * This function is used for submitting a manual assessment/result. diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/StudentScoreRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/StudentScoreRepository.java index 7515acd4f459..dbf32eb37bad 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/StudentScoreRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/StudentScoreRepository.java @@ -16,7 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.assessment.domain.StudentScore; -import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSum; +import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSumDTO; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @@ -33,13 +33,13 @@ public interface StudentScoreRepository extends ArtemisJpaRepository findByExercise_IdAndUser_Id(long exerciseId, long userId); @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSum(u.id, COALESCE(SUM(s.lastRatedPoints), 0)) + SELECT new de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSumDTO(u.id, COALESCE(SUM(s.lastRatedPoints), 0)) FROM StudentScore s LEFT JOIN s.user u WHERE s.exercise IN :exercises GROUP BY u.id """) - Set getAchievedPointsOfStudents(@Param("exercises") Set exercises); + Set getAchievedPointsOfStudents(@Param("exercises") Set exercises); @Query(""" SELECT s diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/TeamScoreRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/TeamScoreRepository.java index 042dada814e0..5556fecb148a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/TeamScoreRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/TeamScoreRepository.java @@ -16,7 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.assessment.domain.TeamScore; -import de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSum; +import de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSumDTO; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @@ -34,13 +34,13 @@ public interface TeamScoreRepository extends ArtemisJpaRepository findByExercise_IdAndTeam_Id(Long exerciseId, Long teamId); @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSum(t.id, COALESCE(SUM(s.lastRatedPoints), 0)) + SELECT new de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSumDTO(t.id, COALESCE(SUM(s.lastRatedPoints), 0)) FROM TeamScore s LEFT JOIN s.team t WHERE s.exercise IN :exercises GROUP BY t.id """) - Set getAchievedPointsOfTeams(@Param("exercises") Set exercises); + Set getAchievedPointsOfTeams(@Param("exercises") Set exercises); @Query(""" SELECT s diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentDashboardService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentDashboardService.java index 93302aac4e9f..08f256cc2ed7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentDashboardService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentDashboardService.java @@ -15,7 +15,7 @@ import de.tum.cit.aet.artemis.assessment.domain.ExampleSubmission; import de.tum.cit.aet.artemis.assessment.domain.TutorParticipation; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; import de.tum.cit.aet.artemis.assessment.repository.ExampleSubmissionRepository; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; import de.tum.cit.aet.artemis.core.dto.DueDateStat; @@ -155,9 +155,9 @@ public void generateStatisticsForExercisesForAssessmentDashboard(Set e * @param examMode - if the exercises are part of an exam */ private void calculateNumberOfSubmissions(Set programmingExercises, Set nonProgrammingExercises, boolean examMode) { - final List programmingSubmissionsCounts; - final List submissionCounts; - final List lateSubmissionCounts; + final List programmingSubmissionsCounts; + final List submissionCounts; + final List lateSubmissionCounts; Set programmingExerciseIds = programmingExercises.stream().map(Exercise::getId).collect(Collectors.toSet()); Set nonProgrammingExerciseIds = nonProgrammingExercises.stream().map(Exercise::getId).collect(Collectors.toSet()); @@ -172,9 +172,9 @@ private void calculateNumberOfSubmissions(Set programmingExercises, Se lateSubmissionCounts = submissionRepository.countByExerciseIdsSubmittedAfterDueDate(nonProgrammingExerciseIds); } // convert the data from the queries - var programmingSubmissionMap = programmingSubmissionsCounts.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); - var submissionMap = submissionCounts.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); - var lateSubmissionMap = lateSubmissionCounts.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); + var programmingSubmissionMap = programmingSubmissionsCounts.stream().collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); + var submissionMap = submissionCounts.stream().collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); + var lateSubmissionMap = lateSubmissionCounts.stream().collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); // set the number of submissions for the exercises programmingExercises.forEach(exercise -> exercise.setNumberOfSubmissions(new DueDateStat(programmingSubmissionMap.getOrDefault(exercise.getId(), 0L), 0L))); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ComplaintService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ComplaintService.java index 90433cf4a50f..7c05ad20306f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ComplaintService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ComplaintService.java @@ -20,7 +20,7 @@ import de.tum.cit.aet.artemis.assessment.domain.ComplaintType; import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.dto.ComplaintRequestDTO; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; import de.tum.cit.aet.artemis.assessment.repository.ComplaintRepository; import de.tum.cit.aet.artemis.assessment.repository.ComplaintResponseRepository; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; @@ -210,10 +210,10 @@ public void calculateNrOfOpenComplaints(Set exercises, boolean examMod if (exercises.isEmpty()) { return; } - List numberOfComplaintsOfExercise = List.of(); - List numberOfComplaintResponsesOfExercise = List.of(); - List numberOfMoreFeedbackRequestsOfExercise = List.of(); - List numberOfMoreFeedbackResponsesOfExercise = List.of(); + List numberOfComplaintsOfExercise = List.of(); + List numberOfComplaintResponsesOfExercise = List.of(); + List numberOfMoreFeedbackRequestsOfExercise = List.of(); + List numberOfMoreFeedbackResponsesOfExercise = List.of(); Set exerciseIds = exercises.stream().map(DomainObject::getId).collect(Collectors.toSet()); // only invoke the query for non empty exercise sets to avoid performance issues @@ -235,10 +235,12 @@ public void calculateNrOfOpenComplaints(Set exercises, boolean examMod ComplaintType.MORE_FEEDBACK); } } - var numberOfComplaintsMap = numberOfComplaintsOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); - var numberOfComplaintResponsesMap = numberOfComplaintResponsesOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); - var numberOfMoreFeedbackRequestsMap = numberOfMoreFeedbackRequestsOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); - var numberOfMoreFeedbackResponsesMap = numberOfMoreFeedbackResponsesOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntry::exerciseId, ExerciseMapEntry::value)); + var numberOfComplaintsMap = numberOfComplaintsOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); + var numberOfComplaintResponsesMap = numberOfComplaintResponsesOfExercise.stream().collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); + var numberOfMoreFeedbackRequestsMap = numberOfMoreFeedbackRequestsOfExercise.stream() + .collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); + var numberOfMoreFeedbackResponsesMap = numberOfMoreFeedbackResponsesOfExercise.stream() + .collect(Collectors.toMap(ExerciseMapEntryDTO::exerciseId, ExerciseMapEntryDTO::value)); exercises.forEach(exercise -> { exercise.setNumberOfOpenComplaints(numberOfComplaintsMap.getOrDefault(exercise.getId(), 0L) - numberOfComplaintResponsesMap.getOrDefault(exercise.getId(), 0L)); exercise.setNumberOfComplaints(numberOfComplaintsMap.getOrDefault(exercise.getId(), 0L)); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationService.java index d51d71f250a0..ff7a43011f70 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationService.java @@ -27,7 +27,7 @@ import de.tum.cit.aet.artemis.assessment.domain.GradingScale; import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.dto.BonusSourceResultDTO; -import de.tum.cit.aet.artemis.assessment.dto.MaxAndReachablePoints; +import de.tum.cit.aet.artemis.assessment.dto.MaxAndReachablePointsDTO; import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoresDTO; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; @@ -87,10 +87,10 @@ public CourseScoreCalculationService(StudentParticipationRepository studentParti * @param exercises the exercises which are included into max points calculation * @return the max and reachable max points for the given exercises */ - private MaxAndReachablePoints calculateMaxAndReachablePoints(GradingScale gradingScale, Set exercises) { + private MaxAndReachablePointsDTO calculateMaxAndReachablePoints(GradingScale gradingScale, Set exercises) { if (exercises.isEmpty()) { - return new MaxAndReachablePoints(0, 0, 0); + return new MaxAndReachablePointsDTO(0, 0, 0); } double maxPoints = 0.0; @@ -116,7 +116,7 @@ private MaxAndReachablePoints calculateMaxAndReachablePoints(GradingScale gradin reachableMaxPoints += reachablePresentationPoints; } - return new MaxAndReachablePoints(maxPoints, reachableMaxPoints, reachablePresentationPoints); + return new MaxAndReachablePointsDTO(maxPoints, reachableMaxPoints, reachablePresentationPoints); } /** @@ -140,7 +140,7 @@ public Map calculateCourseScoresForExamBonusSource(C return null; } - MaxAndReachablePoints maxAndReachablePoints = calculateMaxAndReachablePoints(gradingScale, courseExercises); + MaxAndReachablePointsDTO maxAndReachablePoints = calculateMaxAndReachablePoints(gradingScale, courseExercises); List plagiarismCases; @@ -175,7 +175,7 @@ public Map calculateCourseScoresForExamBonusSource(C } private BonusSourceResultDTO constructBonusSourceResultDTO(Course course, GradingScale gradingScale, Long studentId, List participations, - MaxAndReachablePoints maxAndReachablePoints, List plagiarismCases) { + MaxAndReachablePointsDTO maxAndReachablePoints, List plagiarismCases) { StudentScoresDTO studentScores = calculateCourseScoreForStudent(course, gradingScale, studentId, participations, maxAndReachablePoints, plagiarismCases); boolean presentationScorePassed; @@ -232,7 +232,7 @@ public CourseForDashboardDTO getScoresAndParticipationResults(Course course, Gra Set courseExercises = course.getExercises(); - MaxAndReachablePoints maxAndReachablePoints = calculateMaxAndReachablePoints(gradingScale, courseExercises); + MaxAndReachablePointsDTO maxAndReachablePoints = calculateMaxAndReachablePoints(gradingScale, courseExercises); List plagiarismCases = new ArrayList<>(); for (Exercise exercise : courseExercises) { @@ -315,7 +315,7 @@ private Map calculateCourseScores(Course course, * @return a StudentScoresDTO instance with the presentation score, relative and absolute points achieved by the given student. */ public StudentScoresDTO calculateCourseScoreForStudent(Course course, GradingScale gradingScale, Long studentId, Collection participationsOfStudent, - MaxAndReachablePoints maxAndReachablePoints, Collection plagiarismCases) { + MaxAndReachablePointsDTO maxAndReachablePoints, Collection plagiarismCases) { PlagiarismMapping plagiarismMapping = PlagiarismMapping.createFromPlagiarismCases(plagiarismCases); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreService.java index 812c2f3b31ea..355690ff6f52 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreService.java @@ -22,8 +22,8 @@ import de.tum.cit.aet.artemis.assessment.domain.GradingScale; import de.tum.cit.aet.artemis.assessment.domain.ParticipantScore; import de.tum.cit.aet.artemis.assessment.dto.score.ScoreDTO; -import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSum; -import de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSum; +import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoreSumDTO; +import de.tum.cit.aet.artemis.assessment.dto.score.TeamScoreSumDTO; import de.tum.cit.aet.artemis.assessment.repository.ParticipantScoreRepository; import de.tum.cit.aet.artemis.assessment.repository.StudentScoreRepository; import de.tum.cit.aet.artemis.assessment.repository.TeamScoreRepository; @@ -148,16 +148,16 @@ private List calculateScores(Set exercises, Set users, .getCourseViaExerciseGroupOrCourseMember(); // individual exercises - final Set studentAndAchievedPoints = studentScoreRepository.getAchievedPointsOfStudents(individualExercises); - Map pointsAchieved = studentAndAchievedPoints.stream().collect(Collectors.toMap(StudentScoreSum::userId, StudentScoreSum::sumPointsAchieved)); + final Set studentAndAchievedPoints = studentScoreRepository.getAchievedPointsOfStudents(individualExercises); + Map pointsAchieved = studentAndAchievedPoints.stream().collect(Collectors.toMap(StudentScoreSumDTO::userId, StudentScoreSumDTO::sumPointsAchieved)); // We have to retrieve this separately because the students are not directly retrievable due to the taxonomy structure - Set teamScoreSums = teamScoreRepository.getAchievedPointsOfTeams(teamExercises); - Set teamIds = teamScoreSums.stream().map(TeamScoreSum::teamId).collect(Collectors.toSet()); + Set teamScoreSums = teamScoreRepository.getAchievedPointsOfTeams(teamExercises); + Set teamIds = teamScoreSums.stream().map(TeamScoreSumDTO::teamId).collect(Collectors.toSet()); var teamList = teamRepository.findAllWithStudentsByIdIn(teamIds); var teamMap = teamList.stream().collect(Collectors.toMap(Team::getId, Function.identity())); final Set userIds = users.stream().map(User::getId).collect(Collectors.toSet()); - for (TeamScoreSum teamScoreSum : teamScoreSums) { + for (TeamScoreSumDTO teamScoreSum : teamScoreSums) { Team team = teamMap.get(teamScoreSum.teamId()); for (User student : team.getStudents()) { if (userIds.contains(student.getId())) { diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/RatingService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/RatingService.java index 4365ebebe1bc..9d29d7ebf0d4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/RatingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/RatingService.java @@ -10,7 +10,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Rating; import de.tum.cit.aet.artemis.assessment.domain.Result; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCount; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseRatingCountDTO; import de.tum.cit.aet.artemis.assessment.repository.RatingRepository; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; @@ -94,7 +94,7 @@ public Rating updateRating(long resultId, int ratingValue) { * @param exerciseId - id of the exercise * @return the rating information of the exercise */ - public ExerciseRatingCount averageRatingByExerciseId(Long exerciseId) { + public ExerciseRatingCountDTO averageRatingByExerciseId(Long exerciseId) { return ratingRepository.averageRatingByExerciseId(exerciseId); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java index fd0cb47e843b..07b038b9cab2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java @@ -61,7 +61,6 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseStudentParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; @@ -119,7 +118,7 @@ public ResultService(UserRepository userRepository, ResultRepository resultRepos SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository, ProgrammingExerciseStudentParticipationRepository programmingExerciseStudentParticipationRepository, StudentExamRepository studentExamRepository, BuildJobRepository buildJobRepository, BuildLogEntryService buildLogEntryService, StudentParticipationRepository studentParticipationRepository, - ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, ProgrammingExerciseTaskService programmingExerciseTaskService) { + ProgrammingExerciseTaskService programmingExerciseTaskService) { this.userRepository = userRepository; this.resultRepository = resultRepository; this.ltiNewResultService = ltiNewResultService; diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorEffortService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorEffortService.java index 7b89b6ed533a..1a774ab5044c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorEffortService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorEffortService.java @@ -13,7 +13,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.assessment.dto.TutorEffort; +import de.tum.cit.aet.artemis.assessment.dto.TutorEffortDTO; import de.tum.cit.aet.artemis.assessment.repository.TextAssessmentEventRepository; import de.tum.cit.aet.artemis.text.domain.TextAssessmentEvent; @@ -41,17 +41,17 @@ public TutorEffortService(TextAssessmentEventRepository textAssessmentEventRepos * @param exerciseId id of the exercise to calculate for * @return a list of built tutor efforts */ - public List buildTutorEffortList(Long courseId, Long exerciseId) { + public List buildTutorEffortList(Long courseId, Long exerciseId) { Map submissionsPerTutor = textAssessmentEventRepository.getAssessedSubmissionCountPerTutor(courseId, exerciseId); List listOfEvents = textAssessmentEventRepository.findAllNonEmptyEvents(courseId, exerciseId); - List tutorEffortList = new ArrayList<>(); + List tutorEffortList = new ArrayList<>(); Map> newMap = listOfEvents.stream().collect(groupingBy(TextAssessmentEvent::getUserId)); if (newMap.isEmpty()) { return tutorEffortList; } newMap.forEach((currentUserId, currentUserEvents) -> { - TutorEffort effort = createTutorEffortWithInformation(currentUserId, currentUserEvents, submissionsPerTutor.get(currentUserId)); + TutorEffortDTO effort = createTutorEffortWithInformation(currentUserId, currentUserEvents, submissionsPerTutor.get(currentUserId)); tutorEffortList.add(effort); }); return tutorEffortList; @@ -65,14 +65,9 @@ public List buildTutorEffortList(Long courseId, Long exerciseId) { * @param submissions the number of submissions the tutor assessed * @return a TutorEffort object with all the data set */ - private TutorEffort createTutorEffortWithInformation(Long userId, List events, int submissions) { - TutorEffort effort = new TutorEffort(); - effort.setUserId(userId); - effort.setCourseId(events.getFirst().getCourseId()); - effort.setExerciseId(events.getFirst().getTextExerciseId()); - effort.setTotalTimeSpentMinutes(calculateTutorOverallTimeSpent(events)); - effort.setNumberOfSubmissionsAssessed(submissions); - return effort; + private TutorEffortDTO createTutorEffortWithInformation(Long userId, List events, int submissions) { + var firstEvent = events.getFirst(); + return new TutorEffortDTO(userId, submissions, calculateTutorOverallTimeSpent(events), firstEvent.getTextExerciseId(), firstEvent.getCourseId()); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorLeaderboardService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorLeaderboardService.java index 8ce8a0e8dc92..d753216d78f0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorLeaderboardService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/TutorLeaderboardService.java @@ -12,11 +12,11 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequests; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessments; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponses; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaints; -import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequests; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAnsweredMoreFeedbackRequestsDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO; +import de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO; import de.tum.cit.aet.artemis.assessment.repository.ComplaintRepository; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; import de.tum.cit.aet.artemis.core.domain.Course; @@ -52,16 +52,16 @@ public TutorLeaderboardService(ResultRepository resultRepository, ComplaintRepos public List getCourseLeaderboard(Course course, Set exerciseIdsOfCourse) { var tutors = userRepository.getTutors(course); - List tutorLeaderboardAssessments = List.of(); + List tutorLeaderboardAssessmentDTOS = List.of(); // only invoke the query for non empty exercise sets to avoid performance issues if (!exerciseIdsOfCourse.isEmpty()) { - tutorLeaderboardAssessments = resultRepository.findTutorLeaderboardAssessmentByCourseId(exerciseIdsOfCourse); + tutorLeaderboardAssessmentDTOS = resultRepository.findTutorLeaderboardAssessmentByCourseId(exerciseIdsOfCourse); } var tutorLeaderboardComplaints = complaintRepository.findTutorLeaderboardComplaintsByCourseId(course.getId()); var tutorLeaderboardComplaintResponses = complaintRepository.findTutorLeaderboardComplaintResponsesByCourseId(course.getId()); var tutorLeaderboardMoreFeedbackRequests = complaintRepository.findTutorLeaderboardMoreFeedbackRequestsByCourseId(course.getId()); var tutorLeaderboardAnsweredMoreFeedbackRequests = complaintRepository.findTutorLeaderboardAnsweredMoreFeedbackRequestsByCourseId(course.getId()); - return aggregateTutorLeaderboardData(tutors, tutorLeaderboardAssessments, tutorLeaderboardComplaints, tutorLeaderboardMoreFeedbackRequests, + return aggregateTutorLeaderboardData(tutors, tutorLeaderboardAssessmentDTOS, tutorLeaderboardComplaints, tutorLeaderboardMoreFeedbackRequests, tutorLeaderboardComplaintResponses, tutorLeaderboardAnsweredMoreFeedbackRequests, false); } @@ -99,15 +99,15 @@ public List getExerciseLeaderboard(Exercise exercise) { } @NotNull - private List aggregateTutorLeaderboardData(Set tutors, List assessments, List complaints, - List feedbackRequests, List complaintResponses, - List answeredFeedbackRequests, boolean isExam) { + private List aggregateTutorLeaderboardData(Set tutors, List assessments, + List complaints, List feedbackRequests, + List complaintResponses, List answeredFeedbackRequests, boolean isExam) { - var assessmentsMap = assessments.stream().collect(Collectors.toMap(TutorLeaderboardAssessments::userId, value -> value)); - var complaintsMap = complaints.stream().collect(Collectors.toMap(TutorLeaderboardComplaints::userId, value -> value)); - var feedbackRequestsMap = feedbackRequests.stream().collect(Collectors.toMap(TutorLeaderboardMoreFeedbackRequests::userId, value -> value)); - var complaintResponsesMap = complaintResponses.stream().collect(Collectors.toMap(TutorLeaderboardComplaintResponses::userId, value -> value)); - var answeredFeedbackRequestsMap = answeredFeedbackRequests.stream().collect(Collectors.toMap(TutorLeaderboardAnsweredMoreFeedbackRequests::userId, value -> value)); + var assessmentsMap = assessments.stream().collect(Collectors.toMap(TutorLeaderboardAssessmentsDTO::userId, value -> value)); + var complaintsMap = complaints.stream().collect(Collectors.toMap(TutorLeaderboardComplaintsDTO::userId, value -> value)); + var feedbackRequestsMap = feedbackRequests.stream().collect(Collectors.toMap(TutorLeaderboardMoreFeedbackRequestsDTO::userId, value -> value)); + var complaintResponsesMap = complaintResponses.stream().collect(Collectors.toMap(TutorLeaderboardComplaintResponsesDTO::userId, value -> value)); + var answeredFeedbackRequestsMap = answeredFeedbackRequests.stream().collect(Collectors.toMap(TutorLeaderboardAnsweredMoreFeedbackRequestsDTO::userId, value -> value)); List tutorLeaderBoardEntries = new ArrayList<>(); @@ -121,29 +121,29 @@ private List aggregateTutorLeaderboardData(Set tutors long numberOfTutorMoreFeedbackRequests = 0L; double points = 0.0; - var assessmentsOfTutor = assessmentsMap.getOrDefault(tutor.getId(), new TutorLeaderboardAssessments()); + var assessmentsOfTutor = assessmentsMap.getOrDefault(tutor.getId(), new TutorLeaderboardAssessmentsDTO()); numberOfAssessments += assessmentsOfTutor.assessments(); points += assessmentsOfTutor.points(); - var complaintsAboutTutor = complaintsMap.getOrDefault(tutor.getId(), new TutorLeaderboardComplaints()); + var complaintsAboutTutor = complaintsMap.getOrDefault(tutor.getId(), new TutorLeaderboardComplaintsDTO()); numberOfTutorComplaints += complaintsAboutTutor.allComplaints(); numberOfAcceptedComplaints += complaintsAboutTutor.acceptedComplaints(); // accepted complaints count 2x negatively points -= 2.0 * complaintsAboutTutor.points(); - var complaintResponsesOfTutor = complaintResponsesMap.getOrDefault(tutor.getId(), new TutorLeaderboardComplaintResponses()); + var complaintResponsesOfTutor = complaintResponsesMap.getOrDefault(tutor.getId(), new TutorLeaderboardComplaintResponsesDTO()); numberOfComplaintResponses += complaintResponsesOfTutor.complaintResponses(); // resolved complaints count 2x points += 2.0 * complaintResponsesOfTutor.points(); if (!isExam) { - var feedbackRequestsAboutTutor = feedbackRequestsMap.getOrDefault(tutor.getId(), new TutorLeaderboardMoreFeedbackRequests()); + var feedbackRequestsAboutTutor = feedbackRequestsMap.getOrDefault(tutor.getId(), new TutorLeaderboardMoreFeedbackRequestsDTO()); numberOfNotAnsweredMoreFeedbackRequests += feedbackRequestsAboutTutor.notAnsweredRequests(); numberOfTutorMoreFeedbackRequests += feedbackRequestsAboutTutor.allRequests(); // not answered requests count only 1x negatively points -= feedbackRequestsAboutTutor.points(); - var answeredFeedbackRequestsOfTutor = answeredFeedbackRequestsMap.getOrDefault(tutor.getId(), new TutorLeaderboardAnsweredMoreFeedbackRequests()); + var answeredFeedbackRequestsOfTutor = answeredFeedbackRequestsMap.getOrDefault(tutor.getId(), new TutorLeaderboardAnsweredMoreFeedbackRequestsDTO()); numberOfAnsweredMoreFeedbackRequests += answeredFeedbackRequestsOfTutor.answeredRequests(); // answered requests doesn't count, because it only means that the tutor repaired the negative points } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java b/src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java index 678476645c96..5b7c0fd342cb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java @@ -331,21 +331,7 @@ private void filterOutUselessDataFromComplaint(Complaint complaint) { StudentParticipation originalParticipation = (StudentParticipation) complaint.getResult().getParticipation(); if (originalParticipation != null && originalParticipation.getExercise() != null) { - Exercise exerciseWithOnlyTitle = originalParticipation.getExercise(); - if (exerciseWithOnlyTitle instanceof TextExercise) { - exerciseWithOnlyTitle = new TextExercise(); - } - else if (exerciseWithOnlyTitle instanceof ModelingExercise) { - exerciseWithOnlyTitle = new ModelingExercise(); - } - else if (exerciseWithOnlyTitle instanceof FileUploadExercise) { - exerciseWithOnlyTitle = new FileUploadExercise(); - } - else if (exerciseWithOnlyTitle instanceof ProgrammingExercise) { - exerciseWithOnlyTitle = new ProgrammingExercise(); - } - exerciseWithOnlyTitle.setTitle(originalParticipation.getExercise().getTitle()); - exerciseWithOnlyTitle.setId(originalParticipation.getExercise().getId()); + final var exerciseWithOnlyTitle = getExercise(originalParticipation); originalParticipation.setExercise(exerciseWithOnlyTitle); } @@ -353,26 +339,39 @@ else if (exerciseWithOnlyTitle instanceof ProgrammingExercise) { Submission originalSubmission = complaint.getResult().getSubmission(); if (originalSubmission != null) { Submission submissionWithOnlyId; - if (originalSubmission instanceof TextSubmission) { - submissionWithOnlyId = new TextSubmission(); - } - else if (originalSubmission instanceof ModelingSubmission) { - submissionWithOnlyId = new ModelingSubmission(); - } - else if (originalSubmission instanceof FileUploadSubmission) { - submissionWithOnlyId = new FileUploadSubmission(); - } - else if (originalSubmission instanceof ProgrammingSubmission) { - submissionWithOnlyId = new ProgrammingSubmission(); - } - else { - return; + switch (originalSubmission) { + case TextSubmission ignored -> submissionWithOnlyId = new TextSubmission(); + case ModelingSubmission ignored -> submissionWithOnlyId = new ModelingSubmission(); + case FileUploadSubmission ignored -> submissionWithOnlyId = new FileUploadSubmission(); + case ProgrammingSubmission ignored -> submissionWithOnlyId = new ProgrammingSubmission(); + default -> { + return; + } } submissionWithOnlyId.setId(originalSubmission.getId()); complaint.getResult().setSubmission(submissionWithOnlyId); } } + private static Exercise getExercise(StudentParticipation originalParticipation) { + Exercise exerciseWithOnlyTitle = originalParticipation.getExercise(); + if (exerciseWithOnlyTitle instanceof TextExercise) { + exerciseWithOnlyTitle = new TextExercise(); + } + else if (exerciseWithOnlyTitle instanceof ModelingExercise) { + exerciseWithOnlyTitle = new ModelingExercise(); + } + else if (exerciseWithOnlyTitle instanceof FileUploadExercise) { + exerciseWithOnlyTitle = new FileUploadExercise(); + } + else if (exerciseWithOnlyTitle instanceof ProgrammingExercise) { + exerciseWithOnlyTitle = new ProgrammingExercise(); + } + exerciseWithOnlyTitle.setTitle(originalParticipation.getExercise().getTitle()); + exerciseWithOnlyTitle.setId(originalParticipation.getExercise().getId()); + return exerciseWithOnlyTitle; + } + private void filterOutUselessDataFromComplaints(List complaints, boolean filterOutStudentFromComplaints) { if (filterOutStudentFromComplaints) { complaints.forEach(this::filterOutStudentFromComplaint); diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/web/GradingScaleResource.java b/src/main/java/de/tum/cit/aet/artemis/assessment/web/GradingScaleResource.java index aae951b2e9a7..10a259237c55 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/web/GradingScaleResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/web/GradingScaleResource.java @@ -296,20 +296,7 @@ private void validatePresentationsConfiguration(GradingScale gradingScale) { return; } - Course course = gradingScale.getCourse(); - - // Check validity of basic presentation configuration - if (course != null && course.getPresentationScore() != null && course.getPresentationScore() != 0) { - // The presentationsNumber and presentationsWeight must be null. - if (gradingScale.getPresentationsNumber() != null || gradingScale.getPresentationsWeight() != null) { - throw new BadRequestAlertException("You cannot set up graded presentations if the course is already set up for basic presentations", ENTITY_NAME, - "basicPresentationAlreadySet"); - } - // The presentationScore must be above 0. - if (course.getPresentationScore() <= 0) { - throw new BadRequestAlertException("The number of presentations must be a whole number above 0!", ENTITY_NAME, "invalidBasicPresentationsConfiguration"); - } - } + final var course = getCourse(gradingScale); // Check validity of graded presentation configuration if (gradingScale.getPresentationsNumber() != null || gradingScale.getPresentationsWeight() != null) { @@ -327,4 +314,22 @@ private void validatePresentationsConfiguration(GradingScale gradingScale) { } } } + + private static Course getCourse(GradingScale gradingScale) { + Course course = gradingScale.getCourse(); + + // Check validity of basic presentation configuration + if (course != null && course.getPresentationScore() != null && course.getPresentationScore() != 0) { + // The presentationsNumber and presentationsWeight must be null. + if (gradingScale.getPresentationsNumber() != null || gradingScale.getPresentationsWeight() != null) { + throw new BadRequestAlertException("You cannot set up graded presentations if the course is already set up for basic presentations", ENTITY_NAME, + "basicPresentationAlreadySet"); + } + // The presentationScore must be above 0. + if (course.getPresentationScore() <= 0) { + throw new BadRequestAlertException("The number of presentations must be a whole number above 0!", ENTITY_NAME, "invalidBasicPresentationsConfiguration"); + } + } + return course; + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/web/TutorEffortResource.java b/src/main/java/de/tum/cit/aet/artemis/assessment/web/TutorEffortResource.java index ca31436a4e62..17885f07a778 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/web/TutorEffortResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/web/TutorEffortResource.java @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import de.tum.cit.aet.artemis.assessment.dto.TutorEffort; +import de.tum.cit.aet.artemis.assessment.dto.TutorEffortDTO; import de.tum.cit.aet.artemis.assessment.service.TutorEffortService; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; @@ -63,7 +63,7 @@ public TutorEffortResource(AuthorizationCheckService authorizationCheckService, */ @GetMapping("courses/{courseId}/exercises/{exerciseId}/tutor-effort") @EnforceAtLeastInstructor - public ResponseEntity> calculateTutorEfforts(@PathVariable Long courseId, @PathVariable Long exerciseId) { + public ResponseEntity> calculateTutorEfforts(@PathVariable Long courseId, @PathVariable Long exerciseId) { log.debug("tutor-effort with argument[s] course = {}, exercise = {}", courseId, exerciseId); // check courseId and exerciseId exist and are linked to each other @@ -75,7 +75,7 @@ public ResponseEntity> calculateTutorEfforts(@PathVariable Lon User user = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, exercise, user); - List tutorEffortList = tutorEffortService.buildTutorEffortList(courseId, exerciseId); + List tutorEffortList = tutorEffortService.buildTutorEffortList(courseId, exerciseId); if (tutorEffortList.isEmpty()) { return ResponseEntity.noContent().build(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/athena/service/AthenaRepositoryExportService.java b/src/main/java/de/tum/cit/aet/artemis/athena/service/AthenaRepositoryExportService.java index 529f0f6e9076..bf7a95437789 100644 --- a/src/main/java/de/tum/cit/aet/artemis/athena/service/AthenaRepositoryExportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/athena/service/AthenaRepositoryExportService.java @@ -19,7 +19,6 @@ import de.tum.cit.aet.artemis.core.exception.ServiceUnavailableException; import de.tum.cit.aet.artemis.core.service.FileService; import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseStudentParticipationRepository; @@ -107,8 +106,7 @@ public File exportRepository(long exerciseId, Long submissionId, RepositoryType if (repositoryType == null) { // Export student repository var submission = programmingSubmissionRepository.findById(submissionId).orElseThrow(); // Load participation with eager submissions - var participation = (ProgrammingExerciseStudentParticipation) programmingExerciseStudentParticipationRepository - .findWithSubmissionsById(submission.getParticipation().getId()).getFirst(); + var participation = programmingExerciseStudentParticipationRepository.findWithSubmissionsById(submission.getParticipation().getId()).getFirst(); zipFile = programmingExerciseExportService.getRepositoryWithParticipation(programmingExercise, participation, exportOptions, exportDir, exportDir, true); } else { diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/StatsForDashboardDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/StatsForDashboardDTO.java index d76b7010ed03..5f29b177aedd 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/dto/StatsForDashboardDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/StatsForDashboardDTO.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; +// TODO: convert to record @JsonInclude(JsonInclude.Include.NON_EMPTY) public class StatsForDashboardDTO { diff --git a/src/main/java/de/tum/cit/aet/artemis/core/service/StatisticsService.java b/src/main/java/de/tum/cit/aet/artemis/core/service/StatisticsService.java index 31f35f7e89d8..73b723235627 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/service/StatisticsService.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/service/StatisticsService.java @@ -24,7 +24,7 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.assessment.domain.GradingScale; -import de.tum.cit.aet.artemis.assessment.dto.ScoreDistribution; +import de.tum.cit.aet.artemis.assessment.dto.ScoreDistributionDTO; import de.tum.cit.aet.artemis.assessment.repository.GradingScaleRepository; import de.tum.cit.aet.artemis.assessment.repository.ParticipantScoreRepository; import de.tum.cit.aet.artemis.core.domain.Course; @@ -222,12 +222,12 @@ public ExerciseManagementStatisticsDTO getExerciseStatistics(Exercise exercise) Double maxPoints = exercise.getMaxPoints(); Double averageScore = participantScoreRepository.findAvgScore(Set.of(exercise)); double averageScoreForExercise = averageScore != null ? roundScoreSpecifiedByCourseSettings(averageScore, course) : 0.0; - List scores = participantScoreRepository.getScoreDistributionForExercise(exercise.getId()); + List scores = participantScoreRepository.getScoreDistributionForExercise(exercise.getId()); var scoreDistribution = new int[10]; Arrays.fill(scoreDistribution, 0); scores.forEach(score -> { - var index = (int) (score.getScore() / 10.0); + var index = (int) (score.score() / 10.0); if (index >= 10) { scoreDistribution[9] += 1; } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/SubmissionRepository.java b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/SubmissionRepository.java index 26ed288340e4..16cac0f54cef 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/SubmissionRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/SubmissionRepository.java @@ -15,7 +15,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.dto.DueDateStat; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; @@ -325,7 +325,7 @@ SELECT COUNT(DISTINCT p) * exercise due date at all */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( p.exercise.id, COUNT(DISTINCT p) ) @@ -338,7 +338,7 @@ SELECT COUNT(DISTINCT p) AND (e.dueDate IS NULL OR s.submissionDate <= e.dueDate) GROUP BY p.exercise.id """) - List countByExerciseIdsSubmittedBeforeDueDateIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds); + List countByExerciseIdsSubmittedBeforeDueDateIgnoreTestRuns(@Param("exerciseIds") Set exerciseIds); /** * Calculate the number of submissions for the given exercise by the given student. @@ -361,7 +361,7 @@ SELECT COUNT(DISTINCT s) * @return the numbers of submissions belonging to each exercise id, which have the submitted flag set to true and the submission date after the exercise due date */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( e.id, COUNT(DISTINCT p) ) @@ -373,7 +373,7 @@ SELECT COUNT(DISTINCT s) AND s.submissionDate > e.dueDate GROUP BY e.id """) - List countByExerciseIdsSubmittedAfterDueDate(@Param("exerciseIds") Set exerciseIds); + List countByExerciseIdsSubmittedAfterDueDate(@Param("exerciseIds") Set exerciseIds); /** * Returns submissions for an exercise. Returns only a submission that has a result with a matching assessor. Since the results list may also contain diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java index 4bdcec847878..370c191e0ac0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java @@ -23,7 +23,7 @@ import org.springframework.stereotype.Repository; import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry; +import de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; @@ -411,7 +411,7 @@ SELECT COUNT (DISTINCT p) * @return list of exercises with the count of distinct submissions belonging to the exercise id */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntry( + SELECT new de.tum.cit.aet.artemis.assessment.dto.dashboard.ExerciseMapEntryDTO( p.exercise.id, count(DISTINCT p) ) @@ -423,7 +423,7 @@ SELECT COUNT (DISTINCT p) AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) GROUP BY p.exercise.id """) - List countSubmissionsByExerciseIdsSubmittedIgnoreTestRun(@Param("exerciseIds") Set exerciseIds); + List countSubmissionsByExerciseIdsSubmittedIgnoreTestRun(@Param("exerciseIds") Set exerciseIds); /** * In distinction to other exercise types, students can have multiple submissions in a programming exercise. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanCreator.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanCreator.java index 3991904b5db2..332b601f9471 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanCreator.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanCreator.java @@ -46,8 +46,6 @@ public class JenkinsBuildPlanCreator implements JenkinsXmlConfigBuilder { private static final String REPLACE_SOLUTION_CHECKOUT_PATH = "#solutionCheckoutPath"; - private static final String REPLACE_PUSH_TOKEN = "#secretPushToken"; - private static final String REPLACE_ARTEMIS_NOTIFICATION_URL = "#notificationsUrl"; private static final String REPLACE_NOTIFICATIONS_TOKEN = "#jenkinsNotificationToken"; @@ -62,9 +60,6 @@ public class JenkinsBuildPlanCreator implements JenkinsXmlConfigBuilder { private String artemisNotificationUrl; - @Value("${artemis.continuous-integration.secret-push-token}") - private String pushToken; - @Value("${artemis.continuous-integration.vcs-credentials}") private String gitCredentialsKey; @@ -97,7 +92,7 @@ public Document buildBasicConfig(final ProgrammingLanguage programmingLanguage, final String jenkinsfile = getJenkinsfile(internalVcsRepositoryURLs, programmingLanguage, checkoutSolution, buildPlanUrl); final Path configFilePath = Path.of("templates", "jenkins", "config.xml"); - final var configFileReplacements = Map.of(REPLACE_PIPELINE_SCRIPT, jenkinsfile, REPLACE_PUSH_TOKEN, pushToken); + final var configFileReplacements = Map.of(REPLACE_PIPELINE_SCRIPT, jenkinsfile); final var xmlResource = resourceLoaderService.getResource(configFilePath); return JenkinsXmlFileUtils.readXmlFile(xmlResource, configFileReplacements); } diff --git a/src/main/resources/config/application-artemis.yml b/src/main/resources/config/application-artemis.yml index f1501cbceba2..4f54275e26d9 100644 --- a/src/main/resources/config/application-artemis.yml +++ b/src/main/resources/config/application-artemis.yml @@ -64,15 +64,6 @@ artemis: password: token: # Enter a valid token generated in the CI system giving Artemis full Admin access url: - # Some CI systems, like Jenkins, offer a specific token that gets checked against any incoming notifications - # from a VCS trying to trigger a build plan. Only if the notification request contains the correct token, the plan - # is triggered. This can be seen as an alternative to sending an authenticated request to a REST API and then - # triggering the plan. - # In the case of Artemis, this is only really needed for the Jenkins + GitLab setup, since the GitLab plugin in - # Jenkins only allows triggering the Jenkins jobs using such a token. Furthermore, in this case, the value of the - # hudson.util.Secret is stored in the build plan, so you also have to specify this encrypted string here and NOT the actual token value itself! - # You can retrieve this by getting any job.xml for a job with an activated GitLab step and your token value of choice. - secret-push-token: # Key of the saved credentials for the VCS service # GitLab CI: not needed # Jenkins: You have to specify the key from the credentials page in Jenkins under which the user and diff --git a/src/main/resources/templates/jenkins/config.xml b/src/main/resources/templates/jenkins/config.xml index 5f2f8f3cb077..3d26e85ea498 100644 --- a/src/main/resources/templates/jenkins/config.xml +++ b/src/main/resources/templates/jenkins/config.xml @@ -12,36 +12,6 @@ false - - GitLab - - - - - - true - false - false - false - false - false - never - false - Jenkins please retry a build - true - true - true - All - - - - - {#secretPushToken} - - false - - - diff --git a/src/test/java/de/tum/cit/aet/artemis/assessment/TutorEffortIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/assessment/TutorEffortIntegrationTest.java index b6f1e480d5fd..008eff5356b4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/assessment/TutorEffortIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/assessment/TutorEffortIntegrationTest.java @@ -13,7 +13,7 @@ import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; -import de.tum.cit.aet.artemis.assessment.dto.TutorEffort; +import de.tum.cit.aet.artemis.assessment.dto.TutorEffortDTO; import de.tum.cit.aet.artemis.assessment.repository.TextAssessmentEventRepository; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @@ -74,9 +74,10 @@ void testCalculateTutorEfforts0MinutesOneTimestamp() throws Exception { textAssessmentEventRepository.saveAll(events); - List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, TutorEffort.class); + List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, + TutorEffortDTO.class); - TutorEffort effortExpected = createTutorEffortObject(0); + TutorEffortDTO effortExpected = createTutorEffortObject(0); assertThat(tutorEfforts).isNotNull().hasSize(1); assertThat(tutorEfforts.getFirst()).usingRecursiveComparison().isEqualTo(effortExpected); @@ -93,9 +94,10 @@ void testCalculateTutorEffortsDistance5Minutes() throws Exception { textAssessmentEventRepository.saveAll(events); - List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, TutorEffort.class); + List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, + TutorEffortDTO.class); - TutorEffort effortExpected = createTutorEffortObject(25); + TutorEffortDTO effortExpected = createTutorEffortObject(25); assertThat(tutorEfforts).isNotNull().hasSize(1); assertThat(tutorEfforts.getFirst()).usingRecursiveComparison().isEqualTo(effortExpected); @@ -112,22 +114,17 @@ void testCalculateTutorEffortsDistance10Minutes() throws Exception { List events = createTextAssessmentEventsInIntervals(11, 10); textAssessmentEventRepository.saveAll(events); - List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, TutorEffort.class); + List tutorEfforts = request.getList("/api/courses/" + course.getId() + "/exercises/" + exercise.getId() + "/tutor-effort", HttpStatus.OK, + TutorEffortDTO.class); - TutorEffort effortExpected = createTutorEffortObject(0); + TutorEffortDTO effortExpected = createTutorEffortObject(0); assertThat(tutorEfforts).isNotNull().hasSize(1); assertThat(tutorEfforts.getFirst()).usingRecursiveComparison().isEqualTo(effortExpected); } - TutorEffort createTutorEffortObject(int minutes) { - TutorEffort tutorEffort = new TutorEffort(); - tutorEffort.setUserId(1L); - tutorEffort.setNumberOfSubmissionsAssessed(1); - tutorEffort.setExerciseId(exercise.getId()); - tutorEffort.setCourseId(course.getId()); - tutorEffort.setTotalTimeSpentMinutes(minutes); - return tutorEffort; + TutorEffortDTO createTutorEffortObject(int minutes) { + return new TutorEffortDTO(1L, 1, minutes, exercise.getId(), course.getId()); } List createTextAssessmentEventsInIntervals(int timestamps, int distance) { diff --git a/src/test/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationServiceTest.java index 078b2ea0e441..89d594d99982 100644 --- a/src/test/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/assessment/service/CourseScoreCalculationServiceTest.java @@ -20,7 +20,7 @@ import de.tum.cit.aet.artemis.assessment.domain.GradingScale; import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.dto.BonusSourceResultDTO; -import de.tum.cit.aet.artemis.assessment.dto.MaxAndReachablePoints; +import de.tum.cit.aet.artemis.assessment.dto.MaxAndReachablePointsDTO; import de.tum.cit.aet.artemis.assessment.dto.score.StudentScoresDTO; import de.tum.cit.aet.artemis.assessment.repository.GradingScaleRepository; import de.tum.cit.aet.artemis.assessment.test_repository.ResultTestRepository; @@ -156,7 +156,7 @@ void calculateCourseScoreForExamBonusSourceWithMultipleResultsInParticipation(bo result.score(null); StudentScoresDTO studentScoresDTO = courseScoreCalculationService.calculateCourseScoreForStudent(course, null, student.getId(), studentParticipations, - new MaxAndReachablePoints(25.0, 5.0, 0.0), List.of()); + new MaxAndReachablePointsDTO(25.0, 5.0, 0.0), List.of()); if (withDueDate) { assertThat(studentScoresDTO.absoluteScore()).isEqualTo(2.1); assertThat(studentScoresDTO.relativeScore()).isEqualTo(8.4); @@ -280,7 +280,7 @@ void calculateCourseScoreWithNoParticipations() { User student = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); StudentScoresDTO studentScore = courseScoreCalculationService.calculateCourseScoreForStudent(course, null, student.getId(), Collections.emptyList(), - new MaxAndReachablePoints(100.00, 100.00, 0.0), Collections.emptyList()); + new MaxAndReachablePointsDTO(100.00, 100.00, 0.0), Collections.emptyList()); assertThat(studentScore.absoluteScore()).isZero(); assertThat(studentScore.relativeScore()).isZero(); assertThat(studentScore.currentRelativeScore()).isZero(); diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/RepositoryArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/RepositoryArchitectureTest.java index a306c4bf47dc..91355aa292ca 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/RepositoryArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/RepositoryArchitectureTest.java @@ -67,9 +67,9 @@ void testJPQLStyle() { } // See https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_langref.html#jpa_langref_from_identifiers - private static final Set SQL_KEYWORDS = Set.of("SELECT", "UPDATE", "SET", "DELETE", "DISTINCT", "EXISTS", "FROM", "WHERE", "LEFT", "OUTER", "INNER", "JOIN", "FETCH", - "TREAT", "AND", "OR", "AS", "ON", "ORDER", "BY", "ASC", "DSC", "GROUP", "COUNT", "SUM", "AVG", "MAX", "MIN", "IS", "NOT", "FALSE", "TRUE", "NULL", "LIKE", "IN", - "BETWEEN", "HAVING", "EMPTY", "MEMBER", "OF", "UPPER", "LOWER", "TRIM"); + private static final Set SQL_KEYWORDS = Set.of("SELECT", "UPDATE", "CAST", "SET", "DELETE", "DISTINCT", "EXISTS", "FROM", "WHERE", "LEFT", "OUTER", "INNER", "JOIN", + "FETCH", "TREAT", "AND", "OR", "AS", "ON", "ORDER", "BY", "ASC", "DSC", "GROUP", "COUNT", "SUM", "AVG", "MAX", "MIN", "IS", "NOT", "FALSE", "TRUE", "NULL", "LIKE", + "IN", "BETWEEN", "HAVING", "EMPTY", "MEMBER", "OF", "UPPER", "LOWER", "TRIM"); private static final ArchCondition USE_UPPER_CASE_SQL_STYLE = new ArchCondition<>("have keywords in upper case") { diff --git a/src/test/resources/config/application-artemis.yml b/src/test/resources/config/application-artemis.yml index 9cac2ec6dc93..d06190330e15 100644 --- a/src/test/resources/config/application-artemis.yml +++ b/src/test/resources/config/application-artemis.yml @@ -48,7 +48,6 @@ artemis: password: fake-password token: fake-token url: https://continuous-integration.fake.fake - secret-push-token: fake-token-hash vcs-credentials: fake-key artemis-authentication-token-key: fake-key artemis-authentication-token-value: fake-token diff --git a/supporting_scripts/analyze_module_complexity.py b/supporting_scripts/analyze_module_complexity.py new file mode 100644 index 000000000000..aabce698f0ea --- /dev/null +++ b/supporting_scripts/analyze_module_complexity.py @@ -0,0 +1,98 @@ +import os +import subprocess +import json + +# Function to run cloc and get metrics, install cloc before using `brew install cloc` (macOS) or `sudo apt install cloc` (Linux) +def run_cloc(directory): + try: + result = subprocess.run( + ['cloc', '--json', directory], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + cloc_data = json.loads(result.stdout) + if 'header' in cloc_data: + # Sum all code lines from cloc output + total_lines = sum(lang_info['code'] for lang_info in cloc_data.values() if isinstance(lang_info, dict) and 'code' in lang_info) + return total_lines + return 0 + except Exception as e: + print(f"Error running cloc: {e}") + return None + +# Function to count the number of classes and methods in a Java file +def count_classes_methods(file_path): + class_count = 0 + method_count = 0 + with open(file_path, 'r') as f: + for line in f: + line = line.strip() + # Check for classes, enums, interfaces, or records + if any(keyword in line for keyword in ['class ', 'enum ', 'interface ', 'record ']): + class_count += 1 + if '(' in line and ')' in line and ('{' in line or ';' in line): + method_count += 1 + return class_count, method_count + +# Function to analyze individual module +def analyze_module(module_path): + total_files = 0 + total_classes = 0 + total_methods = 0 + + for root, dirs, files in os.walk(module_path): + java_files = [f for f in files if f.endswith('.java')] + total_files += len(java_files) + + for file in java_files: + file_path = os.path.join(root, file) + classes, methods = count_classes_methods(file_path) + total_classes += classes + total_methods += methods + + # Run cloc for lines of code and sum them up + total_loc = run_cloc(module_path) + + return { + 'total_files': total_files, + 'total_classes': total_classes, + 'total_methods': total_methods, + 'total_loc': total_loc + } + +# Function to dynamically detect all subfolders (modules) in the base directory +def detect_modules(base_directory): + modules = [] + for item in os.listdir(base_directory): + item_path = os.path.join(base_directory, item) + if os.path.isdir(item_path): + modules.append(item) + return sorted(modules) # Sort the modules alphabetically + +# Function to analyze all detected modules +def analyze_all_modules(base_directory): + modules = detect_modules(base_directory) + results = {} + for module in modules: + module_path = os.path.join(base_directory, module) + if os.path.exists(module_path): + print(f"Analyzing module: {module}") + results[module] = analyze_module(module_path) + else: + print(f"Module path does not exist: {module_path}") + return results + +# Specify the base directory of your codebase +base_directory = '../src/main/java/de/tum/cit/aet/artemis' + +# Analyze all dynamically detected modules +module_metrics = analyze_all_modules(base_directory) + +# Print the results for each module +for module, metrics in module_metrics.items(): + print(f"\nMetrics for module: {module}") + print(f"Java Files: {metrics['total_files']}") + print(f"Classes: {metrics['total_classes']} (includes enums, interfaces, records)") + print(f"Methods: {metrics['total_methods']}") + print(f"Lines of Code: {metrics['total_loc']}")