github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/public/src/overmind/namespaces/review/actions.ts (about)

     1  import { Context } from '../..'
     2  import { GradingBenchmark, GradingCriterion, GradingCriterion_Grade, Review, Submission } from '../../../../proto/qf/types_pb'
     3  import { Color, isAuthor } from '../../../Helpers'
     4  import { SubmissionOwner } from '../../state'
     5  
     6  
     7  /* Set the index of the selected review */
     8  export const setSelectedReview = ({ state }: Context, index: number): void => {
     9      const reviews = state.review.reviews.get(state.selectedSubmission?.ID ?? -1n)
    10      if (index < 0) {
    11          const idx = reviews?.findIndex(r => isAuthor(state.self, r) || state.isCourseCreator)
    12          state.review.selectedReview = idx && idx >= 0 ? idx : 0
    13      } else {
    14          state.review.selectedReview = index
    15      }
    16  }
    17  
    18  /* Update the selected review */
    19  export const updateReview = async ({ state, effects }: Context): Promise<boolean> => {
    20      if (!(state.review.canUpdate && state.review.currentReview)) {
    21          // If canUpdate is false, the review cannot be updated
    22          return false
    23      }
    24      const submissionID = state.selectedSubmission?.ID ?? -1n
    25      const reviews = state.review.reviews.get(submissionID)
    26      if (!reviews) {
    27          // If there are no reviews, the review cannot be updated
    28          return false
    29      }
    30  
    31      const review = state.review.currentReview
    32      const response = await effects.api.client.updateReview({
    33          courseID: state.activeCourse,
    34          review
    35      })
    36      if (response.error) {
    37          return false
    38      }
    39  
    40      const idx = reviews.findIndex(r => r.ID === review.ID)
    41      if (idx === -1) {
    42          // If the review was not found, abort
    43          return false
    44      }
    45      reviews[idx] = response.message
    46  
    47      // Copy the review map and update the review
    48      const reviewMap = new Map(state.review.reviews)
    49      reviewMap.set(submissionID, reviews)
    50      state.review.reviews = reviewMap;
    51  
    52      (state.selectedSubmission as Submission).score = response.message.score
    53      return true
    54  }
    55  
    56  export const updateReady = async ({ state, actions }: Context, ready: boolean): Promise<void> => {
    57      if (state.review.currentReview) {
    58          state.review.currentReview.ready = ready
    59          await actions.review.updateReview()
    60      }
    61  }
    62  
    63  export const updateComment = async ({ actions }: Context, { grade, comment }: { grade: GradingBenchmark | GradingCriterion, comment: string }): Promise<void> => {
    64      const oldComment = grade.comment
    65      grade.comment = comment
    66      const ok = await actions.review.updateReview()
    67      if (!ok) {
    68          grade.comment = oldComment
    69      }
    70  }
    71  
    72  export const updateFeedback = async ({ state, actions }: Context, { feedback }: { feedback: string }): Promise<void> => {
    73      if (state.review.currentReview) {
    74          const oldFeedback = state.review.currentReview.feedback
    75          state.review.currentReview.feedback = feedback
    76          const ok = await actions.review.updateReview()
    77          if (!ok) {
    78              state.review.currentReview.feedback = oldFeedback
    79          }
    80      }
    81  }
    82  
    83  export const setGrade = async ({ actions }: Context, { criterion, grade }: { criterion: GradingCriterion, grade: GradingCriterion_Grade }): Promise<void> => {
    84      const oldGrade = criterion.grade
    85      criterion.grade = grade
    86      const ok = await actions.review.updateReview()
    87      if (!ok) {
    88          criterion.grade = oldGrade
    89      }
    90  }
    91  
    92  /* createReview creates a new review for the current submission and course */
    93  export const createReview = async ({ state, actions, effects }: Context): Promise<void> => {
    94      if (!confirm('Are you sure you want to create a new review?')) {
    95          return
    96      }
    97  
    98      const submission = state.selectedSubmission
    99      // If there is no submission or active course, we cannot create a review
   100      if (submission && state.activeCourse) {
   101          // Set the current user as the reviewer
   102          const review = new Review({
   103              ReviewerID: state.self.ID,
   104              SubmissionID: submission.ID,
   105          })
   106  
   107          const response = await effects.api.client.createReview({
   108              courseID: state.activeCourse,
   109              review,
   110          })
   111          if (response.error) {
   112              return
   113          }
   114          // Adds the new review to the reviews list if the server responded with a review
   115          const reviews = new Map(state.review.reviews)
   116          const length = reviews.get(submission.ID)?.push(response.message) ?? 0
   117          state.review.reviews = reviews
   118          actions.review.setSelectedReview(length - 1)
   119      }
   120  }
   121  
   122  
   123  export const setAssignmentID = ({ state }: Context, aid: bigint): void => {
   124      const id = state.review.assignmentID > 0 ? BigInt(-1) : aid
   125      state.review.assignmentID = id
   126  }
   127  
   128  export const setMinimumScore = ({ state }: Context, minimumScore: number): void => {
   129      state.review.minimumScore = minimumScore
   130  }
   131  
   132  export const releaseAll = async ({ state, actions, effects }: Context, { release, approve }: { release: boolean, approve: boolean }): Promise<void> => {
   133      const assignment = state.assignments[state.activeCourse.toString()].find(a => a.ID === state.review.assignmentID)
   134  
   135      const releaseString = release && approve ? 'release and approve'
   136          : release ? 'release'
   137              : approve ? "approve"
   138                  : ""
   139      const confirmText = `Are you sure you want to ${releaseString} all reviews for ${assignment?.name} above ${state.review.minimumScore} score?`
   140      const invalidMinimumScore = state.review.minimumScore < 0 || state.review.minimumScore > 100
   141  
   142      if (invalidMinimumScore || !confirm(confirmText)) {
   143          invalidMinimumScore && actions.alert({ text: 'Minimum score must be in range [0, 100]', color: Color.YELLOW })
   144          return
   145      }
   146  
   147      const response = await effects.api.client.updateSubmissions({
   148          courseID: state.activeCourse,
   149          assignmentID: state.review.assignmentID,
   150          scoreLimit: state.review.minimumScore,
   151          release,
   152          approve,
   153      })
   154      if (response.error) {
   155          return
   156      }
   157      // Refresh submissions in state for the active course
   158      await actions.refreshCourseSubmissions(state.activeCourse)
   159  }
   160  
   161  export const release = async ({ state, effects }: Context, { submission, owner }: { submission: Submission | null, owner: SubmissionOwner }): Promise<void> => {
   162      if (!submission) {
   163          return
   164      }
   165      const clone = submission.clone()
   166      clone.released = !submission.released
   167      const response = await effects.api.client.updateSubmission({
   168          courseID: state.activeCourse,
   169          submissionID: submission.ID,
   170          status: submission.status,
   171          released: clone.released,
   172          score: submission.score,
   173      })
   174      if (response.error) {
   175          return
   176      }
   177      submission.released = clone.released
   178      state.submissionsForCourse.update(owner, submission)
   179  }