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
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
kwargs = {"course_identifier": course_identifier,
"opportunity_id": opp_id,
"flow_session_id": flow_session_id}
return reverse("relate-view_reopen_session", kwargs=kwargs)
@classmethod
def get_reopen_session_view(cls, gopp_identifier, flow_session_id=None,
course_identifier=None, force_login_instructor=True):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
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_reopen_session_url(
gopp_identifier, flow_session_id, course_identifier))
@classmethod
def post_reopen_session_view(cls, gopp_identifier, data, flow_session_id=None,
course_identifier=None, force_login_instructor=True):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
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_reopen_session_url(
gopp_identifier, flow_session_id, course_identifier), data=data)
@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()
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
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'}
non_in_progress_only = kwargs.pop("non_in_progress_only", True)
if not non_in_progress_only:
del data["non_in_progress_only"]
return cls.c.post(
cls.download_all_submissions_url(flow_id, course_identifier),
def get_flow_page_analytics_url(cls, flow_id, group_id, page_id,
course_identifier=None,
restrict_to_first_attempt=False):
course_identifier = cls.get_default_course_identifier()
params = {"course_identifier": course_identifier,
"flow_id": flow_id,
"group_id": group_id,
"page_id": page_id}
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
url = reverse("relate-page_analytics", kwargs=params)
if restrict_to_first_attempt:
url += "?restrict_to_first_attempt=1"
return url
@classmethod
def get_flow_page_analytics(cls, flow_id, group_id, page_id,
course_identifier=None,
force_login_instructor=True,
restrict_to_first_attempt=False):
course_identifier = course_identifier or cls.get_default_course_identifier()
url = cls.get_flow_page_analytics_url(
flow_id, group_id, page_id, course_identifier, restrict_to_first_attempt)
if not force_login_instructor:
user = cls.get_logged_in_user()
else:
user = cls.instructor_participation.user
with cls.temporarily_switch_to_user(user):
return cls.c.get(url)
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
# {{{ 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)
# }}}
def get_form_submit_inputs(self, form):
from crispy_forms.layout import Submit
inputs = [
(input.name, input.value) for input in form.helper.inputs
if isinstance(input, Submit)
]
names = list(dict(inputs).keys())
values = list(dict(inputs).values())
return names, values
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
def get_flow_analytics_url(self, flow_id, course_identifier,
restrict_to_first_attempt=None):
course_identifier = course_identifier or self.get_default_course_identifier()
kwargs = {
"flow_id": flow_id,
"course_identifier": course_identifier}
result = reverse("relate-flow_analytics", kwargs=kwargs)
if restrict_to_first_attempt:
result += "?restrict_to_first_attempt=%s" % restrict_to_first_attempt
return result
def get_flow_analytics_view(self, flow_id, course_identifier=None,
restrict_to_first_attempt=None,
force_login_instructor=True):
course_identifier = course_identifier or self.get_default_course_identifier()
if not force_login_instructor:
user = self.get_logged_in_user()
else:
user = self.instructor_participation.user
with self.temporarily_switch_to_user(user):
return self.c.get(
self.get_flow_analytics_url(
flow_id, course_identifier=course_identifier,
restrict_to_first_attempt=restrict_to_first_attempt))
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)
if v is None:
kwargs[k] = ""
return kwargs
@classmethod
def get_course_page_context(cls, user):
request = rf.get(cls.get_course_page_url())
request.user = user
from course.utils import CoursePageContext
pctx = CoursePageContext(request, cls.course.identifier)
cls, 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(cls.get_course_page_url())
request.user = user
if flow_id is None:
flow_id = QUIZ_FLOW_ID
if commit_sha is None:
commit_sha = cls.course.active_git_commit_sha
commit_sha = commit_sha.encode()
from course.content import get_flow_desc
with cls.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)
def get_hacked_flow_desc_with_access_rule_tags(self, rule_tags):
assert isinstance(rule_tags, list)
from relate.utils import struct_to_dict, dict_to_struct
hacked_flow_desc_dict = self.get_hacked_flow_desc(as_dict=True)
rules = hacked_flow_desc_dict["rules"]
rules_dict = struct_to_dict(rules)
rules_dict["tags"] = rule_tags
rules = dict_to_struct(rules_dict)
hacked_flow_desc_dict["rules"] = rules
hacked_flow_desc = dict_to_struct(hacked_flow_desc_dict)
assert hacked_flow_desc.rules.tags == rule_tags
return hacked_flow_desc
class TwoCourseTestMixin(CoursesTestMixinBase):
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
@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):