Newer
Older
@classmethod
def get_gradebook_by_opp_view(
cls, gopp_identifier, view_page_grades=False, 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_gradebook_by_opp_url(
gopp_identifier, view_page_grades, course_identifier))
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
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
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
@classmethod
def post_gradebook_by_opp_view(
cls, gopp_identifier, post_data, view_page_grades=False,
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_gradebook_by_opp_url(
gopp_identifier, view_page_grades, course_identifier),
data=post_data)
@classmethod
def get_reopen_session_url(cls, gopp_identifier, flow_session_id=None,
course_identifier=None):
course_identifier = (
course_identifier or cls.get_default_course_identifier())
opp_id = GradingOpportunity.objects.get(
course__identifier=course_identifier,
identifier=gopp_identifier).pk
if flow_session_id is None:
flow_session_id = cls.get_default_flow_session_id(course_identifier)
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)
def get_logged_in_user(cls, client):
logged_in_user_id = client.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
def temporarily_switch_to_user(cls, client, switch_to):
return _ClientUserSwitcher(
client, cls.get_logged_in_user(client), 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, client, flow_id, *, course_identifier=None,
"""
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 = client.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, client, *, course_identifier=None, flow_session_id=None,
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 = client.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, client, 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 = client.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, client, 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 = client.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)
def post_grade_by_ordinal(cls, client, 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 = client.post(
cls.get_page_grading_url_by_ordinal(**page_params),
def post_grade_by_page_id(cls, client, 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(client,
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, client, page_ordinal, *,
course_identifier=None, flow_session_id=None):
resp = client.get(
cls.get_page_submit_history_url_by_ordinal(
page_ordinal, course_identifier, flow_session_id),
HTTP_X_REQUESTED_WITH="XMLHttpRequest")
def get_page_grade_history_by_ordinal(
cls, client, page_ordinal, *,
course_identifier=None, flow_session_id=None):
resp = client.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)
def post_update_course_content(cls, client, 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 = client.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}
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
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)
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
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
# {{{ 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
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
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.client.get(
self.get_flow_analytics_url(
flow_id, course_identifier=course_identifier,
restrict_to_first_attempt=restrict_to_first_attempt))
def get_manage_authentication_token_url(self, course_identifier=None):
course_identifier = course_identifier or self.get_default_course_identifier()
return reverse("relate-manage_authentication_tokens",
args=(course_identifier,))
# }}}
# {{{ SingleCourseTestMixin
class SingleCourseTestMixin(CoursesTestMixinBase):
courses_setup_list = SINGLE_COURSE_SETUP_LIST
@classmethod
def setUpTestData(cls): # noqa
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
cls.course_page_url = cls.get_course_page_url()
def setUp(self): # noqa
# 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()
self.client.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
# }}}
# {{{ TwoCourseTestMixin
class TwoCourseTestMixin(CoursesTestMixinBase):
@classmethod
def setUpTestData(cls): # noqa
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
1997
1998
1999
2000
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)