Skip to content

Commit

Permalink
Merge branch 'feat/2023-dual-ui' into jkantor/bff-file-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
jansenk committed Oct 13, 2023
2 parents 66a46b8 + d4a1500 commit 0884c57
Show file tree
Hide file tree
Showing 23 changed files with 1,393 additions and 636 deletions.
38 changes: 37 additions & 1 deletion openassessment/assessment/api/staff.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@

from openassessment.assessment.errors import StaffAssessmentInternalError, StaffAssessmentRequestError
from openassessment.assessment.models import Assessment, AssessmentPart, InvalidRubricSelection, StaffWorkflow
from openassessment.assessment.serializers import InvalidRubric, full_assessment_dict, rubric_from_dict
from openassessment.assessment.serializers import (
InvalidRubric,
full_assessment_dict,
rubric_from_dict,
serialize_assessments,
)
from openassessment.assessment.score_type_constants import STAFF_TYPE


Expand Down Expand Up @@ -462,3 +467,34 @@ def bulk_retrieve_workflow_status(course_id, item_id, submission_uuids=None):
return StaffWorkflow.bulk_retrieve_workflow_status(
course_id, item_id, submission_uuids
)


def get_assessment(submission_uuid):
"""
Retrieve a staff-assessment for a submission_uuid.
Args:
submission_uuid (str): The submission UUID for we want information for
regarding staff assessment.
Returns:
assessment (dict) is a serialized Assessment model, or None (if the user has not yet self-assessed)
If multiple submissions or staff-assessments are found, returns the most recent one.
"""
# Retrieve assessments for the submission UUID
# We weakly enforce that number of staff-assessments per submission is <= 1,
# but not at the database level. Someone could take advantage of the race condition
# between checking the number of staff-assessments and creating a new staff-assessment.
# To be safe, we retrieve just the most recent submission.
serialized_assessments = serialize_assessments(Assessment.objects.filter(
score_type=STAFF_TYPE, submission_uuid=submission_uuid
).order_by('-scored_at')[:1])

if not serialized_assessments:
logger.info("No staff-assessment found for submission %s", submission_uuid)
return None

serialized_assessment = serialized_assessments[0]
logger.info("Retrieved staff-assessment for submission %s", submission_uuid)

return serialized_assessment
4 changes: 4 additions & 0 deletions openassessment/xblock/apis/assessments/peer_assessment_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def submission_uuid(self):
def assessment(self):
return self.config_data.get_assessment_module("peer-assessment")

@property
def assessments(self):
return peer_api.get_assessments(self.submission_uuid)

@property
def continue_grading(self):
return self._continue_grading and self.workflow_data.is_peer_complete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def rubric_dict(self):
self.config_data.prompts, self.config_data.rubric_criteria_with_labels
)

@property
def assessment(self):
return staff_api.get_assessment(self.workflow_data.workflow.get("submission_uuid"))

def create_team_assessment(self, data):
team_submission = team_sub_api.get_team_submission_from_individual_submission(
data["submission_uuid"]
Expand Down
12 changes: 12 additions & 0 deletions openassessment/xblock/apis/step_data_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
""" Base class for step data collations """
from openassessment.xblock.apis.workflow_api import WorkflowStep
from openassessment.xblock.utils.resolve_dates import DISTANT_FUTURE


Expand All @@ -10,6 +11,7 @@ def __init__(self, block, step=None):
self._closed_reason = closed_reason
self._start_date = start_date
self._due_date = due_date
self._step = WorkflowStep(step)

def __repr__(self):
return "{0}".format(
Expand All @@ -29,6 +31,16 @@ def config_data(self):
def workflow_data(self):
return self._block.api_data.workflow_data

@property
def has_reached_step(self):
"""Util for determining if we have reached or surpassed this step"""
if self.workflow_data.status == self._step:
return True
step_info = self.workflow_data.status_details.get(str(self._step), {})
if step_info.get("complete"):
return True
return False

@property
def problem_closed(self):
return self._problem_closed
Expand Down
6 changes: 3 additions & 3 deletions openassessment/xblock/apis/submissions/submissions_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import logging
import os
from submissions.api import Submission, SubmissionError, SubmissionRequestError
from openassessment.fileupload.exceptions import FileUploadError

from openassessment.fileupload.exceptions import FileUploadError
from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.apis.submissions.errors import (
DeleteNotAllowed,
EmptySubmissionError,
Expand All @@ -21,9 +22,8 @@
SubmitInternalError,
UnsupportedFileTypeException
)
from openassessment.xblock.utils.validation import validate_submission

from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.utils.validation import validate_submission
from openassessment.xblock.utils.data_conversion import (
format_files_for_submission,
prepare_submission_for_serialization,
Expand Down
64 changes: 64 additions & 0 deletions openassessment/xblock/apis/workflow_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"""


from enum import Enum


class WorkflowAPI:
def __init__(self, block):
self._block = block
Expand Down Expand Up @@ -112,3 +115,64 @@ def get_team_workflow_status_counts(self):

def get_team_workflow_cancellation_info(self, team_submission_uuid):
return self._block.get_team_workflow_cancellation_info(team_submission_uuid)


class WorkflowStep:
"""Utility class for comparing and serializing steps"""

# Store one disambiguated step
canonical_step = None
step_name = None

# Enum of workflow steps, used for canonical mapping of steps
class Step(Enum):
SUBMISSION = "submission"
PEER = "peer"
STUDENT_TRAINING = "training"
STAFF = "staff"
SELF = "self"
AI = "ai"

_assessment_module_mappings = {
"peer-assessment": Step.PEER,
"student-training": Step.STUDENT_TRAINING,
"staff-assessment": Step.STAFF,
"self-assessment": Step.SELF,
}

_workflow_step_mappings = {
"submission": Step.SUBMISSION,
"training": Step.STUDENT_TRAINING,
"peer": Step.PEER,
"self": Step.SELF,
"staff": Step.STAFF,
}

_step_mappings = {**_assessment_module_mappings, **_workflow_step_mappings}

@property
def assessment_module_name(self):
""" Get the assessment module name for the step """
for assessment_step, canonical_step in self._assessment_module_mappings.items():
if canonical_step == self.canonical_step:
return assessment_step
return "unknown"

@property
def workflow_step_name(self):
""" Get the workflow step name for the step """
for workflow_step, canonical_step in self._workflow_step_mappings.items():
if canonical_step == self.canonical_step:
return workflow_step
return "unknown"

def __init__(self, step_name):
# Get the "canonical" step from any representation of the step name
self.step_name = step_name
self.canonical_step = self._step_mappings.get(step_name)

def __eq__(self, __value: object) -> bool:
return self.canonical_step == self._step_mappings.get(__value)

def __repr__(self) -> str:
return str(self.canonical_step)
4 changes: 4 additions & 0 deletions openassessment/xblock/test/test_openassessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def test__create_ui_models(self, xblock):
# always include grade and submission.
# assessments from rubric are loaded into the ui model.
models = xblock._create_ui_models() # pylint: disable=protected-access
StaffAssessmentAPI.staff_assessment_exists = lambda submission_uuid: False
self.assertEqual(len(models), 4)
self.assertEqual(models[0], UI_MODELS["submission"])
self.assertEqual(models[1], dict(
Expand All @@ -165,6 +166,7 @@ def test__create_ui_models__teams_enabled(self, xblock):
# peer and self assessment types are not included in VALID_ASSESSMENT_TYPES_FOR_TEAMS
xblock.teams_enabled = True
models = xblock._create_ui_models() # pylint: disable=protected-access
StaffAssessmentAPI.staff_assessment_exists = lambda submission_uuid: False
self.assertEqual(len(models), 2)
self.assertEqual(models[0], UI_MODELS["submission"])
self.assertEqual(models[1], UI_MODELS["grade"])
Expand Down Expand Up @@ -193,6 +195,7 @@ def test__create_ui_models__no_leaderboard_if_teams_enabled(self, xblock):
xblock.leaderboard_show = 10
xblock.teams_enabled = True
models = xblock._create_ui_models() # pylint: disable=protected-access
StaffAssessmentAPI.staff_assessment_exists = lambda submission_uuid: False
self.assertEqual(len(models), 2)
self.assertEqual(models[0], UI_MODELS["submission"])
self.assertEqual(models[1], UI_MODELS["grade"])
Expand Down Expand Up @@ -581,6 +584,7 @@ def test_assessment_type_without_staff(self, xblock):
@scenario('data/grade_scenario_self_staff_not_required.xml', user_id='Bob')
def test_assessment_type_with_staff_not_required(self, xblock):
xblock.mfe_views_enabled = True
StaffAssessmentAPI.staff_assessment_exists = lambda submission_uuid: False
# Check that staff-assessment is not in assessment_steps
self.assertNotIn('staff-assessment', xblock.assessment_steps)

Expand Down
1 change: 1 addition & 0 deletions openassessment/xblock/ui_mixins/legacy/handlers_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from openassessment.xblock.ui_mixins.legacy.student_training.actions import training_assess
from openassessment.xblock.ui_mixins.legacy.submissions.serializers import SaveFilesDescriptionRequestSerializer
from openassessment.xblock.utils.data_conversion import verify_assessment_parameters
from openassessment.xblock.apis.submissions import submissions_actions

logger = logging.getLogger(__name__)

Expand Down
Loading

0 comments on commit 0884c57

Please sign in to comment.