Fixed primary key-related (nullability) issues in DB schema

This commit is contained in:
2025-06-10 15:18:13 +02:00
parent 3a47154969
commit 29d23f8400
5 changed files with 40 additions and 23 deletions

View File

@ -56,6 +56,7 @@ object GroupAssignments : UUIDTable("grpAssgmts") {
val name = varchar("name", 50)
val assignment = text("assignment")
val deadline = datetime("deadline")
val globalCriterion = reference("global_crit", GroupAssignmentCriteria.id)
}
object GroupAssignmentCriteria : UUIDTable("grpAsCr") {
@ -70,6 +71,7 @@ object SoloAssignments : UUIDTable("soloAssgmts") {
val name = varchar("name", 50)
val assignment = text("assignment")
val deadline = datetime("deadline")
val globalCriterion = reference("global_crit", SoloAssignmentCriteria.id)
}
object SoloAssignmentCriteria : UUIDTable("soloAsCr") {
@ -86,7 +88,7 @@ object PeerEvaluations : UUIDTable("peerEvals") {
object GroupFeedbacks : CompositeIdTable("grpFdbks") {
val assignmentId = reference("group_assignment_id", GroupAssignments.id)
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id).nullable()
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id)
val groupId = reference("group_id", Groups.id)
val feedback = text("feedback")
val grade = varchar("grade", 32)
@ -96,7 +98,7 @@ object GroupFeedbacks : CompositeIdTable("grpFdbks") {
object IndividualFeedbacks : CompositeIdTable("indivFdbks") {
val assignmentId = reference("group_assignment_id", GroupAssignments.id)
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id).nullable()
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id)
val groupId = reference("group_id", Groups.id)
val studentId = reference("student_id", Students.id)
val feedback = text("feedback")
@ -107,7 +109,7 @@ object IndividualFeedbacks : CompositeIdTable("indivFdbks") {
object SoloFeedbacks : CompositeIdTable("soloFdbks") {
val assignmentId = reference("solo_assignment_id", SoloAssignments.id)
val criterionId = reference("criterion_id", SoloAssignmentCriteria.id).nullable()
val criterionId = reference("criterion_id", SoloAssignmentCriteria.id)
val studentId = reference("student_id", Students.id)
val feedback = text("feedback")
val grade = varchar("grade", 32)

View File

@ -3,7 +3,10 @@ package com.jaytux.grader.data
import MigrationUtils
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
object Database {
val db by lazy {

View File

@ -60,6 +60,7 @@ class GroupAssignment(id: EntityID<UUID>) : Entity<UUID>(id) {
var name by GroupAssignments.name
var assignment by GroupAssignments.assignment
var deadline by GroupAssignments.deadline
var globalCriterion by GroupAssignmentCriterion referencedOn GroupAssignments.globalCriterion
val criteria by GroupAssignmentCriterion referrersOn GroupAssignmentCriteria.assignmentId
}
@ -98,6 +99,7 @@ class SoloAssignment(id: EntityID<UUID>) : Entity<UUID>(id) {
var name by SoloAssignments.name
var assignment by SoloAssignments.assignment
var deadline by SoloAssignments.deadline
var globalCriterion by SoloAssignmentCriterion referencedOn SoloAssignments.globalCriterion
val criteria by SoloAssignmentCriterion referrersOn SoloAssignmentCriteria.assignmentId
}

View File

@ -475,7 +475,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
private val _task = mutableStateOf(assignment.assignment); val task = _task.immutable()
private val _deadline = mutableStateOf(assignment.deadline); val deadline = _deadline.immutable()
val criteria = RawDbState {
assignment.criteria.orderBy(GroupAssignmentCriteria.name to SortOrder.ASC).toList()
assignment.criteria.orderBy(GroupAssignmentCriteria.name to SortOrder.ASC).filter { it.id != assignment.globalCriterion.id }
}
val feedback = RawDbState { loadFeedback() }
@ -494,7 +494,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
private fun Transaction.loadFeedback(): List<Pair<Group, LocalGFeedback>> {
val allCrit = GroupAssignmentCriterion.find {
GroupAssignmentCriteria.assignmentId eq assignment.id
}
}.filter { it.id != assignment.globalCriterion.id }
return Group.find {
(Groups.editionId eq assignment.edition.id)
@ -502,16 +502,19 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
val forGroup = (GroupFeedbacks innerJoin Groups).selectAll().where {
(GroupFeedbacks.assignmentId eq assignment.id) and (Groups.id eq group.id)
}.map { row ->
val crit = row[GroupFeedbacks.criterionId]?.let { GroupAssignmentCriterion[it] }
val crit = GroupAssignmentCriterion[row[GroupFeedbacks.criterionId]]
val fdbk = row[GroupFeedbacks.feedback]
val grade = row[GroupFeedbacks.grade]
crit to FeedbackEntry(fdbk, grade)
}
val global = forGroup.firstOrNull { it.first == null }?.second
val byCrit_ = forGroup.map { it.first?.let { k -> LocalCriterionFeedback(k, it.second) } }
.filterNotNull().associateBy { it.criterion.id }
val global = forGroup.firstOrNull { it.first.id == assignment.globalCriterion.id }?.second
val byCrit_ = forGroup
.filter{ it.first.id != assignment.globalCriterion.id }
.map { LocalCriterionFeedback(it.first, it.second) }
.associateBy { it.criterion.id }
val byCrit = allCrit.map { c ->
byCrit_[c.id] ?: LocalCriterionFeedback(c, null)
}
@ -527,16 +530,19 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
(IndividualFeedbacks.assignmentId eq assignment.id) and
(GroupStudents.studentId eq student.id) and (Groups.id eq group.id)
}.map { row ->
val crit = row[IndividualFeedbacks.criterionId]?.let { id -> GroupAssignmentCriterion[id] }
val crit = GroupAssignmentCriterion[row[IndividualFeedbacks.criterionId]]
val fdbk = row[IndividualFeedbacks.feedback]
val grade = row[IndividualFeedbacks.grade]
crit to FeedbackEntry(fdbk, grade)
}
val global = forSt.firstOrNull { it.first == null }?.second
val byCrit_ = forSt.map { it.first?.let { k -> LocalCriterionFeedback(k, it.second) } }
.filterNotNull().associateBy { it.criterion.id }
val global = forSt.firstOrNull { it.first == assignment.globalCriterion.id }?.second
val byCrit_ = forSt
.filter { it.first != assignment.globalCriterion.id }
.map { LocalCriterionFeedback(it.first, it.second) }
.associateBy { it.criterion.id }
val byCrit = allCrit.map { c ->
byCrit_[c.id] ?: LocalCriterionFeedback(c, null)
}
@ -556,7 +562,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
it[groupId] = group.id
it[this.feedback] = msg
it[this.grade] = grd
it[criterionId] = criterion?.id
it[criterionId] = criterion?.id ?: assignment.globalCriterion.id
}
}
feedback.refresh(); autofill.refresh()
@ -570,7 +576,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
it[studentId] = student.id
it[this.feedback] = msg
it[this.grade] = grd
it[criterionId] = criterion?.id
it[criterionId] = criterion?.id ?: assignment.globalCriterion.id
}
}
feedback.refresh(); autofill.refresh()
@ -628,7 +634,7 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
private val _task = mutableStateOf(assignment.assignment); val task = _task.immutable()
private val _deadline = mutableStateOf(assignment.deadline); val deadline = _deadline.immutable()
val criteria = RawDbState {
assignment.criteria.orderBy(SoloAssignmentCriteria.name to SortOrder.ASC).toList()
assignment.criteria.orderBy(SoloAssignmentCriteria.name to SortOrder.ASC).filter { it.id != assignment.globalCriterion.id }
}
val feedback = RawDbState { loadFeedback() }
@ -641,22 +647,25 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
private fun Transaction.loadFeedback(): List<Pair<Student, FullFeedback>> {
val allCrit = SoloAssignmentCriterion.find {
SoloAssignmentCriteria.assignmentId eq assignment.id
}
}.filter { it.id != assignment.globalCriterion.id }
return editionCourse.second.soloStudents.sortAsc(Students.name).map { student ->
val forStudent = (IndividualFeedbacks innerJoin Students).selectAll().where {
(IndividualFeedbacks.assignmentId eq assignment.id) and (Students.id eq student.id)
}.map { row ->
val crit = row[IndividualFeedbacks.criterionId]?.let { SoloAssignmentCriterion[it] }
val crit = SoloAssignmentCriterion[row[IndividualFeedbacks.criterionId]]
val fdbk = row[IndividualFeedbacks.feedback]
val grade = row[IndividualFeedbacks.grade]
crit to LocalFeedback(fdbk, grade)
}
val global = forStudent.firstOrNull { it.first == null }?.second
val byCrit_ = forStudent.map { it.first?.let { k -> Pair(k, it.second) } }
.filterNotNull().associateBy { it.first.id }
val global = forStudent.firstOrNull { it.first == assignment.globalCriterion.id }?.second
val byCrit_ = forStudent
.filter { it.first != assignment.globalCriterion.id }
.map { Pair(it.first, it.second) }
.associateBy { it.first.id }
val byCrit = allCrit.map { c ->
byCrit_[c.id] ?: Pair(c, null)
}
@ -672,7 +681,7 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
it[studentId] = student.id
it[this.feedback] = msg ?: ""
it[this.grade] = grd ?: ""
it[criterionId] = criterion?.id
it[criterionId] = criterion?.id ?: assignment.globalCriterion.id
}
}
feedback.refresh(); autofill.refresh()

View File

@ -20,6 +20,7 @@ kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-co
exposed-core = { group = "org.jetbrains.exposed", name = "exposed-core", version.ref = "exposed" }
exposed-dao = { group = "org.jetbrains.exposed", name = "exposed-dao", version.ref = "exposed" }
exposed-jdbc = { group = "org.jetbrains.exposed", name = "exposed-jdbc", version.ref = "exposed" }
exposed-migration = { group = "org.jetbrains.exposed", name = "exposed-migration", version.ref = "exposed" }
exposed-kotlin-datetime = { group = "org.jetbrains.exposed", name = "exposed-kotlin-datetime", version.ref = "exposed" }
sqlite = { group = "org.xerial", name = "sqlite-jdbc", version = "3.34.0" }
sl4j = { group = "org.slf4j", name = "slf4j-simple", version = "2.0.12" }
@ -33,4 +34,4 @@ rtfield = { group = "com.mohamedrejeb.richeditor", name = "richeditor-compose",
[plugins]
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }