Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
@classmethod
def get_single_grade_url(cls, participation_id, opp_id,
course_identifier=None):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
kwargs = {"course_identifier": course_identifier,
"opportunity_id": opp_id,
"participation_id": participation_id}
return reverse("relate-view_single_grade", kwargs=kwargs)
@classmethod
def get_view_single_grade(cls, participation, gopp,
course_identifier=None, force_login_instructor=True):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
opp_id = GradingOpportunity.objects.get(
course__identifier=course_identifier,
identifier=gopp.identifier).pk
if force_login_instructor:
switch_to = cls.get_default_instructor_user(course_identifier)
else:
switch_to = cls.get_logged_in_user()
with cls.temporarily_switch_to_user(switch_to):
return cls.c.get(cls.get_single_grade_url(
participation.pk, opp_id, course_identifier))
@classmethod
def post_view_single_grade(cls, participation, gopp, data,
course_identifier=None, force_login_instructor=True):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
opp_id = GradingOpportunity.objects.get(
course__identifier=course_identifier,
identifier=gopp.identifier).pk
if force_login_instructor:
switch_to = cls.get_default_instructor_user(course_identifier)
else:
switch_to = cls.get_logged_in_user()
with cls.temporarily_switch_to_user(switch_to):
return cls.c.post(cls.get_single_grade_url(
participation.pk, opp_id, course_identifier),
data=data)
@classmethod
def get_logged_in_user(cls):
logged_in_user_id = cls.c.session['_auth_user_id']
from django.contrib.auth import get_user_model
logged_in_user = get_user_model().objects.get(
pk=int(logged_in_user_id))
except KeyError:
logged_in_user = None
return logged_in_user
@classmethod
def temporarily_switch_to_user(cls, switch_to):
from functools import wraps
class ClientUserSwitcher(object):
def __init__(self, switch_to):
self.logged_in_user = cls.get_logged_in_user()
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
def __enter__(self):
if self.logged_in_user == self.switch_to:
return
if self.switch_to is None:
self.client.logout()
return
self.client.force_login(self.switch_to)
def __exit__(self, exc_type, exc_val, exc_tb):
if self.logged_in_user == self.switch_to:
return
if self.logged_in_user is None:
self.client.logout()
return
self.client.force_login(self.logged_in_user)
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kw):
with self:
return func(*args, **kw)
return wrapper
return ClientUserSwitcher(switch_to)
@classmethod
def get_default_course(cls):
if Course.objects.count() > 1:
raise AttributeError(
"'course' arg can not be omitted for "
"testcases with more than one courses")
raise NotImplementedError
def get_default_course_identifier(cls):
if Course.objects.count() > 1:
raise AttributeError(
"'course_identifier' arg can not be omitted for "
"testcases with more than one courses")
raise NotImplementedError
@classmethod
def get_latest_session_id(cls, course_identifier):
flow_session_qset = FlowSession.objects.filter(
course__identifier=course_identifier).order_by('-pk')[:1]
if flow_session_qset:
return flow_session_qset[0].id
else:
return None
@classmethod
def get_default_flow_session_id(cls, course_identifier):
raise NotImplementedError
def update_default_flow_session_id(cls, course_identifier):
raise NotImplementedError
@classmethod
def get_default_instructor_user(cls, course_identifier):
return Participation.objects.filter(
course__identifier=course_identifier,
roles__identifier="instructor",
status=participation_status.active
).first().user
@classmethod
def update_course_attribute(cls, attrs, course=None):
# course instead of course_identifier because we need to do
# refresh_from_db
assert isinstance(attrs, dict)
course = course or cls.get_default_course()
if attrs:
course.__dict__.update(attrs)
course.save()
course.refresh_from_db()
def get_view_start_flow_url(cls, flow_id, course_identifier=None):
course_identifier = course_identifier or cls.get_default_course_identifier()
return reverse("relate-view_start_flow", kwargs=kwargs)
@classmethod
def start_flow(cls, flow_id, course_identifier=None,
ignore_cool_down=True, assume_success=True):
"""
Notice: be cautious to use this in setUpTestData, because this will
create many related objects in db, if those objects are changed in
individual test, other tests followed might fail.
"""
existing_session_count = FlowSession.objects.all().count()
if ignore_cool_down:
cool_down_seconds = 0
else:
cool_down_seconds = settings.RELATE_SESSION_RESTART_COOLDOWN_SECONDS
with override_settings(
RELATE_SESSION_RESTART_COOLDOWN_SECONDS=cool_down_seconds):
resp = cls.c.post(
cls.get_view_start_flow_url(flow_id, course_identifier))
if assume_success:
assert resp.status_code == 302, resp.content
new_session_count = FlowSession.objects.all().count()
assert new_session_count == existing_session_count + 1
_, _, params = resolve(resp.url)
del params["page_ordinal"]
cls.default_flow_params = params
cls.update_default_flow_session_id(course_identifier)
@classmethod
def end_flow(cls, course_identifier=None, flow_session_id=None,
post_parameter="submit"):
if not course_identifier or not flow_session_id:
if cls.default_flow_params is None:
raise RuntimeError(
"There's no started flow_sessions, or "
"the session is not started by start_flow")
resp = cls.c.post(
cls.get_finish_flow_session_view_url(
course_identifier, flow_session_id),
data={post_parameter: ['']})
return resp
@classmethod
def get_resume_flow_url(cls, course_identifier=None, flow_session_id=None):
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
return reverse("relate-view_resume_flow", kwargs=flow_params)
@classmethod
def get_flow_params(cls, course_identifier=None, flow_session_id=None):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
if flow_session_id is None:
flow_session_id = cls.get_default_flow_session_id(course_identifier)
return {
"course_identifier": course_identifier,
"flow_session_id": flow_session_id
}
@classmethod
def get_page_params(cls, course_identifier=None, flow_session_id=None,
page_params = cls.get_flow_params(course_identifier, flow_session_id)
if page_ordinal is None:
page_ordinal = 0
page_params.update({"page_ordinal": page_ordinal})
return page_params
def get_page_ordinal_via_page_id(
cls, page_id, course_identifier=None, flow_session_id=None,
with_group_id=False):
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
return (
get_flow_page_ordinal_from_page_id(
flow_params["flow_session_id"], page_id,
with_group_id=with_group_id))
cls, page_ordinal, course_identifier=None, flow_session_id=None,
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
return (
get_flow_page_id_from_page_ordinal(
flow_params["flow_session_id"], page_ordinal,
with_group_id=with_group_id))
def get_page_view_url_by_ordinal(
cls, viewname, page_ordinal, course_identifier=None,
page_params = cls.get_page_params(
course_identifier, flow_session_id, page_ordinal)
return reverse(viewname, kwargs=page_params)
def get_page_view_url_by_page_id(
cls, viewname, page_id, course_identifier=None, flow_session_id=None):
page_ordinal = cls.get_page_ordinal_via_page_id(
page_id, course_identifier, flow_session_id)
return cls.get_page_view_url_by_ordinal(
viewname, page_ordinal, course_identifier, flow_session_id)
def get_page_url_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None,
visit_id=None):
url = cls.get_page_view_url_by_ordinal(
"relate-view_flow_page",
page_ordinal, course_identifier, flow_session_id)
if visit_id is not None:
url += "?visit_id=%s" % str(visit_id)
return url
def get_page_url_by_page_id(
cls, page_id, course_identifier=None, flow_session_id=None,
visit_id=None):
page_ordinal = cls.get_page_ordinal_via_page_id(
page_id, course_identifier, flow_session_id)
return cls.get_page_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id, visit_id)
def get_page_grading_url_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None):
return cls.get_page_view_url_by_ordinal(
"relate-grade_flow_page",
page_ordinal, course_identifier, flow_session_id)
def get_page_grading_url_by_page_id(
cls, page_id, course_identifier=None, flow_session_id=None):
page_ordinal = cls.get_page_ordinal_via_page_id(
page_id, course_identifier, flow_session_id)
return cls.get_page_grading_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id)
def post_answer_by_ordinal(
cls, page_ordinal, answer_data,
course_identifier=None, flow_session_id=None, visit_id=None):
submit_data = answer_data
submit_data.update({"submit": ["Submit final answer"]})
resp = cls.c.post(
cls.get_page_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id, visit_id),
submit_data)
return resp
def post_answer_by_page_id(
cls, page_id, answer_data,
course_identifier=None, flow_session_id=None, visit_id=None):
page_ordinal = cls.get_page_ordinal_via_page_id(
page_id, course_identifier, flow_session_id)
return cls.post_answer_by_ordinal(
page_ordinal, answer_data, course_identifier, flow_session_id, visit_id)
@classmethod
def post_answer_by_ordinal_class(cls, page_ordinal, answer_data,
course_identifier, flow_session_id):
submit_data = answer_data
submit_data.update({"submit": ["Submit final answer"]})
page_params = {
"course_identifier": course_identifier,
"flow_session_id": flow_session_id,
}
page_url = reverse("relate-view_flow_page", kwargs=page_params)
resp = cls.c.post(page_url, submit_data)
return resp
@classmethod
def post_answer_by_page_id_class(cls, page_id, answer_data,
course_identifier, flow_session_id):
page_ordinal = get_flow_page_ordinal_from_page_id(flow_session_id, page_id)
return cls.post_answer_by_ordinal_class(page_ordinal, answer_data,
course_identifier, flow_session_id)
@classmethod
def post_grade_by_ordinal(cls, page_ordinal, grade_data,
course_identifier=None, flow_session_id=None,
force_login_instructor=True):
post_data = {"submit": [""]}
post_data.update(grade_data)
page_params = cls.get_page_params(
course_identifier, flow_session_id, page_ordinal)
force_login_user = cls.get_logged_in_user()
force_login_user = cls.get_default_instructor_user(
page_params["course_identifier"])
with cls.temporarily_switch_to_user(force_login_user):
response = cls.c.post(
cls.get_page_grading_url_by_ordinal(**page_params),
@classmethod
def post_grade_by_page_id(cls, page_id, grade_data,
course_identifier=None, flow_session_id=None,
force_login_instructor=True):
page_ordinal = cls.get_page_ordinal_via_page_id(
page_id, course_identifier, flow_session_id)
return cls.post_grade_by_ordinal(
page_ordinal, grade_data, course_identifier,
flow_session_id, force_login_instructor)
def assertSessionScoreEqual( # noqa
cls, expected_score, course_identifier=None, flow_session_id=None):
if flow_session_id is None:
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
flow_session_id = flow_params["flow_session_id"]
flow_session = FlowSession.objects.get(id=flow_session_id)
if expected_score is not None:
from decimal import Decimal
assert flow_session.points == Decimal(str(expected_score)), (
"The flow session got '%s' in stead of '%s'"
% (str(flow_session.points), str(Decimal(str(expected_score))))
)
assert flow_session.points is None, (
"This flow session unexpectedly got %s instead of None"
% flow_session.points)
def get_page_submit_history_url_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None):
return cls.get_page_view_url_by_ordinal(
"relate-get_prev_answer_visits_dropdown_content",
page_ordinal, course_identifier, flow_session_id)
def get_page_grade_history_url_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None):
return cls.get_page_view_url_by_ordinal(
"relate-get_prev_grades_dropdown_content",
page_ordinal, course_identifier, flow_session_id)
def get_page_submit_history_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None):
resp = cls.c.get(
cls.get_page_submit_history_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
return resp
def get_page_grade_history_by_ordinal(
cls, page_ordinal, course_identifier=None, flow_session_id=None):
resp = cls.c.get(
cls.get_page_grade_history_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
return resp
def assertSubmitHistoryItemsCount( # noqa
self, page_ordinal, expected_count, course_identifier=None,
flow_session_id=None):
resp = self.get_page_submit_history_by_ordinal(
page_ordinal, course_identifier, flow_session_id)
import json
result = json.loads(resp.content.decode())["result"]
self.assertEqual(len(result), expected_count)
def assertGradeHistoryItemsCount( # noqa
self, page_ordinal, expected_count,
course_identifier=None,
flow_session_id=None,
if course_identifier is None:
course_identifier = self.get_default_course_identifier()
switch_to = self.get_default_instructor_user(course_identifier)
else:
switch_to = self.get_logged_in_user()
with self.temporarily_switch_to_user(switch_to):
resp = self.get_page_grade_history_by_ordinal(
page_ordinal, course_identifier, flow_session_id)
import json
result = json.loads(resp.content.decode())["result"]
self.assertEqual(len(result), expected_count)
@classmethod
def get_update_course_url(cls, course_identifier=None):
if course_identifier is None:
course_identifier = cls.get_default_course_identifier()
return reverse("relate-update_course", args=[course_identifier])
@classmethod
def get_course_commit_sha(cls, participation, course=None):
course = course or cls.get_default_course()
from course.content import get_course_commit_sha
return get_course_commit_sha(course, participation)
@classmethod
def post_update_course_content(cls, commit_sha,
prevent_discarding_revisions=True,
force_login_instructor=True,
course=None,
):
# course instead of course_identifier because we need to do
# refresh_from_db
course = course or cls.get_default_course()
try:
commit_sha = commit_sha.decode()
except Exception:
pass
data = {"new_sha": [commit_sha]}
if not prevent_discarding_revisions:
data["prevent_discarding_revisions"] = ["on"]
# normally, command should be in
# ["fetch", "fetch_update", "update", "fetch_preview", "preview",
# "end_preview"]
data[command] = 'on'
force_login_user = cls.get_default_instructor_user(course.identifier)
with cls.temporarily_switch_to_user(force_login_user):
response = cls.c.post(
cls.get_update_course_url(course.identifier), data)
def get_page_data_by_page_id(
cls, page_id, course_identifier=None, flow_session_id=None):
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
return FlowPageData.objects.get(
flow_session_id=flow_params["flow_session_id"], page_id=page_id)
@classmethod
def get_page_visits(cls, course_identifier=None,
flow_session_id=None, page_ordinal=None, page_id=None,
**kwargs):
query_kwargs = {}
if kwargs.get("answer_visit", False):
query_kwargs.update({"answer__isnull": False})
flow_params = cls.get_flow_params(course_identifier, flow_session_id)
query_kwargs.update({"flow_session_id": flow_params["flow_session_id"]})
if page_ordinal is not None:
query_kwargs.update({"page_data__page_ordinal": page_ordinal})
elif page_id is not None:
query_kwargs.update({"page_data__page_id": page_id})
return FlowPageVisit.objects.filter(**query_kwargs)
@classmethod
def get_last_answer_visit(cls, course_identifier=None,
flow_session_id=None, page_ordinal=None,
page_id=None, assert_not_none=True):
result_qset = cls.get_page_visits(course_identifier,
flow_session_id, page_ordinal, page_id,
answer_visit=True).order_by('-pk')[:1]
if result_qset:
result = result_qset[0]
else:
result = None
if assert_not_none:
assert result is not None, "The query returns None"
return result
@classmethod
def download_all_submissions_url(cls, flow_id, course_identifier):
params = {"course_identifier": course_identifier,
"flow_id": flow_id}
return reverse("relate-download_all_submissions", kwargs=params)
@classmethod
def get_download_all_submissions(cls, flow_id, course_identifier=None):
course_identifier = cls.get_default_course_identifier()
return cls.c.get(
cls.download_all_submissions_url(flow_id, course_identifier))
def post_download_all_submissions_by_group_page_id(
cls, group_page_id, flow_id, course_identifier=None, **kwargs):
"""
:param group_page_id: format: group_id/page_id
:param flow_id:
:param course_identifier:
:param kwargs: for updating the default post_data
:return: response
"""
if course_identifier is None:
course_identifier = cls.get_default_course_identifier()
data = {'restrict_to_rules_tag': '<<<ALL>>>',
'which_attempt': 'last',
'extra_file': '', 'download': 'Download',
'page_id': group_page_id,
'non_in_progress_only': 'on'}
data.update(kwargs)
return cls.c.post(
cls.download_all_submissions_url(flow_id, course_identifier),
@classmethod
def get_flow_page_analytics(cls, flow_id, group_id, page_id,
course_identifier=None):
if course_identifier is None:
course_identifier = cls.get_default_course_identifier()
params = {"course_identifier": course_identifier,
"flow_id": flow_id,
"group_id": group_id,
"page_id": page_id}
return cls.c.get(reverse("relate-page_analytics", kwargs=params))
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
# {{{ hack getting session rules
default_session_start_rule = {
"tag_session": None,
"may_start_new_session": True,
"may_list_existing_sessions": True,
"default_expiration_mode": None}
def get_hacked_session_start_rule(self, **kwargs):
"""
Used for mocking session_start_rule
:param kwargs: attributes in the mocked FlowSessionStartRule instance
:return: a :class:`FlowSessionStartRule` instance
Example:
with mock.patch(
"course.flow.get_session_start_rule") as mock_get_nrule:
mock_get_nrule.return_value = (
self.get_hacked_session_start_rule())
"""
from course.utils import FlowSessionStartRule
defaults = deepcopy(self.default_session_start_rule)
defaults.update(kwargs)
return FlowSessionStartRule(**defaults)
default_session_access_rule = {
"permissions": [fperm.view, fperm.end_session]}
def get_hacked_session_access_rule(self, **kwargs):
"""
Used for mocking session_access_rule
:param kwargs: attributes in the mocked FlowSessionAccessRule instance
:return: a :class:`FlowSessionAccessRule` instance
Example:
with mock.patch(
"course.flow.get_session_access_rule") as mock_get_arule:
mock_get_arule.return_value = (
self.get_hacked_session_access_rule(
permissions=[fperm.end_session]))
"""
from course.utils import FlowSessionAccessRule
defaults = deepcopy(self.default_session_access_rule)
defaults.update(kwargs)
return FlowSessionAccessRule(**defaults)
default_session_grading_rule = {
"grade_identifier": "la_quiz",
"grade_aggregation_strategy": g_strategy.use_latest,
"due": None,
"generates_grade": True,
"description": None,
"credit_percent": 100,
"use_last_activity_as_completion_time": False,
"bonus_points": 0,
"max_points": None,
"max_points_enforced_cap": None,
}
def get_hacked_session_grading_rule(self, **kwargs):
"""
Used for mocking session_grading_rule
:param kwargs: attributes in the mocked FlowSessionGradingRule instance
:return: a :class:`FlowSessionGradingRule` instance
Example:
with mock.patch(
"course.flow.get_session_grading_rule") as mock_get_grule:
mock_get_grule.return_value = \
self.get_hacked_session_grading_rule(bonus_points=2)
"""
from course.utils import FlowSessionGradingRule
defaults = deepcopy(self.default_session_grading_rule)
defaults.update(kwargs)
return FlowSessionGradingRule(**defaults)
# }}}
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
# {{{ grades view
def view_participant_grades_url(self, participation_id, course_identifier=None):
course_identifier = (
course_identifier or self.get_default_course_identifier())
kwargs = {"course_identifier": course_identifier}
if participation_id is not None:
kwargs["participation_id"] = participation_id
return reverse("relate-view_participant_grades", kwargs=kwargs)
def get_view_participant_grades(self, participation_id, course_identifier=None):
return self.c.get(self.view_participant_grades_url(
participation_id, course_identifier))
def get_view_my_grades(self, course_identifier=None):
return self.c.get(self.view_participant_grades_url(
participation_id=None, course_identifier=course_identifier))
# }}}
class SingleCourseTestMixin(CoursesTestMixinBase):
courses_setup_list = SINGLE_COURSE_SETUP_LIST
initial_commit_sha = None
force_login_student_for_each_test = True
@classmethod
def setUpTestData(cls): # noqa
super(SingleCourseTestMixin, cls).setUpTestData()
assert len(cls.course_qset) == 1
cls.course = cls.course_qset.first()
if cls.initial_commit_sha is not None:
cls.course.active_git_commit_sha = cls.initial_commit_sha
cls.course.save()
cls.instructor_participation = Participation.objects.filter(
course=cls.course,
roles__identifier="instructor",
status=participation_status.active
).first()
assert cls.instructor_participation
cls.student_participation = Participation.objects.filter(
course=cls.course,
roles__identifier="student",
status=participation_status.active
).first()
assert cls.student_participation
cls.ta_participation = Participation.objects.filter(
course=cls.course,
roles__identifier="ta",
status=participation_status.active
).first()
assert cls.ta_participation
if cls.force_login_student_for_each_test:
cls.c.force_login(cls.student_participation.user)
else:
cls.c.logout()
cls.course_page_url = cls.get_course_page_url()
def setUp(self): # noqa
super(SingleCourseTestMixin, self).setUp()
# reload objects created during setUpTestData in case they were modified in
# tests. Ref: https://goo.gl/AuzJRC#django.test.TestCase.setUpTestData
self.course.refresh_from_db()
self.instructor_participation.refresh_from_db()
self.student_participation.refresh_from_db()
self.ta_participation.refresh_from_db()
if self.force_login_student_for_each_test:
self.c.force_login(self.student_participation.user)
@classmethod
def get_default_course(cls):
return cls.course
@classmethod
def get_default_course_identifier(cls):
return cls.get_default_course().identifier
def copy_course_dict_and_set_attrs_for_post(self, attrs_dict={}):
from course.models import Course
kwargs = Course.objects.first().__dict__
kwargs.update(attrs_dict)
import six
for k, v in six.iteritems(kwargs):
if v is None:
kwargs[k] = ""
return kwargs
def get_course_page_context(self, user):
rf = RequestFactory()
request = rf.get(self.get_course_page_url())
request.user = user
from course.utils import CoursePageContext
pctx = CoursePageContext(request, self.course.identifier)
return pctx
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
def get_hacked_flow_desc(
self, user=None, flow_id=None, commit_sha=None,
del_rules=False, as_dict=False, **kwargs):
"""
Get a hacked version of flow_desc
:param user: the flow_desc viewed by which user, default to a student
:param flow_id: the flow_desc of which flow_id, default to `quiz-test`
:param commit_sha: default to corrent running commit_sha
:param kwargs: the attributes of the hacked flow_dec
:return: the faked flow_desc
"""
# {{{ get the actual flow_desc by a real visit
rf = RequestFactory()
request = rf.get(self.get_course_page_url())
if user is None:
user = self.student_participation.user
request.user = user
if flow_id is None:
flow_id = QUIZ_FLOW_ID
if commit_sha is None:
commit_sha = self.course.active_git_commit_sha
if isinstance(commit_sha, six.text_type):
commit_sha = commit_sha.encode()
from course.content import get_flow_desc
with self.get_course_page_context(user) as pctx:
flow_desc = get_flow_desc(
pctx.repo, pctx.course, flow_id, commit_sha)
# }}}
from relate.utils import struct_to_dict, dict_to_struct
flow_desc_dict = struct_to_dict(flow_desc)
if del_rules:
del flow_desc_dict["rules"]
flow_desc_dict.update(kwargs)
if as_dict:
return flow_desc_dict
return dict_to_struct(flow_desc_dict)
class TwoCourseTestMixin(CoursesTestMixinBase):
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
@classmethod
def setUpTestData(cls): # noqa
super(TwoCourseTestMixin, cls).setUpTestData()
assert len(cls.course_qset) == 2, (
"'courses_setup_list' should contain two courses")
cls.course1 = cls.course_qset.first()
cls.course1_instructor_participation = Participation.objects.filter(
course=cls.course1,
roles__identifier="instructor",
status=participation_status.active
).first()
assert cls.course1_instructor_participation
cls.course1_student_participation = Participation.objects.filter(
course=cls.course1,
roles__identifier="student",
status=participation_status.active
).first()
assert cls.course1_student_participation
cls.course1_ta_participation = Participation.objects.filter(
course=cls.course1,
roles__identifier="ta",
status=participation_status.active
).first()
assert cls.course1_ta_participation
cls.course1_page_url = cls.get_course_page_url(cls.course1.identifier)
cls.course2 = cls.course_qset.last()
cls.course2_instructor_participation = Participation.objects.filter(
course=cls.course2,
roles__identifier="instructor",
status=participation_status.active
).first()
assert cls.course2_instructor_participation
cls.course2_student_participation = Participation.objects.filter(
course=cls.course2,
roles__identifier="student",
status=participation_status.active
).first()
assert cls.course2_student_participation
cls.course2_ta_participation = Participation.objects.filter(
course=cls.course2,
roles__identifier="ta",
status=participation_status.active
).first()
assert cls.course2_ta_participation
cls.course2_page_url = cls.get_course_page_url(cls.course2.identifier)
cls.c.logout()
def setUp(self): # noqa
super(TwoCourseTestMixin, self).setUp()
# reload objects created during setUpTestData in case they were modified in
# tests. Ref: https://goo.gl/AuzJRC#django.test.TestCase.setUpTestData
self.course1.refresh_from_db()
self.course1_instructor_participation.refresh_from_db()
self.course1_student_participation.refresh_from_db()
self.course1_ta_participation.refresh_from_db()
self.course2.refresh_from_db()
self.course2_instructor_participation.refresh_from_db()
self.course2_student_participation.refresh_from_db()
self.course2_ta_participation.refresh_from_db()
class SingleCoursePageTestMixin(SingleCourseTestMixin):
# This serves as cache
_default_session_id = None
@classmethod
def update_default_flow_session_id(cls, course_identifier):
cls._default_session_id = cls.default_flow_params["flow_session_id"]
@classmethod
def get_default_flow_session_id(cls, course_identifier):
if cls._default_session_id is not None:
return cls._default_session_id
cls._default_session_id = cls.get_latest_session_id(course_identifier)
return cls._default_session_id
class TwoCoursePageTestMixin(TwoCourseTestMixin):
_course1_default_session_id = None
_course2_default_session_id = None
@property
def flow_id(self):
raise NotImplementedError
@classmethod
def get_default_flow_session_id(cls, course_identifier):
if course_identifier == cls.course1.identifier:
if cls._course1_default_session_id is not None:
return cls._course1_default_session_id
cls._course1_default_session_id = (
cls.get_last_session_id(course_identifier))
return cls._course1_default_session_id
if course_identifier == cls.course2.identifier:
if cls._course2_default_session_id is not None:
return cls._course2_default_session_id
cls._course2_default_session_id = (
cls.get_last_session_id(course_identifier))
return cls._course2_default_session_id
@classmethod
def update_default_flow_session_id(cls, course_identifier):
new_session_id = cls.default_flow_params["flow_session_id"]
if course_identifier == cls.course1.identifier:
cls._course1_default_session_id = new_session_id
elif course_identifier == cls.course2.identifier:
cls._course2_default_session_id = new_session_id
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
class SingleCourseQuizPageTestMixin(SingleCoursePageTestMixin):
skip_code_question = True
@classmethod
def ensure_grading_ui_get(cls, page_id):
with cls.temporarily_switch_to_user(cls.instructor_participation.user):
url = cls.get_page_grading_url_by_page_id(page_id)
resp = cls.c.get(url)
assert resp.status_code == 200
@classmethod
def ensure_analytic_page_get(cls, group_id, page_id):
with cls.temporarily_switch_to_user(cls.instructor_participation.user):
resp = cls.get_flow_page_analytics(
flow_id=cls.flow_id, group_id=group_id,
page_id=page_id)
assert resp.status_code == 200
@classmethod
def ensure_download_submission(
cls, group_id, page_id, dl_file_extension=None,
file_with_ext_count=None):
with cls.temporarily_switch_to_user(cls.instructor_participation.user):
group_page_id = "%s/%s" % (group_id, page_id)
resp = cls.post_download_all_submissions_by_group_page_id(
group_page_id=group_page_id, flow_id=cls.flow_id)
assert resp.status_code == 200
prefix, zip_file = resp["Content-Disposition"].split('=')
assert prefix == "attachment; filename"
assert resp.get('Content-Type') == "application/zip"
if dl_file_extension:
buf = six.BytesIO(resp.content)
import zipfile
with zipfile.ZipFile(buf, 'r') as zf:
assert zf.testzip() is None
# todo: make more assertions in terms of file content
for f in zf.filelist:
assert f.file_size > 0
if file_with_ext_count is None:
assert len([f for f in zf.filelist if
f.filename.endswith(dl_file_extension)]) > 0
else:
assert (
len([f for f in zf.filelist if
f.filename.endswith(dl_file_extension)])
== file_with_ext_count)