Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add grading method support #299

Closed
wants to merge 2 commits into from
Closed

Conversation

BryanttV
Copy link

@BryanttV BryanttV commented Nov 2, 2023

Description

This PR adds a new field in the Problem settings for choosing a Grading Method. Currently, the only Grading Method is the Last Score. From now on, the new grading methods allow are:

  1. Last Score (Default): The last score made is taken for grading.
  2. First Score: The first score made is taken for grading.
  3. Highest Score: The highest score made is taken for grading.
  4. Average Score: The average of all scores made is taken for grading.

Dependencies

This PR works with the legacy Problem Editor interface, but if you use the new interface with course-authoring MFE, you need the changes in this PR.

Legacy Interface

image

MFE Interface

image

Testing Instructions

  1. Go to Studio and create a problem component from a Unit.
  2. In component settings you should see a new field: Grading Method. This field is a dropdown list with the Grading Methods mentioned above.
  3. Choose a Grading Method and save changes.
  4. From the LMS answer the problems and check with the different Grading Methods. You can also check from the progress section.

Rescoring

  1. Go to Studio and create a problem component from a Unit. e.g. Advanced > Blank Problem. In the settings you can add this example of a problem:
<problem>
  <h3 class="hd hd-2 problem-header">Multiple Choice</h3>
  <multiplechoiceresponse>
    <choicegroup type="MultipleChoice">
      <choice correct="true">correct</choice>
      <choice correct="false">incorrect 1</choice>
      <choice correct="false">incorrect 2</choice>
    </choicegroup>
  </multiplechoiceresponse>
  
  <h3 class="hd hd-2 problem-header">CheckBox</h3>
  <choiceresponse>
    <checkboxgroup>
      <choice correct="true">correct 1</choice>
      <choice correct="true">correct 2</choice>
      <choice correct="false">incorrect</choice>
    </checkboxgroup>
  </choiceresponse>
  
  <h3 class="hd hd-2 problem-header">Drop-Down List</h3>
  <optionresponse>
    <optioninput options="('resp 1', 'resp 2', 'resp 3')" correct="resp 3"></optioninput>
  </optionresponse>
</problem>
  1. In component settings choose a grading method, e.g. Highest score.
  2. From the LMS answer the problems and check your score.
  3. From Studio edit the correct answers to the problem.
  4. From the LMS as an instructor, make the rescore of the problem in STAFF DEBUG INFO > Rescore Learner's Submission
  5. The final score should have changed depending on the answers.

@BryanttV BryanttV changed the title feat: add grading strategy support [POC] feat: add grading strategy support Nov 3, 2023
xmodule/capa_block.py Outdated Show resolved Hide resolved
@BryanttV BryanttV marked this pull request as ready for review November 15, 2023 16:57
@BryanttV BryanttV requested a review from a team November 15, 2023 16:57
xmodule/capa_block.py Outdated Show resolved Hide resolved
xmodule/capa_block.py Outdated Show resolved Hide resolved
xmodule/capa_block.py Outdated Show resolved Hide resolved
xmodule/capa_block.py Outdated Show resolved Hide resolved
xmodule/capa_block.py Outdated Show resolved Hide resolved
@BryanttV BryanttV changed the title [POC] feat: add grading strategy support feat: add grading strategy support Nov 17, 2023
Comment on lines 1788 to 1807

new_score = self.score_from_lcp(self.lcp)
self.score_history.append(new_score)
grading_strategy_handler = GradingStrategyHandler(
self.grading_strategy,
self.score_history,
self.max_score(),
)
score = grading_strategy_handler.get_score()
self.set_score(score)
Copy link
Collaborator

@mariajgrimaldi mariajgrimaldi Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this to its method? called _set_score or something like it, so we can encapsulate the functionality

Comment on lines 2190 to 2207
grading_strategy_handler = GradingStrategyHandler(
self.grading_strategy,
self.score_history,
self.max_score(),
)
calculated_score = grading_strategy_handler.get_score()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this piece of code to its own method? so we can re-use it elsewhere

Comment on lines 534 to 537
if 'filesubmission' in responder.allowed_inputfields and student_answers is not None:
results = responder.evaluate_answers(student_answers, oldcmap)
else:
results = responder.evaluate_answers(student_answers, oldcmap)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the difference between these two conditions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No difference, conditional would not be necessary

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we update it then? thanks!


# enforce_type is set to False here because this field is saved as a dict in the database.
score = ScoreField(help=_("Dictionary with the current student score"), scope=Scope.user_state, enforce_type=False)
score_history = ListScoreField(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a new field for this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because if it is stored as a List field, when querying the value it will have a dictionary, and it should have an instance of Score

@@ -2206,13 +2266,35 @@ def update_correctness(self):
new_correct_map = self.lcp.get_grade_from_current_answers(None)
self.lcp.correct_map.update(new_correct_map)

def update_correctness_list(self):
"""Updates the correctness map list of the LCP."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain in detail what does this do?

Comment on lines +2277 to +2303
if new_correct_map_list:
self.lcp.correct_map.update(new_correct_map_list[-1])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this part in particular

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After updating each correct map in correct_map_history, take the last correct map in the history and assign it to the correct_map field, to update the field with the current correct map.

Comment on lines 1797 to 1807
self.correct_map_history.append(correct_map.get_dict())

new_score = self.score_from_lcp(self.lcp)
self.score_history.append(new_score)
grading_strategy_handler = GradingStrategyHandler(
self.score,
self.grading_strategy,
self.score_history,
self.max_score(),
)
score = grading_strategy_handler.get_score()
self.set_score(score)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still missing here moving this into its own method.

@BryanttV BryanttV force-pushed the bav/add-grading-strategy branch 4 times, most recently from ec0aaeb to dfe9032 Compare December 11, 2023 20:04
@BryanttV BryanttV force-pushed the bav/add-grading-strategy branch 2 times, most recently from cf084d3 to d3416c9 Compare December 14, 2023 16:45
@BryanttV BryanttV force-pushed the bav/add-grading-strategy branch 2 times, most recently from 3afaeae to ae8a024 Compare January 9, 2024 19:26
@BryanttV BryanttV changed the title feat: add grading strategy support feat: add grading method support Feb 19, 2024
refactor: add grading strategy handler class

feat: show grading strategy in student view

fix: reset score history when reset student attempts

fix: add missing argument in handle_average_attempt and fix typing hint

chore: remove inline comments and print statements

docs: add class and method docstring

feat: add ListScoreField

refactor: update grading strategy handler

feat: consider grading strategy in rescore functionality

feat: reset score history when reset student attempts

feat: add correct_map_history and student_answers_history for rescore functionality

fix: reset correct_map_history and student_answers_history when reset attempts

test: fix unit tests

refactor: address PR review

fix: update correct_map_history from lcp instance for each attempt

refactor: add _set_score and get_rescore methods

chore: rename grading strategies (attempt -> score)

refactor: address PR review

fix: show grading strategy with unlimited attempts

chore: add es_419 translations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants