Compare commits

..

3 Commits

Author SHA1 Message Date
jay-tux 3a47154969
Migration 2025-04-29 17:42:36 +02:00
jay-tux 6b80457da5
UI change: show group member role in peer evaluation view 2025-04-29 14:28:46 +02:00
jay-tux ab8cb36fd8
Small oversight 2025-04-29 13:37:58 +02:00
4 changed files with 33 additions and 14 deletions

View File

@ -31,6 +31,7 @@ kotlin {
implementation(libs.exposed.core) implementation(libs.exposed.core)
implementation(libs.exposed.jdbc) implementation(libs.exposed.jdbc)
implementation(libs.exposed.dao) implementation(libs.exposed.dao)
implementation(libs.exposed.migration)
implementation(libs.exposed.kotlin.datetime) implementation(libs.exposed.kotlin.datetime)
implementation(libs.sqlite) implementation(libs.sqlite)
implementation(libs.material3.desktop) implementation(libs.material3.desktop)

View File

@ -1,5 +1,6 @@
package com.jaytux.grader.data package com.jaytux.grader.data
import MigrationUtils
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
@ -17,15 +18,19 @@ object Database {
StudentToGroupEvaluation StudentToGroupEvaluation
) )
val addMissing = SchemaUtils.addMissingColumnsStatements( val migrate = MigrationUtils.statementsRequiredForDatabaseMigration(
Courses, Editions, Groups, Courses, Editions, Groups,
Students, GroupStudents, EditionStudents, Students, GroupStudents, EditionStudents,
GroupAssignments, SoloAssignments, GroupAssignmentCriteria, SoloAssignmentCriteria, GroupAssignments, SoloAssignments, GroupAssignmentCriteria, SoloAssignmentCriteria,
GroupFeedbacks, IndividualFeedbacks, SoloFeedbacks, GroupFeedbacks, IndividualFeedbacks, SoloFeedbacks,
PeerEvaluations, PeerEvaluationContents, StudentToStudentEvaluation, PeerEvaluations, PeerEvaluationContents, StudentToStudentEvaluation,
StudentToGroupEvaluation StudentToGroupEvaluation,
withLogs = true
) )
addMissing.forEach { exec(it) }
println(" --- Migration --- ")
migrate.forEach { println(it); exec(it) }
println(" --- End migration --- ")
} }
actual actual
} }

View File

@ -26,6 +26,7 @@ import com.jaytux.grader.viewmodel.SoloAssignmentState
import com.mohamedrejeb.richeditor.model.rememberRichTextState import com.mohamedrejeb.richeditor.model.rememberRichTextState
import com.mohamedrejeb.richeditor.ui.material3.OutlinedRichTextEditor import com.mohamedrejeb.richeditor.ui.material3.OutlinedRichTextEditor
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import org.jetbrains.exposed.sql.transactions.inTopLevelTransaction
@Composable @Composable
fun GroupAssignmentView(state: GroupAssignmentState) { fun GroupAssignmentView(state: GroupAssignmentState) {
@ -585,9 +586,21 @@ fun PeerEvaluationView(state: PeerEvaluationState) {
} }
MeasuredLazyColumn(key = idx) { MeasuredLazyColumn(key = idx) {
measuredItem { HLine() } measuredItem { HLine() }
items(current.students) { (from, glob, map) -> items(current.students) { (from, role, glob, map) ->
Row(Modifier.height(cellSize)) { Row(Modifier.height(cellSize)) {
Text(from.name, Modifier.width(textLenMeasured.dp).align(Alignment.CenterVertically)) Column(Modifier.width(textLenMeasured.dp).align(Alignment.CenterVertically)) {
Text(from.name, Modifier.width(textLenMeasured.dp))
role?.let { r ->
Row {
Spacer(Modifier.width(10.dp))
Text(
r,
style = MaterialTheme.typography.bodySmall,
fontStyle = FontStyle.Italic
)
}
}
}
LazyRow(state = horScroll) { LazyRow(state = horScroll) {
item { VLine() } item { VLine() }
items(map) { (to, entry) -> items(map) { (to, entry) ->

View File

@ -722,7 +722,7 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
class PeerEvaluationState(val evaluation: PeerEvaluation) { class PeerEvaluationState(val evaluation: PeerEvaluation) {
data class Student2StudentEntry(val grade: String, val feedback: String) data class Student2StudentEntry(val grade: String, val feedback: String)
data class StudentEntry(val student: Student, val global: Student2StudentEntry?, val others: List<Pair<Student, Student2StudentEntry?>>) data class StudentEntry(val student: Student, val role: String?, val global: Student2StudentEntry?, val others: List<Pair<Student, Student2StudentEntry?>>)
data class GroupEntry(val group: Group, val content: String, val students: List<StudentEntry>) data class GroupEntry(val group: Group, val content: String, val students: List<StudentEntry>)
val editionCourse = transaction { evaluation.edition.course to evaluation.edition } val editionCourse = transaction { evaluation.edition.course to evaluation.edition }
private val _name = mutableStateOf(evaluation.name); val name = _name.immutable() private val _name = mutableStateOf(evaluation.name); val name = _name.immutable()
@ -731,11 +731,17 @@ class PeerEvaluationState(val evaluation: PeerEvaluation) {
private fun Transaction.loadContents(): List<GroupEntry> { private fun Transaction.loadContents(): List<GroupEntry> {
return evaluation.edition.groups.map { group -> return evaluation.edition.groups.map { group ->
val globalNotes = PeerEvaluationContents.selectAll() val globalNotes = PeerEvaluationContents.selectAll()
.where { PeerEvaluationContents.groupId eq group.id }.firstOrNull()?.let { .where {
(PeerEvaluationContents.groupId eq group.id) and
(PeerEvaluationContents.peerEvaluationId eq evaluation.id)
}.firstOrNull()?.let {
it[PeerEvaluationContents.content] it[PeerEvaluationContents.content]
} }
val students = group.students.map { from -> val students = group.students.map { from ->
val role = GroupStudents.selectAll().where { (GroupStudents.studentId eq from.id) and (GroupStudents.groupId eq group.id) }.firstOrNull()?.let {
it[GroupStudents.role]
}
val s2g = StudentToGroupEvaluation.selectAll().where { val s2g = StudentToGroupEvaluation.selectAll().where {
(StudentToGroupEvaluation.peerEvaluationId eq evaluation.id) and (StudentToGroupEvaluation.peerEvaluationId eq evaluation.id) and
(StudentToGroupEvaluation.studentId eq from.id) (StudentToGroupEvaluation.studentId eq from.id)
@ -755,7 +761,7 @@ class PeerEvaluationState(val evaluation: PeerEvaluation) {
other to eval other to eval
} }
StudentEntry(from, s2g, others) StudentEntry(from, role, s2g, others)
} }
GroupEntry(group, globalNotes ?: "", students) GroupEntry(group, globalNotes ?: "", students)
@ -764,7 +770,6 @@ class PeerEvaluationState(val evaluation: PeerEvaluation) {
fun upsertGroupFeedback(group: Group, feedback: String) { fun upsertGroupFeedback(group: Group, feedback: String) {
transaction { transaction {
println("Upserting group-level notes for ${group.name} (${evaluation.name})")
PeerEvaluationContents.upsert { PeerEvaluationContents.upsert {
it[peerEvaluationId] = evaluation.id it[peerEvaluationId] = evaluation.id
it[groupId] = group.id it[groupId] = group.id
@ -777,7 +782,6 @@ class PeerEvaluationState(val evaluation: PeerEvaluation) {
fun upsertIndividualFeedback(from: Student, to: Student?, grade: String, feedback: String) { fun upsertIndividualFeedback(from: Student, to: Student?, grade: String, feedback: String) {
transaction { transaction {
to?.let { to?.let {
println("Upserting individual feedback for ${from.name} -> ${to.name} (${evaluation.name})")
StudentToStudentEvaluation.upsert { StudentToStudentEvaluation.upsert {
it[peerEvaluationId] = evaluation.id it[peerEvaluationId] = evaluation.id
it[studentIdFrom] = from.id it[studentIdFrom] = from.id
@ -791,10 +795,6 @@ class PeerEvaluationState(val evaluation: PeerEvaluation) {
it[this.grade] = grade it[this.grade] = grade
it[this.note] = feedback it[this.note] = feedback
} }
if(to == null) {
println("Upserting group-level feedback for ${from.name} ${from.id.value} (${evaluation.name} ${evaluation.id.value})")
}
} }
contents.refresh() contents.refresh()
} }