Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-ora2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-ora2
Commits
1747a2ef
Commit
1747a2ef
authored
Apr 18, 2014
by
David Ormsbee
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #261 from edx/ormsbee/admin_screens
A more useful admin interface
parents
7f1bbf0b
37bbf221
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
232 additions
and
25 deletions
+232
-25
apps/openassessment/assessment/admin.py
+119
-13
apps/openassessment/assessment/models.py
+4
-1
apps/openassessment/workflow/admin.py
+10
-1
apps/submissions/admin.py
+89
-9
apps/submissions/models.py
+10
-1
No files found.
apps/openassessment/assessment/admin.py
View file @
1747a2ef
import
json
from
django.contrib
import
admin
from
django.core.urlresolvers
import
reverse
from
django.utils
import
html
from
openassessment.assessment.models
import
(
Assessment
,
AssessmentPart
,
Rubric
,
AssessmentFeedback
,
AssessmentFeedbackOption
,
Criterion
,
CriterionOption
,
PeerWorkflow
,
PeerWorkflowItem
,
Assessment
,
AssessmentFeedback
,
PeerWorkflow
,
PeerWorkflowItem
,
Rubric
)
from
openassessment.assessment.serializers
import
RubricSerializer
class
RubricAdmin
(
admin
.
ModelAdmin
):
list_per_page
=
20
# Loads of criteria summary are moderately expensive
list_display
=
(
'id'
,
'content_hash'
,
'criteria_summary'
)
list_display_links
=
(
'id'
,
'content_hash'
)
search_fields
=
(
'id'
,
'content_hash'
)
readonly_fields
=
(
'id'
,
'content_hash'
,
'points_possible'
,
'criteria_summary'
,
'data'
)
def
criteria_summary
(
self
,
rubric_obj
):
"""Short description of criteria for presenting in a list."""
rubric_data
=
RubricSerializer
.
serialized_from_cache
(
rubric_obj
)
return
u", "
.
join
(
u"{}: {}"
.
format
(
criterion
[
"name"
],
criterion
[
"points_possible"
])
for
criterion
in
rubric_data
[
"criteria"
]
)
def
data
(
self
,
rubric_obj
):
"""Full JSON string of rubric, indented and HTML formatted."""
rubric_data
=
RubricSerializer
.
serialized_from_cache
(
rubric_obj
)
return
u"<pre>
\n
{}
\n
</pre>"
.
format
(
html
.
escape
(
json
.
dumps
(
rubric_data
,
sort_keys
=
True
,
indent
=
4
))
)
data
.
allow_tags
=
True
class
PeerWorkflowItemInline
(
admin
.
StackedInline
):
model
=
PeerWorkflowItem
fk_name
=
'author'
raw_id_fields
=
(
'author'
,
'scorer'
,
'assessment'
)
extra
=
0
class
PeerWorkflowAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'id'
,
'student_id'
,
'item_id'
,
'course_id'
,
'submission_uuid'
,
'created_at'
,
'completed_at'
,
'grading_completed_at'
,
)
search_fields
=
(
'id'
,
'student_id'
,
'item_id'
,
'course_id'
,
'submission_uuid'
,
)
inlines
=
(
PeerWorkflowItemInline
,)
class
AssessmentAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'id'
,
'submission_uuid'
,
'score_type'
,
'scorer_id'
,
'scored_at'
,
'rubric_link'
,
)
search_fields
=
(
'id'
,
'submission_uuid'
,
'score_type'
,
'scorer_id'
,
'scored_at'
,
'rubric__content_hash'
,
)
readonly_fields
=
(
'submission_uuid'
,
'rubric_link'
,
'scored_at'
,
'scorer_id'
,
'score_type'
,
'points_earned'
,
'points_possible'
,
'feedback'
,
'parts_summary'
,
)
exclude
=
(
'rubric'
,
'submission_uuid'
)
def
rubric_link
(
self
,
assessment_obj
):
url
=
reverse
(
'admin:assessment_rubric_change'
,
args
=
[
assessment_obj
.
rubric
.
id
]
)
return
u'<a href="{}">{}</a>'
.
format
(
url
,
assessment_obj
.
rubric
.
content_hash
)
rubric_link
.
allow_tags
=
True
rubric_link
.
admin_order_field
=
'rubric__content_hash'
rubric_link
.
short_description
=
'Rubric'
def
parts_summary
(
self
,
assessment_obj
):
return
"<br/>"
.
join
(
html
.
escape
(
u"{}/{} - {}: {}"
.
format
(
part
.
points_earned
,
part
.
points_possible
,
part
.
option
.
criterion
.
name
,
part
.
option
.
name
,
)
)
for
part
in
assessment_obj
.
parts
.
all
()
)
parts_summary
.
allow_tags
=
True
class
AssessmentFeedbackAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'id'
,
'submission_uuid'
,)
search_fields
=
(
'id'
,
'submission_uuid'
,)
readonly_fields
=
(
'submission_uuid'
,
'assessments_by'
,
'options'
,
'feedback_text'
)
exclude
=
(
'assessments'
,)
def
assessments_by
(
self
,
assessment_feedback
):
links
=
[
u'<a href="{}">{}</a>'
.
format
(
reverse
(
'admin:assessment_assessment_change'
,
args
=
[
asmt
.
id
]),
html
.
escape
(
asmt
.
scorer_id
)
)
for
asmt
in
assessment_feedback
.
assessments
.
all
()
]
return
", "
.
join
(
links
)
assessments_by
.
allow_tags
=
True
admin
.
site
.
register
(
Assessment
)
admin
.
site
.
register
(
AssessmentPart
)
admin
.
site
.
register
(
AssessmentFeedback
)
admin
.
site
.
register
(
AssessmentFeedbackOption
)
admin
.
site
.
register
(
Rubric
)
admin
.
site
.
register
(
Criterion
)
admin
.
site
.
register
(
CriterionOption
)
admin
.
site
.
register
(
PeerWorkflow
)
admin
.
site
.
register
(
PeerWorkflowItem
)
admin
.
site
.
register
(
Rubric
,
RubricAdmin
)
admin
.
site
.
register
(
PeerWorkflow
,
PeerWorkflowAdmin
)
admin
.
site
.
register
(
Assessment
,
AssessmentAdmin
)
admin
.
site
.
register
(
AssessmentFeedback
,
AssessmentFeedbackAdmin
)
apps/openassessment/assessment/models.py
View file @
1747a2ef
...
...
@@ -237,7 +237,7 @@ class Assessment(models.Model):
objects that map to each :class:`Criterion` in the :class:`Rubric` we're
assessing against.
"""
MAXSIZE
=
1024
*
100
# 100KB
MAXSIZE
=
1024
*
100
# 100KB
submission_uuid
=
models
.
CharField
(
max_length
=
128
,
db_index
=
True
)
rubric
=
models
.
ForeignKey
(
Rubric
)
...
...
@@ -433,6 +433,9 @@ class AssessmentFeedbackOption(models.Model):
"""
text
=
models
.
CharField
(
max_length
=
255
,
unique
=
True
)
def
__unicode__
(
self
):
return
u'"{}"'
.
format
(
self
.
text
)
class
AssessmentFeedback
(
models
.
Model
):
"""
...
...
apps/openassessment/workflow/admin.py
View file @
1747a2ef
...
...
@@ -3,8 +3,17 @@ from django.contrib import admin
from
.models
import
AssessmentWorkflow
class
AssessmentWorkflowAdmin
(
admin
.
ModelAdmin
):
"""Admin for the user's overall workflow through open assessment.
Unlike many of the other models, we allow editing here. This is so that we
can manually move a user's entry to "done" and give them a separate score
in the submissions app if that's required. Unlike rubrics and assessments,
there is no expectation of immutability for `AssessmentWorkflow`.
"""
list_display
=
(
'uuid'
,
'status'
,
's
tatus_changed'
,
'submission_uuid'
,
'score
'
'uuid'
,
'status'
,
's
ubmission_uuid'
,
'course_id'
,
'item_id'
,
'status_changed
'
)
list_filter
=
(
'status'
,)
search_fields
=
(
'uuid'
,
'submission_uuid'
,
'course_id'
,
'item_id'
)
admin
.
site
.
register
(
AssessmentWorkflow
,
AssessmentWorkflowAdmin
)
apps/submissions/admin.py
View file @
1747a2ef
from
django.contrib
import
admin
from
django.core.urlresolvers
import
reverse
from
django.utils
import
html
from
submissions.models
import
Score
,
StudentItem
,
Submission
class
SubmissionAdmin
(
admin
.
ModelAdmin
):
class
StudentItemAdminMixin
(
object
):
"""Mix this class into anything that has a student_item fkey."""
search_fields
=
(
'student_item__course_id'
,
'student_item__student_id'
,
'student_item__item_id'
,
'student_item__id'
)
def
course_id
(
self
,
obj
):
return
obj
.
student_item
.
course_id
course_id
.
admin_order_field
=
'student_item__course_id'
def
item_id
(
self
,
obj
):
return
obj
.
student_item
.
item_id
item_id
.
admin_order_field
=
'student_item__item_id'
def
student_id
(
self
,
obj
):
return
obj
.
student_item
.
student_id
student_id
.
admin_order_field
=
'student_item__student_id'
def
student_item_id
(
self
,
obj
):
url
=
reverse
(
'admin:submissions_studentitem_change'
,
args
=
[
obj
.
student_item
.
id
]
)
return
u'<a href="{}">{}</a>'
.
format
(
url
,
obj
.
student_item
.
id
)
student_item_id
.
allow_tags
=
True
student_item_id
.
admin_order_field
=
'student_item__id'
student_item_id
.
short_description
=
'S.I. ID'
class
StudentItemAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'id'
,
'course_id'
,
'item_type'
,
'item_id'
,
'student_id'
)
list_filter
=
(
'item_type'
,)
search_fields
=
(
'id'
,
'course_id'
,
'item_type'
,
'item_id'
,
'student_id'
)
readonly_fields
=
(
'course_id'
,
'item_type'
,
'item_id'
,
'student_id'
)
class
SubmissionAdmin
(
admin
.
ModelAdmin
,
StudentItemAdminMixin
):
list_display
=
(
'student_item'
,
'uuid'
,
'attempt_number'
,
'submitted_at'
,
'created_at'
,
'raw_answer'
,
'scores'
'id'
,
'uuid'
,
'course_id'
,
'item_id'
,
'student_id'
,
'student_item_id'
,
'attempt_number'
,
'submitted_at'
,
)
list_display_links
=
(
'id'
,
'uuid'
)
list_filter
=
(
'student_item__item_type'
,)
readonly_fields
=
(
'student_item_id'
,
'course_id'
,
'item_id'
,
'student_id'
,
'attempt_number'
,
'submitted_at'
,
'created_at'
,
'raw_answer'
,
'all_scores'
,
)
search_fields
=
(
'id'
,
'uuid'
)
+
StudentItemAdminMixin
.
search_fields
# We're creating our own explicit link and displaying parts of the
# student_item in separate fields -- no need to display this as well.
exclude
=
(
'student_item'
,)
def
scores
(
self
,
obj
):
return
", "
.
join
(
"{}/{}"
.
format
(
score
.
points_earned
,
score
.
points_possible
)
for
score
in
Score
.
objects
.
filter
(
submission
=
obj
.
id
)
def
all_scores
(
self
,
submission
):
return
"
\n
"
.
join
(
"{}/{} - {}"
.
format
(
score
.
points_earned
,
score
.
points_possible
,
score
.
created_at
)
for
score
in
Score
.
objects
.
filter
(
submission
=
submission
)
)
admin
.
site
.
register
(
Score
)
admin
.
site
.
register
(
StudentItem
)
class
ScoreAdmin
(
admin
.
ModelAdmin
,
StudentItemAdminMixin
):
list_display
=
(
'id'
,
'course_id'
,
'item_id'
,
'student_id'
,
'student_item_id'
,
'points'
,
'created_at'
)
list_filter
=
(
'student_item__item_type'
,)
readonly_fields
=
(
'student_item_id'
,
'student_item'
,
'submission'
,
'points_earned'
,
'points_possible'
,
'reset'
,
)
search_fields
=
(
'id'
,
)
+
StudentItemAdminMixin
.
search_fields
def
points
(
self
,
score
):
return
u"{}/{}"
.
format
(
score
.
points_earned
,
score
.
points_possible
)
admin
.
site
.
register
(
Score
,
ScoreAdmin
)
admin
.
site
.
register
(
StudentItem
,
StudentItemAdmin
)
admin
.
site
.
register
(
Submission
,
SubmissionAdmin
)
apps/submissions/models.py
View file @
1747a2ef
...
...
@@ -100,12 +100,21 @@ class Submission(models.Model):
raw_answer
=
self
.
raw_answer
,
))
def
__unicode__
(
self
):
return
u"Submission {}"
.
format
(
self
.
uuid
)
class
Meta
:
ordering
=
[
"-submitted_at"
,
"-id"
]
class
Score
(
models
.
Model
):
"""What the user scored for a given StudentItem at a given time."""
"""What the user scored for a given StudentItem at a given time.
Note that while a Score *can* be tied to a Submission, it doesn't *have* to.
Specifically, if we want to have scores for things that are not a part of
the courseware (like "class participation"), there would be no corresponding
Submission.
"""
student_item
=
models
.
ForeignKey
(
StudentItem
)
submission
=
models
.
ForeignKey
(
Submission
,
null
=
True
)
points_earned
=
models
.
PositiveIntegerField
(
default
=
0
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment