Ejemplo n.º 1
0
    def test_completion(self):
        user_id = 1
        workgroups_to_review = OTHER_GROUPS.keys()
        expected_submissions = {
            "group_score": "100",
            "group_q1": "Y",
            "group_q2": "200"
        }

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        self.project_api_mock.get_workgroup_review_items_for_group.return_value = [
            {
                "question": question,
                "answer": answer,
                "workgroup": group_id,
                "reviewer": str(reviewer_id),
                "content_id": self.activity_id,
            } for question, answer in expected_submissions.iteritems()
            for group_id in workgroups_to_review
            for reviewer_id in KNOWN_USERS.keys()
        ]

        group = stage_element.groups[0]
        self.select_review_subject(group)

        questions = stage_element.form.questions
        questions[0].control.select_option(expected_submissions["group_score"])
        questions[1].control.select_option(expected_submissions["group_q1"])
        questions[2].control.fill_text(expected_submissions["group_q2"])

        self.submit_and_assert_completion_published(stage_element, user_id)
Ejemplo n.º 2
0
    def test_completion(self):
        user_id = 1
        other_users = set(KNOWN_USERS.keys()) - {user_id}
        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        self.project_api_mock.get_peer_review_items_for_group.return_value = [
            {
                "question": question,
                "answer": answer,
                "workgroup": 1,
                "user": peer_id,
                "reviewer": str(user_id),
                "content_id": self.activity_id,
            } for question, answer in expected_submissions.iteritems()
            for peer_id in other_users
        ]

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        questions = stage_element.form.questions
        self.wait_for_ajax()
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.submit_and_assert_completion_published(stage_element, user_id)
    def test_completion(self):
        user_id = 1
        other_users = set(KNOWN_USERS.keys()) - {user_id}
        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        self.project_api_mock.get_peer_review_items_for_group.return_value = [
            {
                "question": question,
                "answer": answer,
                "workgroup": 1,
                "user": peer_id,
                "reviewer": str(user_id),
                "content_id": self.activity_id,
            }
            for question, answer in expected_submissions.iteritems()
            for peer_id in other_users
        ]

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        questions = stage_element.form.questions
        self.wait_for_ajax()
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.submit_and_assert_completion_published(stage_element, user_id)
    def test_completion(self):
        user_id = 1
        workgroups_to_review = OTHER_GROUPS.keys()
        expected_submissions = {
            "group_score": "100",
            "group_q1": "Y",
            "group_q2": "200"
        }

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        self.project_api_mock.get_workgroup_review_items_for_group.return_value = [
            {
                "question": question,
                "answer": answer,
                "workgroup": group_id,
                "reviewer": str(reviewer_id),
                "content_id": self.activity_id,
            }
            for question, answer in expected_submissions.iteritems()
            for group_id in workgroups_to_review
            for reviewer_id in KNOWN_USERS.keys()
        ]

        group = stage_element.groups[0]
        self.select_review_subject(group)

        questions = stage_element.form.questions
        questions[0].control.select_option(expected_submissions["group_score"])
        questions[1].control.select_option(expected_submissions["group_q1"])
        questions[2].control.fill_text(expected_submissions["group_q2"])

        self.submit_and_assert_completion_published(stage_element, user_id)
Ejemplo n.º 5
0
 def setUp(self):
     super(BasePeerReviewStageTest, self).setUp()
     self.project_api_mock.get_workgroups_to_review = mock.Mock(
         return_value=OTHER_GROUPS.values())
     self.project_api_mock.get_workgroup_reviewers = mock.Mock(
         return_value=[{
             "id": user.id
         } for user in KNOWN_USERS.values()])
 def setUp(self):
     super(TestOtherGroupSubmissionLinks, self).setUp()
     self.project_api_mock.get_workgroups_to_review = mock.Mock(
         return_value=OTHER_GROUPS.values())
     self.project_api_mock.get_workgroup_reviewers = mock.Mock(
         return_value=[{
             "id": user.id
         } for user in KNOWN_USERS.values()])
Ejemplo n.º 7
0
    def test_submission(self, user_id):
        self.make_patch(TeamEvaluationStage, 'anonymous_student_id',
                        str(user_id))
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))
        self._setup_review_items_store()

        initial_statuses = {
            usr_id: ReviewState.NOT_STARTED
            for usr_id in list(KNOWN_USERS.keys()) if usr_id != user_id
        }
        self._assert_teammate_statuses(stage_element,
                                       initial_statuses)  # precondition check

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        questions = stage_element.form.questions
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.assertTrue(stage_element.form.submit.is_displayed())
        self.assertEqual(stage_element.form.submit.text,
                         "Submit")  # first time here - should read Submit
        self.click_submit(stage_element)

        self.project_api_mock.submit_peer_review_items.assert_called_once_with(
            str(user_id), stage_element.form.peer_id, 1, self.activity_id,
            expected_submissions)

        expected_statuses = {
            usr_id: ReviewState.NOT_STARTED
            for usr_id in KNOWN_USERS.keys() if usr_id != user_id
        }
        expected_statuses[int(
            peer.subject_id
        )] = ReviewState.COMPLETED  # status is refreshed after submission
        self._assert_teammate_statuses(stage_element, expected_statuses)
Ejemplo n.º 8
0
    def test_interaction(self, user_id):
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        other_users = set(KNOWN_USERS.keys()) - {user_id}

        # A default selection should be made automatically.
        self.assertEquals(stage_element.form.peer_id, min(other_users))

        peers = stage_element.peers
        self.assertEqual(len(peers), len(other_users))
        for user_id, peer in zip(other_users, peers):
            self.assertEqual(peer.name, KNOWN_USERS[user_id].username)
            self.select_review_subject(peer)
            self.assertEqual(stage_element.form.peer_id, user_id)
    def test_interaction(self, user_id):
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        other_users = set(KNOWN_USERS.keys()) - {user_id}

        # A default selection should be made automatically.
        self.assertEquals(stage_element.form.peer_id, min(other_users))

        peers = stage_element.peers
        self.assertEqual(len(peers), len(other_users))
        for user_id, peer in zip(other_users, peers):
            self.assertEqual(peer.name, KNOWN_USERS[user_id].username)
            self.select_review_subject(peer)
            self.assertEqual(stage_element.form.peer_id, user_id)
    def test_submission(self, user_id):
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))
        self._setup_review_items_store()

        initial_statuses = {usr_id: ReviewState.NOT_STARTED for usr_id in KNOWN_USERS.keys() if usr_id != user_id}
        self._assert_teammate_statuses(stage_element, initial_statuses)  # precondition check

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        questions = stage_element.form.questions
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.assertTrue(stage_element.form.submit.is_displayed())
        self.assertEqual(stage_element.form.submit.text, "Submit")  # first time here - should read Submit
        self.click_submit(stage_element)

        self.project_api_mock.submit_peer_review_items.assert_called_once_with(
            str(user_id),
            stage_element.form.peer_id,
            1,
            self.activity_id,
            expected_submissions
        )

        expected_statuses = {usr_id: ReviewState.NOT_STARTED for usr_id in KNOWN_USERS.keys() if usr_id != user_id}
        expected_statuses[int(peer.subject_id)] = ReviewState.COMPLETED  # status is refreshed after submission
        self._assert_teammate_statuses(stage_element, expected_statuses)
 def setUp(self):
     super(TestOtherGroupSubmissionLinks, self).setUp()
     self.project_api_mock.get_workgroups_to_review = mock.Mock(return_value=OTHER_GROUPS.values())
     self.project_api_mock.get_workgroup_reviewers = mock.Mock(return_value=[
         {"id": user.id} for user in KNOWN_USERS.values()
     ])
Ejemplo n.º 12
0
class TeamEvaluationStageTest(BaseReviewStageTest):
    stage_type = TeamEvaluationStage
    stage_element = ReviewStageElement

    STAGE_DATA_XML = textwrap.dedent("""
        <gp-v2-peer-selector/>
        <gp-v2-review-question question_id="peer_score" title="How about that?" required="true" single_line="true">
          <opt:question_content>
            <![CDATA[
              <select>
                <option value="">Rating</option>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
                <option value="6">6</option>
                <option value="7">7</option>
                <option value="8">8</option>
                <option value="9">9</option>
                <option value="10">10</option>
              </select>
            ]]>
          </opt:question_content>
        </gp-v2-review-question>
        <gp-v2-review-question question_id="peer_q1" title="Were they helpful?" required="true" single_line="true">
          <opt:question_content>
            <![CDATA[
              <select>
                <option value="Y">Yes</option>
                <option value="N">No</option>
              </select>
            ]]>
          </opt:question_content>
        </gp-v2-review-question>
        <gp-v2-review-question question_id="peer_q2" title="General Comments" required="false">
          <opt:question_content>
            <![CDATA[
              <textarea/>
            ]]>
          </opt:question_content>
        </gp-v2-review-question>
    """)

    REQUIRED_QUESTION_ID1 = "peer_score"
    REQUIRED_QUESTION_ID2 = "peer_q1"

    def setUp(self):
        super(TeamEvaluationStageTest, self).setUp()
        self.project_api_mock.get_peer_review_items = mock.Mock(
            return_value={})

        self.load_scenario_xml(self.build_scenario_xml(self.STAGE_DATA_XML),
                               load_immediately=False)

    def _setup_review_items_store(self, initial_items=None):
        store = defaultdict(list)
        if initial_items:
            store.update(initial_items)

        def get_review_items(_reviewer_id, peer_id, _group_id, _content_id):
            return store.get(peer_id, [])

        def submit_peer_review_items(reviewer_id, peer_id, group_id,
                                     content_id, data):
            new_items = [
                mri(reviewer_id,
                    question_id,
                    peer=peer_id,
                    content_id=content_id,
                    answer=answer,
                    group=group_id)
                for question_id, answer in data.iteritems() if len(answer) > 0
            ]
            store[peer_id].extend(new_items)

        self.project_api_mock.get_peer_review_items = mock.Mock(
            side_effect=get_review_items)
        self.project_api_mock.submit_peer_review_items = mock.Mock(
            side_effect=submit_peer_review_items)

    def _assert_teammate_statuses(self, stage_element, expected_statuses):
        teammate_statuses = {
            int(peer.subject_id): peer.review_status
            for peer in stage_element.peers
        }
        self.assertEqual(teammate_statuses, expected_statuses)

    def test_rendering_questions(self):
        stage_element = self.get_stage(self.go_to_view())

        expected_options = {str(idx): str(idx) for idx in xrange(1, 11)}
        expected_options.update({"": "Rating"})

        questions = stage_element.form.questions
        self.assertEqual(questions[0].label, "How about that?")
        self.assertEqual(questions[0].control.name, "peer_score")
        self.assertEqual(questions[0].control.tag_name, "select")
        self.assertEqual(questions[0].control.options, expected_options)

        self.assertEqual(questions[1].label, "Were they helpful?")
        self.assertEqual(questions[1].control.name, "peer_q1")
        self.assertEqual(questions[1].control.tag_name, "select")
        self.assertEqual(questions[1].control.options, {"Y": "Yes", "N": "No"})

        self.assertEqual(questions[2].label, "General Comments")
        self.assertEqual(questions[2].control.name, "peer_q2")
        self.assertEqual(questions[2].control.tag_name, "textarea")

    def test_teammate_review_statuses(self):
        user_id = TestConstants.Users.USER1_ID
        reviews = {
            TestConstants.Users.USER2_ID: [
                mri(user_id,
                    self.REQUIRED_QUESTION_ID1,
                    peer=TestConstants.Users.USER2_ID,
                    answer='not empty'),
            ],
            TestConstants.Users.USER3_ID: [
                mri(user_id,
                    self.REQUIRED_QUESTION_ID1,
                    peer=TestConstants.Users.USER3_ID,
                    answer='not empty'),
                mri(user_id,
                    self.REQUIRED_QUESTION_ID2,
                    peer=TestConstants.Users.USER3_ID,
                    answer='other')
            ],
        }
        self._setup_review_items_store(reviews)

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        expected_statuses = {
            TestConstants.Users.USER2_ID: ReviewState.INCOMPLETE,
            TestConstants.Users.USER3_ID: ReviewState.COMPLETED
        }
        self._assert_teammate_statuses(stage_element, expected_statuses)

    @ddt.data(*KNOWN_USERS.keys())  # pylint: disable=star-args
    def test_interaction(self, user_id):
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        other_users = set(KNOWN_USERS.keys()) - {user_id}

        # A default selection should be made automatically.
        self.assertEquals(stage_element.form.peer_id, min(other_users))

        peers = stage_element.peers
        self.assertEqual(len(peers), len(other_users))
        for user_id, peer in zip(other_users, peers):
            self.assertEqual(peer.name, KNOWN_USERS[user_id].username)
            self.select_review_subject(peer)
            self.assertEqual(stage_element.form.peer_id, user_id)

    @ddt.data(*KNOWN_USERS.keys())  # pylint: disable=star-args
    def test_submission(self, user_id):
        stage_element = self.get_stage(self.go_to_view(student_id=user_id))
        self._setup_review_items_store()

        initial_statuses = {
            usr_id: ReviewState.NOT_STARTED
            for usr_id in KNOWN_USERS.keys() if usr_id != user_id
        }
        self._assert_teammate_statuses(stage_element,
                                       initial_statuses)  # precondition check

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        questions = stage_element.form.questions
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.assertTrue(stage_element.form.submit.is_displayed())
        self.assertEqual(stage_element.form.submit.text,
                         "Submit")  # first time here - should read Submit
        self.click_submit(stage_element)

        self.project_api_mock.submit_peer_review_items.assert_called_once_with(
            str(user_id), stage_element.form.peer_id, 1, self.activity_id,
            expected_submissions)

        expected_statuses = {
            usr_id: ReviewState.NOT_STARTED
            for usr_id in KNOWN_USERS.keys() if usr_id != user_id
        }
        expected_statuses[int(
            peer.subject_id
        )] = ReviewState.COMPLETED  # status is refreshed after submission
        self._assert_teammate_statuses(stage_element, expected_statuses)

    def test_persistence_and_resubmission(self):
        user_id = 1
        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        self.project_api_mock.get_peer_review_items.return_value = [{
            "question":
            question,
            "answer":
            answer,
            "user":
            TestConstants.Users.USER2_ID
        } for question, answer in expected_submissions.iteritems()]

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        # loading peer review items from project_api
        self.project_api_mock.get_peer_review_items.assert_called_with(
            str(user_id),
            stage_element.form.peer_id,
            1,
            self.activity_id,
        )

        questions = stage_element.form.questions
        self.assertEqual(questions[0].control.value,
                         expected_submissions["peer_score"])
        self.assertEqual(questions[1].control.value,
                         expected_submissions["peer_q1"])
        self.assertEqual(questions[2].control.value,
                         expected_submissions["peer_q2"])

        new_submissions = {
            "peer_score": "2",
            "peer_q1": "N",
            "peer_q2": "Awful"
        }

        questions[0].control.select_option(new_submissions["peer_score"])
        questions[1].control.select_option(new_submissions["peer_q1"])
        questions[2].control.fill_text(new_submissions["peer_q2"])

        self.assertEqual(stage_element.form.submit.text, "Resubmit")
        self.click_submit(stage_element)

        self.project_api_mock.submit_peer_review_items.assert_called_once_with(
            str(user_id), stage_element.form.peer_id, 1, self.activity_id,
            new_submissions)

    def test_completion(self):
        user_id = 1
        other_users = set(KNOWN_USERS.keys()) - {user_id}
        expected_submissions = {
            "peer_score": "10",
            "peer_q1": "Y",
            "peer_q2": "Awesome"
        }

        stage_element = self.get_stage(self.go_to_view(student_id=user_id))

        self.project_api_mock.get_peer_review_items_for_group.return_value = [
            {
                "question": question,
                "answer": answer,
                "workgroup": 1,
                "user": peer_id,
                "reviewer": str(user_id),
                "content_id": self.activity_id,
            } for question, answer in expected_submissions.iteritems()
            for peer_id in other_users
        ]

        peer = stage_element.peers[0]
        self.select_review_subject(peer)

        questions = stage_element.form.questions
        self.wait_for_ajax()
        questions[0].control.select_option(expected_submissions["peer_score"])
        questions[1].control.select_option(expected_submissions["peer_q1"])
        questions[2].control.fill_text(expected_submissions["peer_q2"])

        self.submit_and_assert_completion_published(stage_element, user_id)
 def setUp(self):
     super(BasePeerReviewStageTest, self).setUp()
     self.project_api_mock.get_workgroups_to_review = mock.Mock(return_value=OTHER_GROUPS.values())
     self.project_api_mock.get_workgroup_reviewers = mock.Mock(return_value=[
         {"id": user.id} for user in KNOWN_USERS.values()
     ])
class TestBaseGroupActivityStage(BaseStageTest):
    """ Tests for Base Stage Class """
    block_to_test = DummyStageBlock

    def setUp(self):
        super(TestBaseGroupActivityStage, self).setUp()
        self.render_template_patch = self.make_patch(self.block,
                                                     'render_template')

    # invalid name in order to match camlCase naming of assertSomething methods
    def assertDictionaryEqual(self, actual, expected, strict=False):  # pylint: disable=invalid-name
        """
        Less strict assert for dictionary equality - checks that `actual` contains all the keys from expected, and
        values match. If `strict` is true, also checks that no other keys are present in the `actual` (roughly equal
        to plain assertEqual)
        :param dict[Any, Any] actual: actual result
        :param dict[Any, ANy] expected: expected result
        :param bool strict: Strict comparison:
            If set to False allows `actual` to contain keys not found in `expected`.
            If True - requires that no other keys are present in `actual` - roughly equivalent to plain assertEqual
        """
        for key, value in expected.iteritems():
            self.assertIn(key, actual)
            self.assertEqual(actual[key], value)

        if strict:
            self.assertEqual(actual.keys(), expected.keys())

    @ddt.data(
        ([WORKGROUP], KNOWN_USERS.values(), StageState.COMPLETED,
         make_stats(1.0, 0, 0)),
        ([WORKGROUP], KNOWN_USERS.values(), StageState.INCOMPLETE,
         make_stats(0.3, 0.4, 0.3)),
        (OTHER_GROUPS, KNOWN_USERS.values(), StageState.INCOMPLETE,
         make_stats(0.3, 0.4, 0.3)),
        (OTHER_GROUPS, KNOWN_USERS.values(), StageState.NOT_STARTED,
         make_stats(0.0, 0.0, 1.0)),
    )
    @ddt.unpack
    def test_dashboard_view(self, workgroups, target_students, state, stats):
        render_template_response = u'render_template_response'
        is_ta_graded = False
        patched_stats = self.make_patch(self.block,
                                        'get_dashboard_stage_state')
        patched_stats.return_value = (state, stats)
        type(self.activity_mock).is_ta_graded = mock.PropertyMock(
            return_value=is_ta_graded)
        self.render_template_patch.return_value = render_template_response

        context = make_context(workgroups, target_students, [])

        expected_human_stats = get_human_stats(stats[StageState.COMPLETED],
                                               stats[StageState.INCOMPLETE],
                                               stats[StageState.NOT_STARTED])
        expected_context = {
            'stage': self.block,
            'stats': expected_human_stats,
            'stage_state': state,
            'ta_graded': is_ta_graded
        }

        with mock.patch('group_project_v2.stage.base.Fragment.add_content'
                        ) as patched_add_content:
            self.block.dashboard_view(context)

            patched_add_content.assert_called_once_with(
                render_template_response)

        # single call - roughly equivalent to assert_called_once_with(...)
        self.assertEqual(len(self.render_template_patch.call_args_list), 1)
        args, kwargs = self.render_template_patch.call_args_list[0]
        self.assertEqual(kwargs, {})
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], 'dashboard_view')
        self.assertDictionaryEqual(args[1], expected_context)

    @ddt.data(
        # not filtered - pass all students
        ([WORKGROUP], KNOWN_USERS.values(), [], KNOWN_USERS.values()),
        # single filter hit - pass all except that student
        ([WORKGROUP], KNOWN_USERS.values(), [TEST_USERS.USER1_ID],
         [KNOWN_USERS[TEST_USERS.USER2_ID], KNOWN_USERS[TEST_USERS.USER3_ID]]),
        # multiple filter hits - pass all except that students
        ([WORKGROUP], KNOWN_USERS.values(), [
            TEST_USERS.USER1_ID, TEST_USERS.USER3_ID
        ], [KNOWN_USERS[TEST_USERS.USER2_ID]]),
        # filter "miss" - pass all
        ([WORKGROUP], KNOWN_USERS.values(), [TEST_USERS.UNKNOWN_USER
                                             ], KNOWN_USERS.values()),
        # filter hit and miss - pass all expcept hit
        ([WORKGROUP], KNOWN_USERS.values(),
         [TEST_USERS.USER2_ID, TEST_USERS.UNKNOWN_USER],
         [KNOWN_USERS[TEST_USERS.USER1_ID], KNOWN_USERS[TEST_USERS.USER3_ID]]),
        # filtered all - pass no students
        ([WORKGROUP], KNOWN_USERS.values(),
         [TEST_USERS.USER1_ID, TEST_USERS.USER2_ID, TEST_USERS.USER3_ID], []),
    )
    @ddt.unpack
    def test_dashboard_view_filters_students(self, workgroups, target_students,
                                             filtered_students,
                                             expected_students):
        patched_stats = self.make_patch(self.block,
                                        'get_dashboard_stage_state')
        patched_stats.return_value = (StageState.COMPLETED,
                                      make_stats(1.0, 0.0, 0.0))
        context = make_context(workgroups, target_students, filtered_students)

        with mock.patch('group_project_v2.stage.base.Fragment.add_content'):
            self.block.dashboard_view(context)

        patched_stats.assert_called_once_with(workgroups, expected_students)

    def test_get_external_group_status(self):
        self.assertEqual(self.block.get_external_group_status('irrelevant'),
                         StageState.NOT_AVAILABLE)

    def test_get_external_status_label(self):
        self.assertEqual(self.block.get_external_status_label('irrelevant'),
                         self.block.DEFAULT_EXTERNAL_STATUS_LABEL)
class TestBaseGroupActivityStage(BaseStageTest):
    """ Tests for Base Stage Class """
    block_to_test = DummyStageBlock

    def setUp(self):
        super(TestBaseGroupActivityStage, self).setUp()
        self.render_template_patch = self.make_patch(self.block,
                                                     'render_template')

    def test_stage_is_not_graded(self):
        self.assertFalse(self.block.is_graded_stage)

    def test_stage_is_not_shown_on_detail_dashboard(self):
        self.assertFalse(self.block.shown_on_detail_view)

    # invalid name in order to match camlCase naming of assertSomething methods
    def assertDictionaryEqual(self, actual, expected, strict=False):  # pylint: disable=invalid-name
        """
        Less strict assert for dictionary equality - checks that `actual` contains all the keys from expected, and
        values match. If `strict` is true, also checks that no other keys are present in the `actual` (roughly equal
        to plain assertEqual)
        :param dict[Any, Any] actual: actual result
        :param dict[Any, ANy] expected: expected result
        :param bool strict: Strict comparison:
            If set to False allows `actual` to contain keys not found in `expected`.
            If True - requires that no other keys are present in `actual` - roughly equivalent to plain assertEqual
        """
        for key, value in expected.iteritems():
            self.assertIn(key, actual)
            self.assertEqual(actual[key], value)

        if strict:
            self.assertEqual(actual.keys(), expected.keys())

    @ddt.data(
        ([WORKGROUP], KNOWN_USERS.values(), StageState.COMPLETED,
         make_stats(1.0, 0, 0)),
        ([WORKGROUP], KNOWN_USERS.values(), StageState.INCOMPLETE,
         make_stats(0.3, 0.4, 0.3)),
        (OTHER_GROUPS, KNOWN_USERS.values(), StageState.INCOMPLETE,
         make_stats(0.3, 0.4, 0.3)),
        (OTHER_GROUPS, KNOWN_USERS.values(), StageState.NOT_STARTED,
         make_stats(0.0, 0.0, 1.0)),
    )
    @ddt.unpack
    def test_dashboard_view(self, workgroups, target_students, state, stats):
        render_template_response = u'render_template_response'
        is_ta_graded = False
        patched_stats = self.make_patch(self.block,
                                        'get_dashboard_stage_state')
        patched_stats.return_value = (state, stats)
        type(self.activity_mock).is_ta_graded = mock.PropertyMock(
            return_value=is_ta_graded)
        self.render_template_patch.return_value = render_template_response

        context = make_context(workgroups, target_students, [])

        expected_human_stats = BaseGroupActivityStage.make_human_stats(stats)
        expected_context = {
            'stage': self.block,
            'stats': expected_human_stats,
            'stage_state': state,
            'ta_graded': is_ta_graded
        }

        with mock.patch('group_project_v2.stage.base.Fragment.add_content'
                        ) as patched_add_content:
            self.block.dashboard_view(context)

            patched_add_content.assert_called_once_with(
                render_template_response)

        # single call - roughly equivalent to assert_called_once_with(...)
        self.assertEqual(len(self.render_template_patch.call_args_list), 1)
        args, kwargs = self.render_template_patch.call_args_list[0]
        self.assertEqual(kwargs, {})
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], 'dashboard_view')
        self.assertDictionaryEqual(args[1], expected_context)

    @ddt.data((make_stats(0.4, 0.3, 0.4), (40, 30, 40)),
              (make_stats(0.1, 0.1, 0.8), (80, 10, 10)),
              (make_stats(None, None, None), (None, None, None)))
    @ddt.unpack
    def test_make_human_stats(self, stats, human_stats_data):
        stats_order = (StageState.NOT_STARTED, StageState.INCOMPLETE,
                       StageState.COMPLETED)
        actual_human_stats = BaseGroupActivityStage.make_human_stats(stats)

        expected_human_stats = OrderedDict([
            (StageState.get_human_name(stats_order[idx]),
             human_stats_data[idx]) for idx in range(3)
        ])

        self.assertEqual(actual_human_stats.keys(),
                         expected_human_stats.keys())

        for idx, human_stat in enumerate(expected_human_stats.items()):
            stat_name, stat_value = human_stat
            actual_stat_value = actual_human_stats[stat_name]
            self.assertAlmostEqual(stat_value, actual_stat_value)

    @ddt.data(
        # not filtered - pass all students
        ([WORKGROUP], KNOWN_USERS.values(), [], KNOWN_USERS.values()),
        # single filter hit - pass all except that student
        ([WORKGROUP], KNOWN_USERS.values(), [TEST_USERS.USER1_ID],
         [KNOWN_USERS[TEST_USERS.USER2_ID], KNOWN_USERS[TEST_USERS.USER3_ID]]),
        # multiple filter hits - pass all except that students
        ([WORKGROUP], KNOWN_USERS.values(), [
            TEST_USERS.USER1_ID, TEST_USERS.USER3_ID
        ], [KNOWN_USERS[TEST_USERS.USER2_ID]]),
        # filter "miss" - pass all
        ([WORKGROUP], KNOWN_USERS.values(), [TEST_USERS.UNKNOWN_USER
                                             ], KNOWN_USERS.values()),
        # filter hit and miss - pass all expcept hit
        ([WORKGROUP], KNOWN_USERS.values(),
         [TEST_USERS.USER2_ID, TEST_USERS.UNKNOWN_USER],
         [KNOWN_USERS[TEST_USERS.USER1_ID], KNOWN_USERS[TEST_USERS.USER3_ID]]),
        # filtered all - pass no students
        ([WORKGROUP], KNOWN_USERS.values(),
         [TEST_USERS.USER1_ID, TEST_USERS.USER2_ID, TEST_USERS.USER3_ID], []),
    )
    @ddt.unpack
    def test_dashboard_view_filters_students(self, workgroups, target_students,
                                             filtered_students,
                                             expected_students):
        patched_stats = self.make_patch(self.block,
                                        'get_dashboard_stage_state')
        patched_stats.return_value = (StageState.COMPLETED,
                                      make_stats(1.0, 0.0, 0.0))
        context = make_context(workgroups, target_students, filtered_students)

        with mock.patch('group_project_v2.stage.base.Fragment.add_content'):
            self.block.dashboard_view(context)

        patched_stats.assert_called_once_with(workgroups, expected_students)

    @ddt.data(
        ([1], [1], [], make_stats(1, 0, 0), True),
        (range(10), range(3), range(3, 6), make_stats(.3, .3, .4), True),
        ([], [], [], make_stats(None, None, None), False),
    )
    @ddt.unpack
    def test_get_stage_stats(self, all_user_ids, completed_user_ids,
                             partial_user_ids, expected_stats,
                             completions_called):
        all_users = [
            make_reduced_user_details(id=user_id) for user_id in all_user_ids
        ]
        completed_user_ids = set(completed_user_ids)
        partial_user_ids = set(partial_user_ids)
        patched_completions = self.make_patch(self.block,
                                              'get_users_completion')
        self.block.display_name = "dummy block"
        patched_completions.return_value = (completed_user_ids,
                                            partial_user_ids)

        target_workgroups = tuple()

        stats = self.block.get_stage_stats(target_workgroups, all_users)
        if completions_called:
            patched_completions.assert_called_once_with(
                target_workgroups, all_users)
        else:
            self.assertFalse(patched_completions.called)

        self.assertEqual(len(expected_stats), len(stats))

        for stat, value in stats.items():
            self.assertAlmostEqual(value, expected_stats[stat])

    def test_get_external_group_status(self):
        self.assertEqual(self.block.get_external_group_status('irrelevant'),
                         StageState.NOT_AVAILABLE)

    def test_get_external_status_label(self):
        self.assertEqual(self.block.get_external_status_label('irrelevant'),
                         self.block.DEFAULT_EXTERNAL_STATUS_LABEL)