Merge pull request 'Fix some assesment-related bugs' (#7) from fix/ids_constraints into main
Reviewed-on: jay-tux/grader#7
This commit is contained in:
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -12,11 +12,9 @@ import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.jaytux.grader.data.GroupAssignment
|
||||
import com.jaytux.grader.data.GroupAssignmentCriterion
|
||||
import com.jaytux.grader.data.SoloAssignmentCriterion
|
||||
import com.jaytux.grader.data.Student
|
||||
@ -26,7 +24,6 @@ import com.jaytux.grader.viewmodel.SoloAssignmentState
|
||||
import com.mohamedrejeb.richeditor.model.rememberRichTextState
|
||||
import com.mohamedrejeb.richeditor.ui.material3.OutlinedRichTextEditor
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import org.jetbrains.exposed.sql.transactions.inTopLevelTransaction
|
||||
|
||||
@Composable
|
||||
fun GroupAssignmentView(state: GroupAssignmentState) {
|
||||
@ -126,14 +123,7 @@ fun groupTaskWidget(
|
||||
Row {
|
||||
DateTimePicker(deadline, onSetDeadline)
|
||||
}
|
||||
RichTextStyleRow(state = updTask)
|
||||
OutlinedRichTextEditor(
|
||||
state = updTask,
|
||||
modifier = Modifier.fillMaxWidth().weight(1f),
|
||||
singleLine = false,
|
||||
minLines = 5,
|
||||
label = { Text("Task") }
|
||||
)
|
||||
RichTextField(updTask, Modifier.fillMaxWidth().weight(1f)) { Text("Task") }
|
||||
CancelSaveRow(
|
||||
true,
|
||||
{ updTask.setMarkdown(taskMD) },
|
||||
@ -188,7 +178,7 @@ fun groupTaskWidget(
|
||||
@Composable
|
||||
fun groupFeedback(state: GroupAssignmentState, fdbk: GroupAssignmentState.LocalGFeedback) {
|
||||
val (group, feedback, individual) = fdbk
|
||||
var idx by remember(fdbk) { mutableStateOf(0) }
|
||||
var studentIdx by remember(fdbk) { mutableStateOf(0) }
|
||||
var critIdx by remember(fdbk) { mutableStateOf(0) }
|
||||
val criteria by state.criteria.entities
|
||||
val suggestions by state.autofill.entities
|
||||
@ -198,8 +188,8 @@ fun groupFeedback(state: GroupAssignmentState, fdbk: GroupAssignmentState.LocalG
|
||||
LazyColumn(Modifier.fillMaxHeight().padding(10.dp)) {
|
||||
item {
|
||||
Surface(
|
||||
Modifier.fillMaxWidth().clickable { idx = 0 },
|
||||
tonalElevation = if (idx == 0) 50.dp else 0.dp,
|
||||
Modifier.fillMaxWidth().clickable { studentIdx = 0 },
|
||||
tonalElevation = if (studentIdx == 0) 50.dp else 0.dp,
|
||||
shape = MaterialTheme.shapes.medium
|
||||
) {
|
||||
Text("Group feedback", Modifier.padding(5.dp), fontStyle = FontStyle.Italic)
|
||||
@ -209,8 +199,8 @@ fun groupFeedback(state: GroupAssignmentState, fdbk: GroupAssignmentState.LocalG
|
||||
itemsIndexed(individual.toList()) { i, (student, details) ->
|
||||
val (role, _) = details
|
||||
Surface(
|
||||
Modifier.fillMaxWidth().clickable { idx = i + 1 },
|
||||
tonalElevation = if (idx == i + 1) 50.dp else 0.dp,
|
||||
Modifier.fillMaxWidth().clickable { studentIdx = i + 1 },
|
||||
tonalElevation = if (studentIdx == i + 1) 50.dp else 0.dp,
|
||||
shape = MaterialTheme.shapes.medium
|
||||
) {
|
||||
Text("${student.name} (${role ?: "no role"})", Modifier.padding(5.dp))
|
||||
@ -219,45 +209,25 @@ fun groupFeedback(state: GroupAssignmentState, fdbk: GroupAssignmentState.LocalG
|
||||
}
|
||||
}
|
||||
|
||||
val updateGrade = { grade: String ->
|
||||
if(idx == 0) {
|
||||
state.upsertGroupFeedback(group, feedback.global?.feedback ?: "", grade)
|
||||
}
|
||||
else {
|
||||
val ind = individual[idx - 1]
|
||||
val glob = ind.second.second.global
|
||||
state.upsertIndividualFeedback(ind.first, group, glob?.feedback ?: "", grade)
|
||||
}
|
||||
}
|
||||
|
||||
val updateFeedback = { fdbk: String ->
|
||||
if(idx == 0) {
|
||||
if(critIdx == 0) {
|
||||
state.upsertGroupFeedback(group, fdbk, feedback.global?.grade ?: "", null)
|
||||
}
|
||||
else {
|
||||
val current = feedback.byCriterion[critIdx - 1]
|
||||
state.upsertGroupFeedback(group, fdbk, current.entry?.grade ?: "", current.criterion)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val ind = individual[idx - 1]
|
||||
if(critIdx == 0) {
|
||||
val entry = ind.second.second
|
||||
state.upsertIndividualFeedback(ind.first, group, fdbk, entry.global?.grade ?: "", null)
|
||||
}
|
||||
else {
|
||||
val entry = ind.second.second.byCriterion[critIdx - 1]
|
||||
state.upsertIndividualFeedback(ind.first, group, fdbk, entry.entry?.grade ?: "", entry.criterion)
|
||||
}
|
||||
val onSave = { grade: String, fdbk: String ->
|
||||
when {
|
||||
studentIdx == 0 && critIdx == 0 -> state.upsertGroupFeedback(group, fdbk, grade)
|
||||
studentIdx == 0 && critIdx != 0 -> state.upsertGroupFeedback(group, fdbk, grade, criteria[critIdx - 1])
|
||||
studentIdx != 0 && critIdx == 0 -> state.upsertIndividualFeedback(individual[studentIdx - 1].first, group, fdbk, grade)
|
||||
else -> state.upsertIndividualFeedback(individual[studentIdx - 1].first, group, fdbk, grade, criteria[critIdx - 1])
|
||||
}
|
||||
}
|
||||
|
||||
groupFeedbackPane(
|
||||
criteria, critIdx, { critIdx = it }, feedback.global,
|
||||
if(critIdx == 0) feedback.global else feedback.byCriterion[critIdx - 1].entry,
|
||||
suggestions, updateGrade, updateFeedback, Modifier.weight(0.75f).padding(10.dp),
|
||||
key = idx to critIdx
|
||||
criteria, critIdx, { critIdx = it },
|
||||
when {
|
||||
studentIdx == 0 && critIdx == 0 -> feedback.global
|
||||
studentIdx == 0 && critIdx != 0 -> feedback.byCriterion[critIdx - 1].entry
|
||||
studentIdx != 0 && critIdx == 0 -> individual[studentIdx - 1].second.second.global
|
||||
else -> individual[studentIdx - 1].second.second.byCriterion[critIdx - 1].entry
|
||||
},
|
||||
suggestions, onSave, Modifier.weight(0.75f).padding(10.dp),
|
||||
key = studentIdx to critIdx
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -267,25 +237,27 @@ fun groupFeedbackPane(
|
||||
criteria: List<GroupAssignmentCriterion>,
|
||||
currentCriterion: Int,
|
||||
onSelectCriterion: (Int) -> Unit,
|
||||
globFeedback: GroupAssignmentState.FeedbackEntry?,
|
||||
criterionFeedback: GroupAssignmentState.FeedbackEntry?,
|
||||
rawFeedback: GroupAssignmentState.FeedbackEntry?,
|
||||
autofill: List<String>,
|
||||
onSetGrade: (String) -> Unit,
|
||||
onSetFeedback: (String) -> Unit,
|
||||
onSave: (String, String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
key: Any? = null
|
||||
) {
|
||||
var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") }
|
||||
var feedback by remember(currentCriterion, criteria, criterionFeedback, key) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) }
|
||||
var grade by remember(rawFeedback, key) { mutableStateOf(rawFeedback?.grade ?: "") }
|
||||
val feedback = rememberRichTextState()
|
||||
|
||||
LaunchedEffect(currentCriterion, criteria, rawFeedback, key) {
|
||||
feedback.setMarkdown(rawFeedback?.feedback ?: "")
|
||||
}
|
||||
|
||||
Column(modifier) {
|
||||
Row {
|
||||
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
|
||||
OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f))
|
||||
Spacer(Modifier.weight(0.6f))
|
||||
Button(
|
||||
{ onSetGrade(grade); onSetFeedback(feedback.text) },
|
||||
Modifier.weight(0.2f).align(Alignment.CenterVertically),
|
||||
enabled = grade.isNotBlank() || feedback.text.isNotBlank()
|
||||
{ onSave(grade, feedback.toMarkdown()) },
|
||||
Modifier.weight(0.2f).align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
@ -297,11 +269,7 @@ fun groupFeedbackPane(
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(5.dp))
|
||||
AutocompleteLineField(
|
||||
feedback, { feedback = it }, Modifier.fillMaxWidth().weight(1f), { Text("Feedback") }
|
||||
) { filter ->
|
||||
autofill.filter { x -> x.trim().startsWith(filter.trim()) }
|
||||
}
|
||||
RichTextField(feedback, Modifier.fillMaxWidth().weight(1f)) { Text("Feedback") }
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,16 +448,19 @@ fun soloFeedbackPane(
|
||||
key: Any? = null
|
||||
) {
|
||||
var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") }
|
||||
var feedback by remember(currentCriterion, criteria, key) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) }
|
||||
val feedback = rememberRichTextState()
|
||||
|
||||
LaunchedEffect(currentCriterion, criteria, criterionFeedback, key) {
|
||||
feedback.setMarkdown(criterionFeedback?.feedback ?: "")
|
||||
}
|
||||
Column(modifier) {
|
||||
Row {
|
||||
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
|
||||
OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f))
|
||||
Spacer(Modifier.weight(0.6f))
|
||||
Button(
|
||||
{ onSetGrade(grade); onSetFeedback(feedback.text) },
|
||||
Modifier.weight(0.2f).align(Alignment.CenterVertically),
|
||||
enabled = grade.isNotBlank() || feedback.text.isNotBlank()
|
||||
{ onSetGrade(grade); onSetFeedback(feedback.toMarkdown()) },
|
||||
Modifier.weight(0.2f).align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
@ -501,11 +472,7 @@ fun soloFeedbackPane(
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(5.dp))
|
||||
AutocompleteLineField(
|
||||
feedback, { feedback = it }, Modifier.fillMaxWidth().weight(1f), { Text("Feedback") }
|
||||
) { filter ->
|
||||
autofill.filter { x -> x.trim().startsWith(filter.trim()) }
|
||||
}
|
||||
RichTextField(feedback, Modifier.fillMaxWidth().weight(1f)) { Text("Feedback") }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import androidx.compose.ui.unit.sp
|
||||
import com.jaytux.grader.loadClipboard
|
||||
import com.jaytux.grader.toClipboard
|
||||
import com.mohamedrejeb.richeditor.model.RichTextState
|
||||
import com.mohamedrejeb.richeditor.ui.material.OutlinedRichTextEditor
|
||||
|
||||
@Composable
|
||||
fun RichTextStyleRow(
|
||||
@ -238,3 +239,17 @@ fun RichTextStyleButton(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RichTextField(
|
||||
state: RichTextState,
|
||||
modifier: Modifier = Modifier,
|
||||
buttonsModifier: Modifier = Modifier,
|
||||
outerModifier: Modifier = Modifier,
|
||||
label: @Composable (() -> Unit)? = null
|
||||
) = Column(outerModifier) {
|
||||
RichTextStyleRow(buttonsModifier, state)
|
||||
OutlinedRichTextEditor(
|
||||
state = state, modifier = modifier, singleLine = false, minLines = 5, label = label
|
||||
)
|
||||
}
|
@ -34,6 +34,7 @@ import androidx.compose.ui.window.*
|
||||
import com.jaytux.grader.data.Course
|
||||
import com.jaytux.grader.data.Edition
|
||||
import com.jaytux.grader.viewmodel.PeerEvaluationState
|
||||
import com.mohamedrejeb.richeditor.model.RichTextState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.*
|
||||
@ -211,7 +212,8 @@ fun PaneHeader(name: String, type: String, courseEdition: Pair<Course, Edition>)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AutocompleteLineField(
|
||||
fun AutocompleteLineField__(
|
||||
// state: RichTextState,
|
||||
value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit,
|
||||
modifier: Modifier = Modifier, label: @Composable (() -> Unit)? = null,
|
||||
onFilter: (String) -> List<String>
|
||||
|
@ -171,10 +171,14 @@ class EditionState(val edition: Edition) {
|
||||
|
||||
fun newSoloAssignment(name: String) {
|
||||
transaction {
|
||||
SoloAssignment.new {
|
||||
val assign = SoloAssignment.new {
|
||||
this.name = name; this.edition = this@EditionState.edition; assignment = ""; deadline = now()
|
||||
this.number = nextIdx()
|
||||
}
|
||||
val global = SoloAssignmentCriterion.new {
|
||||
this.name = "_global"; this.description = "[Global] Meta-criterion for $name"; this.assignment = assign
|
||||
}
|
||||
assign.globalCriterion = global
|
||||
solo.refresh()
|
||||
}
|
||||
}
|
||||
@ -186,10 +190,14 @@ class EditionState(val edition: Edition) {
|
||||
}
|
||||
fun newGroupAssignment(name: String) {
|
||||
transaction {
|
||||
GroupAssignment.new {
|
||||
val assign = GroupAssignment.new {
|
||||
this.name = name; this.edition = this@EditionState.edition; assignment = ""; deadline = now()
|
||||
this.number = nextIdx()
|
||||
}
|
||||
val global = GroupAssignmentCriterion.new {
|
||||
this.name = "_global"; this.description = "[Global] Meta-criterion for $name"; this.assignment = assign
|
||||
}
|
||||
assign.globalCriterion = global
|
||||
groupAs.refresh()
|
||||
}
|
||||
}
|
||||
@ -373,12 +381,12 @@ class StudentState(val student: Student, edition: Edition) {
|
||||
|
||||
val asGroup = (GroupAssignments innerJoin GroupAssignmentCriteria innerJoin GroupFeedbacks innerJoin Groups).selectAll().where {
|
||||
(GroupFeedbacks.groupId inList groupsForEdition.keys.toList()) and
|
||||
(GroupAssignmentCriteria.name eq "")
|
||||
(GroupAssignmentCriteria.id eq GroupAssignments.globalCriterion)
|
||||
}.map { it[GroupAssignments.id] to it }
|
||||
|
||||
val asIndividual = (GroupAssignments innerJoin GroupAssignmentCriteria innerJoin IndividualFeedbacks innerJoin Groups).selectAll().where {
|
||||
(IndividualFeedbacks.studentId eq student.id) and
|
||||
(GroupAssignmentCriteria.name eq "")
|
||||
(GroupAssignmentCriteria.id eq GroupAssignments.globalCriterion)
|
||||
}.map { it[GroupAssignments.id] to it }
|
||||
|
||||
val res = mutableMapOf<EntityID<UUID>, LocalGroupGrade>()
|
||||
@ -475,7 +483,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 +502,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 +510,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)
|
||||
}
|
||||
@ -522,21 +533,25 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
|
||||
val student = it.student
|
||||
val role = it.role
|
||||
|
||||
val forSt = (IndividualFeedbacks innerJoin Groups innerJoin GroupStudents)
|
||||
val forSt = (IndividualFeedbacks innerJoin Groups)
|
||||
.selectAll().where {
|
||||
(IndividualFeedbacks.assignmentId eq assignment.id) and
|
||||
(GroupStudents.studentId eq student.id) and (Groups.id eq group.id)
|
||||
(IndividualFeedbacks.studentId eq student.id) and (Groups.id eq group.id)
|
||||
}.map { row ->
|
||||
val crit = row[IndividualFeedbacks.criterionId]?.let { id -> GroupAssignmentCriterion[id] }
|
||||
val stdId = row[IndividualFeedbacks.studentId]
|
||||
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.id == 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 +571,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 +585,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 +643,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() }
|
||||
|
||||
@ -638,25 +653,28 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
|
||||
}.flatten().distinct().sorted()
|
||||
}
|
||||
|
||||
private fun Transaction.loadFeedback(): List<Pair<Student, FullFeedback>> {
|
||||
private fun Transaction.loadFeedback(): List<Pair<Student, FullFeedback>> {3
|
||||
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 +690,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()
|
||||
|
@ -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" }
|
||||
|
Reference in New Issue
Block a user