Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-submissions
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-submissions
Commits
8ab8ca2b
Commit
8ab8ca2b
authored
Jul 10, 2014
by
Will Daly
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add read replica option to some accessor API calls
parent
0dfd2065
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
105 additions
and
13 deletions
+105
-13
.gitignore
+1
-0
manage.py
+3
-0
settings.py
+6
-5
submissions/api.py
+53
-8
submissions/tests/test_read_replica.py
+42
-0
No files found.
.gitignore
View file @
8ab8ca2b
...
...
@@ -33,6 +33,7 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
submissions_test_db
# Translations
*.mo
...
...
manage.py
View file @
8ab8ca2b
...
...
@@ -7,5 +7,8 @@ if __name__ == "__main__":
if
os
.
environ
.
get
(
'DJANGO_SETTINGS_MODULE'
)
is
None
:
os
.
environ
[
'DJANGO_SETTINGS_MODULE'
]
=
'settings'
if
'test'
in
sys
.
argv
[:
3
]:
sys
.
argv
.
append
(
'--noinput'
)
from
django.core.management
import
execute_from_command_line
execute_from_command_line
(
sys
.
argv
)
settings.py
View file @
8ab8ca2b
...
...
@@ -8,11 +8,12 @@ TEMPLATE_DEBUG = DEBUG
DATABASES
=
{
'default'
:
{
'ENGINE'
:
'django.db.backends.sqlite3'
,
'NAME'
:
'db'
,
'USER'
:
''
,
'PASSWORD'
:
''
,
'HOST'
:
''
,
'PORT'
:
''
,
'TEST_NAME'
:
'submissions_test_db'
,
},
'read_replica'
:
{
'ENGINE'
:
'django.db.backends.sqlite3'
,
'TEST_MIRROR'
:
'default'
}
}
...
...
submissions/api.py
View file @
8ab8ca2b
...
...
@@ -6,6 +6,7 @@ import copy
import
logging
import
json
from
django.conf
import
settings
from
django.core.cache
import
cache
from
django.db
import
IntegrityError
,
DatabaseError
from
dogapi
import
dog_stats_api
...
...
@@ -161,12 +162,16 @@ def create_submission(student_item_dict, answer, submitted_at=None, attempt_numb
raise
SubmissionInternalError
(
error_message
)
def
get_submission
(
submission_uuid
):
def
get_submission
(
submission_uuid
,
read_replica
=
False
):
"""Retrieves a single submission by uuid.
Args:
submission_uuid (str): Identifier for the submission.
Kwargs:
read_replica (bool): If true, attempt to use the read replica database.
If no read replica is available, use the default database.
Raises:
SubmissionNotFoundError: Raised if the submission does not exist.
SubmissionRequestError: Raised if the search parameter is not a string.
...
...
@@ -202,7 +207,11 @@ def get_submission(submission_uuid):
return
cached_submission_data
try
:
submission
=
Submission
.
objects
.
get
(
uuid
=
submission_uuid
)
submission_qs
=
Submission
.
objects
if
read_replica
:
submission_qs
=
_use_read_replica
(
submission_qs
)
submission
=
submission_qs
.
get
(
uuid
=
submission_uuid
)
submission_data
=
SubmissionSerializer
(
submission
)
.
data
cache
.
set
(
cache_key
,
submission_data
)
except
Submission
.
DoesNotExist
:
...
...
@@ -220,13 +229,17 @@ def get_submission(submission_uuid):
return
submission_data
def
get_submission_and_student
(
uuid
):
def
get_submission_and_student
(
uuid
,
read_replica
=
False
):
"""
Retrieve a submission by its unique identifier, including the associated student item.
Args:
uuid (str): the unique identifier of the submission.
Kwargs:
read_replica (bool): If true, attempt to use the read replica database.
If no read replica is available, use the default database.
Returns:
Serialized Submission model (dict) containing a serialized StudentItem model
...
...
@@ -237,7 +250,7 @@ def get_submission_and_student(uuid):
"""
# This may raise API exceptions
submission
=
get_submission
(
uuid
)
submission
=
get_submission
(
uuid
,
read_replica
=
read_replica
)
# Retrieve the student item from the cache
cache_key
=
"submissions.student_item.{}"
.
format
(
submission
[
'student_item'
])
...
...
@@ -254,7 +267,11 @@ def get_submission_and_student(uuid):
else
:
# There is probably a more idiomatic way to do this using the Django REST framework
try
:
student_item
=
StudentItem
.
objects
.
get
(
id
=
submission
[
'student_item'
])
student_item_qs
=
StudentItem
.
objects
if
read_replica
:
student_item_qs
=
_use_read_replica
(
student_item_qs
)
student_item
=
student_item_qs
.
get
(
id
=
submission
[
'student_item'
])
submission
[
'student_item'
]
=
StudentItemSerializer
(
student_item
)
.
data
cache
.
set
(
cache_key
,
submission
[
'student_item'
])
except
Exception
as
ex
:
...
...
@@ -425,21 +442,30 @@ def get_scores(course_id, student_id):
return
scores
def
get_latest_score_for_submission
(
submission_uuid
):
def
get_latest_score_for_submission
(
submission_uuid
,
read_replica
=
False
):
"""
Retrieve the latest score for a particular submission.
Args:
submission_uuid (str): The UUID of the submission to retrieve.
Kwargs:
read_replica (bool): If true, attempt to use the read replica database.
If no read replica is available, use the default database.
Returns:
dict: The serialized score model, or None if no score is available.
"""
try
:
score
=
Score
.
objects
.
filter
(
score
_qs
=
Score
.
objects
.
filter
(
submission__uuid
=
submission_uuid
)
.
order_by
(
"-id"
)
.
select_related
(
"submission"
)[
0
]
)
.
order_by
(
"-id"
)
.
select_related
(
"submission"
)
if
read_replica
:
score_qs
=
_use_read_replica
(
score_qs
)
score
=
score_qs
[
0
]
if
score
.
is_hidden
():
return
None
except
IndexError
:
...
...
@@ -686,3 +712,21 @@ def _get_or_create_student_item(student_item_dict):
student_item_dict
)
logger
.
exception
(
error_message
)
raise
SubmissionInternalError
(
error_message
)
def
_use_read_replica
(
queryset
):
"""
Use the read replica if it's available.
Args:
queryset (QuerySet)
Returns:
QuerySet
"""
return
(
queryset
.
using
(
"read_replica"
)
if
"read_replica"
in
settings
.
DATABASES
else
queryset
)
\ No newline at end of file
submissions/tests/test_read_replica.py
0 → 100644
View file @
8ab8ca2b
"""
Test API calls using the read replica.
"""
import
copy
from
django.test
import
TransactionTestCase
from
submissions
import
api
as
sub_api
class
ReadReplicaTest
(
TransactionTestCase
):
""" Test queries that use the read replica. """
STUDENT_ITEM
=
{
"student_id"
:
"test student"
,
"course_id"
:
"test course"
,
"item_id"
:
"test item"
,
"item_type"
:
"test type"
}
SCORE
=
{
"points_earned"
:
3
,
"points_possible"
:
5
}
def
setUp
(
self
):
""" Create a submission and score. """
self
.
submission
=
sub_api
.
create_submission
(
self
.
STUDENT_ITEM
,
"test answer"
)
self
.
score
=
sub_api
.
set_score
(
self
.
submission
[
'uuid'
],
self
.
SCORE
[
"points_earned"
],
self
.
SCORE
[
"points_possible"
]
)
def
test_get_submission_and_student
(
self
):
retrieved
=
sub_api
.
get_submission_and_student
(
self
.
submission
[
'uuid'
],
read_replica
=
True
)
expected
=
copy
.
deepcopy
(
self
.
submission
)
expected
[
'student_item'
]
=
copy
.
deepcopy
(
self
.
STUDENT_ITEM
)
self
.
assertEqual
(
retrieved
,
expected
)
def
test_get_latest_score_for_submission
(
self
):
retrieved
=
sub_api
.
get_latest_score_for_submission
(
self
.
submission
[
'uuid'
],
read_replica
=
True
)
self
.
assertEqual
(
retrieved
[
'points_possible'
],
self
.
SCORE
[
'points_possible'
])
self
.
assertEqual
(
retrieved
[
'points_earned'
],
self
.
SCORE
[
'points_earned'
])
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