Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/pr-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,14 @@ jobs:
mv ./libs/pandautils/build/outputs/apk/androidTest/debug/pandautils-debug-androidTest.apk ./libs/pandautils/pandautils-test.apk
./gradle/gradlew -p apps :pandautils:assembleDebugAndroidTest -DtestApplicationId=com.instructure.pandautils
mv ./libs/pandautils/build/outputs/apk/androidTest/debug/pandautils-debug-androidTest.apk ./libs/pandautils/pandautils-app.apk
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Run submodule unit tests
run: |
./gradle/gradlew -p apps testDebugUnitTest -x :dataseedingapi:test -x :teacher:test -x :student:test
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload submodule test results
if: always()
Expand Down Expand Up @@ -175,6 +179,8 @@ jobs:
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload parent test results
if: always()
Expand Down Expand Up @@ -227,6 +233,8 @@ jobs:
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload student test results
if: always()
Expand Down Expand Up @@ -279,6 +287,8 @@ jobs:
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload teacher test results
if: always()
Expand Down Expand Up @@ -342,6 +352,8 @@ jobs:
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload horizon test results
if: always()
Expand Down Expand Up @@ -405,6 +417,8 @@ jobs:
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
env:
GRADLE_OPTS: "-Djava.net.preferIPv4Stack=true"

- name: Upload Horizon test APK
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,31 @@ object SubmissionDetailsPresenter : Presenter<SubmissionDetailsModel, Submission
.filterNotNull()
.sortedByDescending { it.submittedAt }

val selectedSubmission = validSubmissions.firstOrNull { it.attempt == model.selectedSubmissionAttempt }
// Check if any attempt number is missing or invalid
val hasAnyMissingAttemptNumber = validSubmissions.any { it.attempt == 0L }

// Create submission to attempt number mapping
val submissionToAttemptMap = if (hasAnyMissingAttemptNumber) {
// Re-index from newest (highest) to 1
validSubmissions.mapIndexed { index, submission ->
submission to (validSubmissions.size - index).toLong()
}.toMap()
} else {
// Use original attempt numbers
validSubmissions.associateWith { it.attempt }
}

val selectedSubmission = if (hasAnyMissingAttemptNumber) {
// Find by position in the sorted list
val attemptNumber = model.selectedSubmissionAttempt
validSubmissions.firstOrNull { submissionToAttemptMap[it] == attemptNumber }
} else {
validSubmissions.firstOrNull { it.attempt == model.selectedSubmissionAttempt }
}

val submissionVersions: List<Pair<Long, String>> = validSubmissions.map { submission ->
submission.attempt to submission.submittedAt?.let { it -> getFormattedAttemptDate(it) }.orEmpty()
val attemptNumber = submissionToAttemptMap[submission] ?: submission.attempt
attemptNumber to submission.submittedAt?.let { getFormattedAttemptDate(it) }.orEmpty()
}

val selectedVersionIdx = submissionVersions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,10 @@ class SubmissionDetailsView(
}

private fun setupSubmissionVersionSpinner(submissions: List<Pair<Long, String>>, selectedIdx: Int) {
val itemViewModels = submissions.mapIndexed { index, submission ->
val itemViewModels = submissions.map { submission ->
AssignmentDetailsAttemptItemViewModel(
AssignmentDetailsAttemptViewData(
context.getString(R.string.attempt, submissions.size - index),
context.getString(R.string.attempt, submission.first),
submission.second
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package com.instructure.canvas.espresso.mockcanvas.endpoints

import android.util.Log
import com.instructure.canvas.espresso.mockcanvas.Endpoint
import com.instructure.canvas.espresso.mockcanvas.MockCanvas
import com.instructure.canvas.espresso.mockcanvas.addSubmissionForAssignment
import com.instructure.canvas.espresso.mockcanvas.utils.Segment
import com.instructure.canvas.espresso.mockcanvas.utils.UserId
Expand All @@ -29,8 +30,8 @@ import com.instructure.canvasapi2.models.FileUploadParams
import com.instructure.canvasapi2.models.Submission
import com.instructure.canvasapi2.models.SubmissionComment
import com.instructure.canvasapi2.type.SubmissionType
import com.instructure.pandautils.utils.orDefault
import java.util.Calendar
import kotlin.random.Random

/**
* Submission index for a specific course/assignment
Expand Down Expand Up @@ -70,14 +71,15 @@ object SubmissionIndexEndpoint : Endpoint(
size = file.size
)
}
val attemptCount = MockCanvas.data.submissions[assignment.id]?.size.orDefault() + 1L
val submission = data.addSubmissionForAssignment(
assignmentId = pathVars.assignmentId,
userId = request.user!!.id,
type = submissionType!!,
body = submissionBody,
url = submissionUrl,
attachment = attachment,
attempt = Random.nextLong()
attempt = attemptCount
)

assignment.submission = submission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,15 +447,49 @@ class AssignmentDetailsViewModel @Inject constructor(
val partialLockedMessage = assignment.lockExplanation.takeIf { it.isValid() && assignment.lockDate?.before(Date()).orDefault() }.orEmpty()

val submissionHistory = assignment.submission?.submissionHistory
val attempts = submissionHistory?.reversed()?.mapIndexedNotNull { index, submission ->
submission?.submittedAt?.toFormattedString()?.let {
AssignmentDetailsAttemptItemViewModel(
AssignmentDetailsAttemptViewData(
resources.getString(R.string.attempt, submissionHistory.size - index),
it,
submission
val attempts = submissionHistory?.let { history ->
// Check if any attempt number is missing or invalid
val hasAnyMissingAttemptNumber = history.any { it?.attempt == null || it?.attempt == 0L }

val sortedHistory = if (hasAnyMissingAttemptNumber) {
// Fallback: sort by submittedAt descending
history.sortedByDescending { it?.submittedAt }
} else {
// Normal case: sort by attempt number descending
history.sortedByDescending { it?.attempt }
}

if (hasAnyMissingAttemptNumber) {
// Filter out null submissions and those without submittedAt, then index from newest to 1
val validSubmissions = sortedHistory.mapNotNull { submission ->
submission?.submittedAt?.toFormattedString()?.let { formattedDate ->
submission to formattedDate
}
}

validSubmissions.mapIndexed { index, (submission, formattedDate) ->
val attemptNumber = validSubmissions.size - index
AssignmentDetailsAttemptItemViewModel(
AssignmentDetailsAttemptViewData(
resources.getString(R.string.attempt, attemptNumber),
formattedDate,
submission
)
)
)
}
} else {
// Use original attempt numbers
sortedHistory.mapNotNull { submission ->
submission?.submittedAt?.toFormattedString()?.let { formattedDate ->
AssignmentDetailsAttemptItemViewModel(
AssignmentDetailsAttemptViewData(
resources.getString(R.string.attempt, submission.attempt),
formattedDate,
submission
)
)
}
}
}
}.orEmpty()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,14 +436,14 @@ class AssignmentDetailsViewModelTest {

@Test
fun `Select submission attempt`() {
val firstSubmission = Submission(submittedAt = Date(), grade = "A", postedAt = Date())
val firstSubmission = Submission(submittedAt = Date(), grade = "A", postedAt = Date(), attempt = 1)
val assignment = Assignment(
submission = Submission(
submissionHistory = listOf(
firstSubmission,
Submission(submittedAt = Date(), grade = "B", postedAt = Date()),
Submission(submittedAt = Date(), grade = "C", postedAt = Date()),
Submission(grade = "D"),
Submission(submittedAt = Date(), grade = "B", postedAt = Date(), attempt = 2),
Submission(submittedAt = Date(), grade = "C", postedAt = Date(), attempt = 3),
Submission(grade = "D", attempt = 4),
)
)
)
Expand Down
Loading