diff --git a/src/main/kotlin/fi/aalto/cs/apluscourses/api/APlusApi.kt b/src/main/kotlin/fi/aalto/cs/apluscourses/api/APlusApi.kt index 2cf464093..64e554cf1 100644 --- a/src/main/kotlin/fi/aalto/cs/apluscourses/api/APlusApi.kt +++ b/src/main/kotlin/fi/aalto/cs/apluscourses/api/APlusApi.kt @@ -180,7 +180,8 @@ object APlusApi { val UserID: Long, val Status: String, val Grade: Int, - val Penalty: Double? + val Penalty: Double?, + val Tags: String //tunnisteet ovat eroteltu pystyviivoilla | api:ssa ) } @@ -286,10 +287,45 @@ object APlusApi { @Resource("/submissions/{id}") class Submission(val id: Long) { + @OptIn(ExperimentalSerializationApi::class) suspend fun get(project: Project): SubmissionBody { - return CoursesClient.getInstance(project).getBody(this@Submission) + val json = Json { + ignoreUnknownKeys = true + namingStrategy = JsonNamingStrategy.SnakeCase + } + val noTagJson = json.encodeToString(OuterData(gradingData = null)) + val raw = CoursesClient.getInstance(project).getBody(this@Submission) + var tags: List = emptyList() + val rawGradingData = raw.gradingData?.let { + val potentialRaw = it.gradingData ?: noTagJson + json.decodeFromString(potentialRaw) + } + if (rawGradingData != null) { + tags = rawGradingData.submissionTags?.split(",")?.map { it.trim() } ?: emptyList() + } + + return SubmissionBody( + feedback = raw.feedback, + htmlUrl = raw.htmlUrl, + status = raw.status, + grade = raw.grade, + latePenaltyApplied = raw.latePenaltyApplied, + submissionTags = tags, + exercise = raw.exercise + ) } + @Serializable + data class RawSubmissionBody( + val feedback: String, + val htmlUrl: String, + val status: String, + val grade: Int, + val latePenaltyApplied: Double?, + val gradingData: OuterData?, + val exercise: SubmissionExercise, + ) + @Serializable data class SubmissionBody( val feedback: String, @@ -297,9 +333,20 @@ object APlusApi { val status: String, val grade: Int, val latePenaltyApplied: Double?, + val submissionTags: List = emptyList(), val exercise: SubmissionExercise, ) + @Serializable + data class OuterData( + val gradingData: String? = null, + ) + + @Serializable + data class RawGradingData( + val submissionTags: String? = "", + ) + @Serializable data class SubmissionExercise( val id: Long, diff --git a/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/Exercise.kt b/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/Exercise.kt index 5b553c6c3..c964f3ecc 100644 --- a/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/Exercise.kt +++ b/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/Exercise.kt @@ -37,7 +37,14 @@ data class Exercise( * Returns the best submission of this exercise (if one exists). */ private fun bestSubmission(): SubmissionResult? { - return submissionResults.find { it.id == bestSubmissionId } + //the best submission is always assumed to be the one returned by api, but if there is another one with equal/higher points and no 'warn', it is treated as the best locally + val best = submissionResults.find { it.id == bestSubmissionId } + return submissionResults.find { !it.hasTag("warn") && it.userPoints >= (best?.userPoints ?: 0) } ?: best + } + + fun bestHasWarn(): Boolean { + val bestSubmission = bestSubmission() + return bestSubmission != null && bestSubmission.hasTag("warn") } fun isLate(): Boolean { diff --git a/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/SubmissionResult.kt b/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/SubmissionResult.kt index 4bed3bb89..0f0323e8d 100644 --- a/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/SubmissionResult.kt +++ b/src/main/kotlin/fi/aalto/cs/apluscourses/model/exercise/SubmissionResult.kt @@ -10,6 +10,7 @@ data class SubmissionResult( val maxPoints: Int, var userPoints: Int, var latePenalty: Double?, + var tags: List, var status: Status, val filesInfo: List, val submitters: List?, @@ -25,6 +26,10 @@ data class SubmissionResult( this.status = status } + fun hasTag(tagSlug: String): Boolean { + return tags.contains(tagSlug) + } + enum class Status { GRADED, UNOFFICIAL, diff --git a/src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/ExercisesUpdater.kt b/src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/ExercisesUpdater.kt index 3f1d5026d..174d92752 100644 --- a/src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/ExercisesUpdater.kt +++ b/src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/ExercisesUpdater.kt @@ -221,6 +221,7 @@ class ExercisesUpdater( status = SubmissionResult.statusFromString(data?.Status), filesInfo = emptyList(), submitters = submitters, + tags = data?.Tags?.split("|")?.map { it.trim() } ?: emptyList() ) }.toMutableList(), maxPoints = exercise.maxPoints, @@ -272,6 +273,7 @@ class ExercisesUpdater( submissionResult.updateStatus(submission.status) submissionResult.userPoints = submission.grade submissionResult.latePenalty = submission.latePenaltyApplied + submissionResult.tags = submission.submissionTags Notifier.notify(FeedbackAvailableNotification(submissionResult, exercise, project), project) fireExerciseUpdated(exercise) } diff --git a/src/main/kotlin/fi/aalto/cs/apluscourses/ui/exercise/ExercisesTreeRenderer.kt b/src/main/kotlin/fi/aalto/cs/apluscourses/ui/exercise/ExercisesTreeRenderer.kt index 2f3386c46..ca629964d 100644 --- a/src/main/kotlin/fi/aalto/cs/apluscourses/ui/exercise/ExercisesTreeRenderer.kt +++ b/src/main/kotlin/fi/aalto/cs/apluscourses/ui/exercise/ExercisesTreeRenderer.kt @@ -190,8 +190,9 @@ class ExercisesTreeRenderer : NodeRenderer() { return if (isSubmittable()) baseColor else ColorUtil.withAlpha(baseColor, 0.5) } + //määrittelee yksittäisen palautksen väriä private fun submissionResultToColor(submission: SubmissionResult): Color { - return if (submission.userPoints == submission.maxPoints) { + return if (submission.userPoints == submission.maxPoints && !submission.hasTag("warn")) { JBColor(0x8bc34a, 0x8bc34a) } else { JBColor(0xffb74d, 0xffb74d) @@ -280,7 +281,7 @@ class ExercisesTreeRenderer : NodeRenderer() { Status.OPTIONAL_PRACTICE } else if (exercise.submissionResults.isEmpty()) { Status.NO_SUBMISSIONS - } else if (exercise.userPoints == exercise.maxPoints) { + } else if (exercise.userPoints == exercise.maxPoints && !exercise.bestHasWarn()) { Status.FULL_POINTS } else if (exercise.isLate()) { Status.LATE