Skip to content

Commit

Permalink
[ACCEL] Add to ora2 return assessment flow (#5)
Browse files Browse the repository at this point in the history
* Change username to email in the staff info panel

* Fix error in studio

* Add front part

* First working version

* Add styles and remove unnecessary lines

* Add russian translations for returning flow

* Fix PR mistakes

* Fix problems with username in studio and staff grade form

* Fix PR mistake
  • Loading branch information
arsentur authored and flying-pi committed Jun 19, 2019
1 parent 252d2ee commit 5d41a72
Show file tree
Hide file tree
Showing 20 changed files with 734 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2019-06-18 09:39
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('assessment', '0003_expand_course_id'),
]

operations = [
migrations.AddField(
model_name='staffworkflow',
name='returned_at',
field=models.DateTimeField(db_index=True, null=True),
),
]
3 changes: 3 additions & 0 deletions openassessment/assessment/models/staff.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class StaffWorkflow(models.Model):
grading_started_at = models.DateTimeField(null=True, db_index=True)
cancelled_at = models.DateTimeField(null=True, db_index=True)
assessment = models.CharField(max_length=128, db_index=True, null=True)
returned_at = models.DateTimeField(null=True, db_index=True)

class Meta:
ordering = ["created_at", "id"]
Expand Down Expand Up @@ -110,6 +111,7 @@ def get_submission_for_review(cls, course_id, item_id, scorer_id):
scorer_id=scorer_id,
grading_completed_at=None,
cancelled_at=None,
returned_at=None,
)
# If no existing submissions exist, then get any other
# available workflows.
Expand All @@ -120,6 +122,7 @@ def get_submission_for_review(cls, course_id, item_id, scorer_id):
item_id=item_id,
grading_completed_at=None,
cancelled_at=None,
returned_at=None,
)
if not staff_workflows:
return None
Expand Down
Binary file modified openassessment/locale/ru/LC_MESSAGES/django.mo
Binary file not shown.
129 changes: 102 additions & 27 deletions openassessment/locale/ru/LC_MESSAGES/django.po

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
{% load tz %}
{% load i18n %}
{% spaceless %}
{% block list_item %}
<li class="openassessment__steps__step step--response is--returned is--showing ui-slidable__container"
tabindex="-1">
{% endblock %}

<header class="step__header ui-slidable__control">
<span>
{% block button %}
<button class="ui-slidable" aria-expanded="true" id="oa_response_{{ xblock_id }}" aria-controls="oa_response_{{ xblock_id }}_content" aria-labeledby="oa_step_title_response">
<span class="icon fa fa-caret-right" aria-hidden="false"/>
</button>
{% endblock %}
</span>

<span>
<h4 class="step__title">
<span class="step__counter"></span>
<span class="wrapper--copy">
<span id="oa_step_title_response" class="step__label" aria-describedby="oa_step_status_response oa_step_deadline_response">{% trans "Your Response" %}</span>
</span>
</h4>
</span>

{% if submission_start %}
<span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=submission_start|timezone:"UTC"|date:"c" time_until=submission_start|timeuntil %}
<span id="oa_step_deadline_response" class="date ora-datetime" data-datetime="{{ start_date }}" data-string="available {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span>
{% elif submission_due %}
<span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=submission_due|timezone:"UTC"|date:"c" time_until=submission_due|timeuntil %}
<span id="oa_step_deadline_response" class="date ora-datetime" data-datetime="{{ due_date }}" data-string="due {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span>
{% endif %}
{% block title %}
<span class="step__status">
<span id="oa_step_status_response" class="step__status__value">
<span class="copy">{% trans "Returned" %}</span>
</span>
</span>
{% endblock %}
</header>

{% block body %}
<div class="ui-slidable__content" aria-labelledby="oa_response_{{ xblock_id }}" id="oa_response_{{ xblock_id }}_content">
<div class="wrapper--step__content">
<div class="step__instruction">
<p>
{% trans "Enter your response to the prompt." %}
{% if submission_due %}
{% trans "You can save your progress and return to complete your response at any time before the due date" %}
(<span class="step__deadline">
<span id="oa_step_deadline_response" class="date ora-datetime" data-datetime="{{ submission_due|timezone:'UTC'|date:'c' }}" data-timezone="{{ user_timezone }}" data-format="longDateTime" data-language="{{ user_language }}"></span>
</span>)
{% else %}
{% trans "You can save your progress and return to complete your response at any time." %}
{% endif %}
<strong class="emphasis">{% trans "After you submit your response, you cannot edit it" %}</strong>.
</p>
</div>

<!-- Return message -->
<div class="step__message message message--incomplete">
<h5 class="message__title">{% trans "Status" %}</h5>

<div class="message__content">
{% trans "Your submission was returned. " %}
<p>
{% if workflow_returning.returned_by %}
{% blocktrans with returned_datetime=workflow_returning.returned_at|timezone:"UTC"|date:"Y-m-d H:i e" returned_by_username=workflow_returning.returned_by %}
Your submission has been returned by {{ returned_by_username }} on {{ returned_datetime }}
{% endblocktrans %}
{% else %}
{% blocktrans with returned_datetime=workflow_returning.returned_at|timezone:"UTC"|date:"Y-m-d H:i e" %}
Your submission was returned on {{ returned_datetime }}
{% endblocktrans %}
{% endif %}
</p>
<p>
<!-- Comments: Reason for Returning -->
{% blocktrans with comments=workflow_returning.comments %}
Comments: {{ comments }}
{% endblocktrans %}
</p>
</div>
</div>

<div class="step__content">
<form class="response__submission">
<ol class="list list--fields response__submission__content">
{% for part in saved_response.answer.parts %}
<li class="submission__answer__part">
<h5 class="submission__answer__part__text__title">{% trans "The prompt for this section" %}</h5>
<article class="submission__answer__part__prompt">
<div class="submission__answer__part__prompt__copy">
{% if prompts_type == 'html' %}
{{ part.prompt.description|safe }}
{% else %}
{{ part.prompt.description|linebreaks }}
{% endif %}
</div>
</article>


{% if text_response %}
<div class="field field--textarea submission__answer__part__text">
<div class="submission__answer__part__text">
<h5 id="submission__answer__part__text__title__{{ forloop.counter }}__{{ xblock_id }}"
class="submission__answer__part__text__title">
{% if text_response == "required" %}
{% trans "Your response (required)" %}
{% elif text_response == "optional" %}
{% trans "Your response (optional)" %}
{% endif %}
</h5>
</div>
<textarea
id="submission__answer__part__text__{{ forloop.counter }}__{{ xblock_id }}"
class="submission__answer__part__text__value"
data-preview="submission__{{ forloop.counter }}"
aria-labelledby="submission__answer__part__text__title__{{ forloop.counter }}__{{ xblock_id }}"
aria-describedby="submission__answer__tip__{{ xblock_id }}"
placeholder="{% trans "Enter your response to the prompt above." %}"
maxlength="100000"
>{{ part.text }}</textarea>
</div>
{% with forloop.counter|stringformat:"s" as submission_num %}
{% include "openassessmentblock/oa_latex_preview.html" with id="submission__"|add:xblock_id|add:submission_num elem="div" preview_name="submission__"|add:submission_num %}
{% endwith %}
{% endif %}
</li>
{% endfor %}
{% if text_response %}
<li class="field">
<div class="response__submission__actions">
<div class="message message--inline message--error message--error-server" tabindex="-1">
<h5 class="message__title">{% trans "We could not save your progress" %}</h5>
<div class="message__content"></div>
</div>

<ul class="list list--actions">
<li class="list--actions__item">
<button type="submit" class="action action--save submission__save" aria-describedby="response__save_status__{{ xblock_id }}" disabled>
{% trans "Save your progress" %}
</button>

<div id="response__save_status__{{ xblock_id }}" class="save__submission__label response__submission__label">
<span class="sr">{% trans "Your Submission Status" %}:</span>
{{ save_status }}
</div>
</li>
</ul>
</div>
</li>
{% endif %}
{% if file_upload_type %}
<li class="field">
<div class="upload__error">
<div class="message message--inline message--error message--error-server" tabindex="-1">
<h5 class="message__title">{% trans "We could not upload files" %}</h5>
<div class="message__content"></div>
</div>
</div>
<label class="sr" for="submission_answer_upload_{{ xblock_id }}">{% trans "Select a file to upload for this submission." %}</label>
<input type="file" class="submission__answer__upload file--upload" id="submission_answer_upload_{{ xblock_id }}" multiple="">
<button type="submit" class="file__upload action action--upload" disabled>{% trans "Upload files" %}</button>
<div class="files__descriptions"></div>
</li>
{% endif %}
<li class="field">
{% include "openassessmentblock/oa_uploaded_file.html" with file_upload_type=file_upload_type file_urls=file_urls class_prefix="submission__answer" including_template="response" xblock_id=xblock_id %}
</li>
</ol>

<span class="tip" id="submission__answer__tip__{{ xblock_id }}">{% trans "You may continue to work on your response until you submit it." %}</span>

</form>
</div>

<div class="step__actions">
<div class="message message--inline message--error message--error-server" tabindex="-1">
<h5 class="message__title">{% trans "We could not submit your response" %}</h5>
<div class="message__content"></div>
</div>

<ul class="list list--actions">
<li class="list--actions__item">
<button type="submit" class="action action--submit step--response__submit"
text_response="{{text_response}}"
file_upload_response="{{file_upload_response}}"
{{submit_enabled|yesno:",disabled" }}>
{% trans "Submit your response and move to the next step" %}
</button>
</li>
</ul>
</div>
</div>
</div>
{% endblock %}
</li>
{% endspaceless %}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ <h5 class="message__title">{% trans "We could not submit your assessment" %}</h5
</div>

<ul class="list list--actions">

<li class="list--actions__item submit_assessment--action">
<button type="submit" class="action action--return_back" disabled>
{% trans "Return back" %}
</button>
</li>

<li class="list--actions__item submit_assessment--action">
<button type="submit" class="action action--submit" disabled>
{% trans "Submit assessment" %}
Expand Down
40 changes: 38 additions & 2 deletions openassessment/workflow/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@

from .errors import (AssessmentWorkflowError, AssessmentWorkflowInternalError, AssessmentWorkflowNotFoundError,
AssessmentWorkflowRequestError)
from .models import AssessmentWorkflow, AssessmentWorkflowCancellation
from .serializers import AssessmentWorkflowCancellationSerializer, AssessmentWorkflowSerializer
from .models import AssessmentWorkflow, AssessmentWorkflowCancellation, AssessmentWorkflowReturning
from .serializers import (
AssessmentWorkflowCancellationSerializer,
AssessmentWorkflowSerializer,
AssessmentWorkflowReturningSerializer,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -396,6 +400,21 @@ def cancel_workflow(submission_uuid, comments, cancelled_by_id, assessment_requi
AssessmentWorkflow.cancel_workflow(submission_uuid, comments, cancelled_by_id, assessment_requirements)


def return_workflow(submission_uuid, comments, returned_by_id):
"""
Add an entry in AssessmentWorkflowReturning table for a AssessmentWorkflow.
AssessmentWorkflow which has been returned is no longer included in the
peer grading pool.
Args:
submission_uuid (str): The UUID of the workflow's submission.
comments (str): The reason for returning.
returned_by_id (str): The ID of the user who returned the peer workflow.
"""
AssessmentWorkflow.return_workflow(submission_uuid, comments, returned_by_id)


def get_assessment_workflow_cancellation(submission_uuid):
"""
Get cancellation information for an assessment workflow.
Expand All @@ -413,6 +432,23 @@ def get_assessment_workflow_cancellation(submission_uuid):
raise PeerAssessmentInternalError(error_message)


def get_assessment_workflow_returning(submission_uuid):
"""
Get returning information for an assessment workflow.
Args:
submission_uuid (str): The UUID of the submission.
"""
try:
workflow_returning = AssessmentWorkflowReturning.get_latest_workflow_returning(submission_uuid)
return AssessmentWorkflowReturningSerializer(workflow_returning).data if workflow_returning else None
except DatabaseError:
error_message = u"Error finding assessment workflow returning for submission UUID {}."\
.format(submission_uuid)
logger.exception(error_message)
raise PeerAssessmentInternalError(error_message)


def is_workflow_cancelled(submission_uuid):
"""
Check if assessment workflow is cancelled?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2019-06-10 15:13
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('workflow', '0002_remove_django_extensions'),
]

operations = [
migrations.CreateModel(
name='AssessmentWorkflowReturning',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('comments', models.TextField(max_length=10000)),
('returned_by_id', models.CharField(db_index=True, max_length=40)),
('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='returning', to='workflow.AssessmentWorkflow')),
],
options={
'ordering': ['created_at', 'id'],
},
),
]
Loading

0 comments on commit 5d41a72

Please sign in to comment.