Skip to content
base_test_mixins.py 107 KiB
Newer Older
Dong Zhuang's avatar
Dong Zhuang committed
    @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))

Dong Zhuang's avatar
Dong Zhuang committed
    @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):
Dong Zhuang's avatar
Dong Zhuang committed
        try:
            logged_in_user_id = client.session["_auth_user_id"]
Dong Zhuang's avatar
Dong Zhuang committed
            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)
Dong Zhuang's avatar
Dong Zhuang committed

    @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

Dong Zhuang's avatar
Dong Zhuang committed
    @classmethod
    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
Dong Zhuang's avatar
Dong Zhuang committed

    @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
Dong Zhuang's avatar
Dong Zhuang committed

    @classmethod
    def get_default_flow_session_id(cls, course_identifier):

    @classmethod
    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()

Dong Zhuang's avatar
Dong Zhuang committed
    @classmethod
Dong Zhuang's avatar
Dong Zhuang committed
    def get_view_start_flow_url(cls, flow_id, course_identifier=None):
        course_identifier = course_identifier or cls.get_default_course_identifier()
Dong Zhuang's avatar
Dong Zhuang committed
        kwargs = {"course_identifier": course_identifier,
Dong Zhuang's avatar
Dong Zhuang committed
                  "flow_id": flow_id}
Dong Zhuang's avatar
Dong Zhuang committed
        return reverse("relate-view_start_flow", kwargs=kwargs)

    @classmethod
    def start_flow(cls, client, flow_id, *, course_identifier=None,
Dong Zhuang's avatar
Dong Zhuang committed
                   ignore_cool_down=True, assume_success=True):
Dong Zhuang's avatar
Dong Zhuang committed
        """
        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.
        """
Dong Zhuang's avatar
Dong Zhuang committed
        existing_session_count = FlowSession.objects.all().count()
Dong Zhuang's avatar
Dong Zhuang committed
        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):
Dong Zhuang's avatar
Dong Zhuang committed
                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)

Dong Zhuang's avatar
Dong Zhuang committed
        return resp

    def end_flow(cls, client, *, course_identifier=None, flow_session_id=None,
Dong Zhuang's avatar
Dong Zhuang committed
                 post_parameter="submit"):
Dong Zhuang's avatar
Dong Zhuang committed
        if not course_identifier or not flow_session_id:
            if cls.default_flow_params is None:
Dong Zhuang's avatar
Dong Zhuang committed
                raise RuntimeError(
                    "There's no started flow_sessions, or "
                    "the session is not started by start_flow")
Dong Zhuang's avatar
Dong Zhuang committed
            cls.get_finish_flow_session_view_url(
                course_identifier, flow_session_id),
            data={post_parameter: [""]})
Dong Zhuang's avatar
Dong Zhuang committed
    @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 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_ordinal=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})
Dong Zhuang's avatar
Dong Zhuang committed

    def get_page_ordinal_via_page_id(
Dong Zhuang's avatar
Dong Zhuang committed
            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(
Dong Zhuang's avatar
Dong Zhuang committed
                flow_params["flow_session_id"], page_id,
                with_group_id=with_group_id))
    def get_page_id_via_page_oridnal(
            cls, page_ordinal, 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_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,
            flow_session_id=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(
Dong Zhuang's avatar
Dong Zhuang committed
            cls, page_ordinal, course_identifier=None, flow_session_id=None,
            visit_id=None):
        url = cls.get_page_view_url_by_ordinal(
            page_ordinal, course_identifier, flow_session_id)
Dong Zhuang's avatar
Dong Zhuang committed
        if visit_id is not None:
            url += "?visit_id=%s" % str(visit_id)

        return url
    def get_page_url_by_page_id(
Dong Zhuang's avatar
Dong Zhuang committed
            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(
Dong Zhuang's avatar
Dong Zhuang committed
            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(
            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, *,
Dong Zhuang's avatar
Dong Zhuang committed
            course_identifier=None, flow_session_id=None, visit_id=None):
        submit_data = answer_data
        submit_data.update({"submit": ["Submit final answer"]})
            cls.get_page_url_by_ordinal(
Dong Zhuang's avatar
Dong Zhuang committed
                page_ordinal, course_identifier, flow_session_id, visit_id),
Dong Zhuang's avatar
Dong Zhuang committed

Dong Zhuang's avatar
Dong Zhuang committed
    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(
Dong Zhuang's avatar
Dong Zhuang committed
            page_ordinal, answer_data, course_identifier, flow_session_id, visit_id)
Dong Zhuang's avatar
Dong Zhuang committed

    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_ordinal": page_ordinal
        }
        page_url = reverse("relate-view_flow_page", kwargs=page_params)
        resp = client.post(page_url, submit_data)
Dong Zhuang's avatar
Dong Zhuang committed

    @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,
Dong Zhuang's avatar
Dong Zhuang committed
                              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()
Dong Zhuang's avatar
Dong Zhuang committed
        if force_login_instructor:
            force_login_user = cls.get_default_instructor_user(
                page_params["course_identifier"])
Dong Zhuang's avatar
Dong Zhuang committed

        with cls.temporarily_switch_to_user(force_login_user):
                cls.get_page_grading_url_by_ordinal(**page_params),
Dong Zhuang's avatar
Dong Zhuang committed
                data=post_data,
                follow=True)
        return response

    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)
Dong Zhuang's avatar
Dong Zhuang committed

        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)
            from decimal import Decimal
Dong Zhuang's avatar
Dong Zhuang committed
            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))))
            )
Dong Zhuang's avatar
Dong Zhuang committed
            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")
Dong Zhuang's avatar
Dong Zhuang committed
        return resp

    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")
Dong Zhuang's avatar
Dong Zhuang committed
        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)
Dong Zhuang's avatar
Dong Zhuang committed
        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,
Dong Zhuang's avatar
Dong Zhuang committed
            force_login_instructor=True):

        if course_identifier is None:
            course_identifier = self.get_default_course_identifier()
Dong Zhuang's avatar
Dong Zhuang committed

        if force_login_instructor:
            switch_to = self.get_default_instructor_user(course_identifier)
Dong Zhuang's avatar
Dong Zhuang committed
        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)
Dong Zhuang's avatar
Dong Zhuang committed

Dong Zhuang's avatar
Dong Zhuang committed
        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])
Dong Zhuang's avatar
Dong Zhuang committed

    @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,
Dong Zhuang's avatar
Dong Zhuang committed
                                   command="update",
                                   ):
        # course instead of course_identifier because we need to do
        # refresh_from_db
        course = course or cls.get_default_course()
Dong Zhuang's avatar
Dong Zhuang committed

        try:
            commit_sha = commit_sha.decode()
        except Exception:
            pass

        data = {"new_sha": [commit_sha]}
Dong Zhuang's avatar
Dong Zhuang committed

        if not prevent_discarding_revisions:
            data["prevent_discarding_revisions"] = ["on"]

Dong Zhuang's avatar
Dong Zhuang committed
        # normally, command should be in
        # ["fetch", "fetch_update", "update", "fetch_preview", "preview",
        #  "end_preview"]
Dong Zhuang's avatar
Dong Zhuang committed

Dong Zhuang's avatar
Dong Zhuang committed
        force_login_user = cls.get_logged_in_user()
Dong Zhuang's avatar
Dong Zhuang committed
        if force_login_instructor:
            force_login_user = cls.get_default_instructor_user(course.identifier)
Dong Zhuang's avatar
Dong Zhuang committed

        with cls.temporarily_switch_to_user(force_login_user):
                cls.get_update_course_url(course.identifier), data)
            course.refresh_from_db()
Dong Zhuang's avatar
Dong Zhuang committed

        return response

    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"
    @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):
        if course_identifier is 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"}
Dong Zhuang's avatar
Dong Zhuang committed
        non_in_progress_only = kwargs.pop("non_in_progress_only", True)
        if not non_in_progress_only:
            del data["non_in_progress_only"]

        data.update(kwargs)

        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):
        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}

        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)
Dong Zhuang's avatar
Dong Zhuang committed
    # {{{ 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

    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))

ifaint's avatar
ifaint committed
    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
Dong Zhuang's avatar
Dong Zhuang committed
    initial_commit_sha = None

    @classmethod
    def setUpTestData(cls):  # noqa
        super().setUpTestData()
        assert len(cls.course_qset) == 1
        cls.course = cls.course_qset.first()
Dong Zhuang's avatar
Dong Zhuang committed
        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
Dong Zhuang's avatar
Dong Zhuang committed

        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)

        for k, v in kwargs.items():
    @classmethod
    def get_course_page_context(cls, user):
        rf = RequestFactory()
        request = rf.get(cls.get_course_page_url())
        request.user = user

        from course.utils import CoursePageContext
        pctx = CoursePageContext(request, cls.course.identifier)
    @classmethod
    def get_hacked_flow_desc(
            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())
        if user is None:
            user = cls.student_participation.user
        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
        if isinstance(commit_sha, str):
            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):
Dong Zhuang's avatar
Dong Zhuang committed
    courses_setup_list = TWO_COURSE_SETUP_LIST

    @classmethod
    def setUpTestData(cls):  # noqa
        super().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)