class TestRestPassedInPreviousPeriod(TransactionTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['old:begins(-14):ends(6)', # 14 months ago
                                     'cur:begins(-2)'], # 2 months ago
                            assignments=['a1:admin(adm):pub(0)']) # 0 days after period begins
        self.client = RestClient()
        self.url = '/devilry_subjectadmin/rest/passedinpreviousperiod/{0}'.format(self.testhelper.sub_cur_a1.id)

    def _getas(self, username, **data):
        self.client.login(username=username, password='******')
        return self.client.rest_get(self.url, **data)

    def _test_get_simple_as(self, username):
        self.testhelper.add_to_path('uni;sub.cur.a1.g1:candidate(student1)')
        content, response = self._getas(username)
        self.assertEquals(response.status_code, 200)
        self.assertEquals(len(content), 1)
        item = content[0]
        self.assertEquals(item['group']['id'], self.testhelper.sub_cur_a1_g1.id)
        self.assertEquals(item['oldgroup'], None)
        self.assertEquals(item['whyignored'], 'not_in_previous')

    def test_get_simple_as_assignmentadmin(self):
        self._test_get_simple_as('adm')

    def test_get_simple_as_superuser(self):
        self.testhelper.create_superuser('super')
        self._test_get_simple_as('super')

    def test_get_as_nobody(self):
        self.testhelper.create_user('nobody')
        content, response = self._getas('nobody')
        self.assertEquals(response.status_code, 403)


    def test_with_oldfeedback(self):
        # Add a group for the student on the old period
        self.testhelper.add_to_path('uni;sub.old.a1.g1:candidate(student1):examiner(examiner1).d1:ends(1)')
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_old_a1_g1, {'grade': 'approved', 'points': 1, 'is_passing_grade': True})
        )

        # Add the student to the current period
        self.testhelper.add_to_path('uni;sub.cur.a1.g1:candidate(student1)')

        content, response = self._getas('adm')
        self.assertEquals(response.status_code, 200)
        self.assertEquals(len(content), 1)
        item = content[0]
        self.assertEquals(item['group']['id'], self.testhelper.sub_cur_a1_g1.id)
        self.assertEquals(item['whyignored'], None)
        self.assertNotEquals(item['oldgroup'], None)
        oldgroup = item['oldgroup']
        self.assertEqual(oldgroup['id'], self.testhelper.sub_old_a1_g1.id)
        self.assertEqual(oldgroup['assignment']['id'], self.testhelper.sub_old_a1.id)
        self.assertEqual(oldgroup['period']['id'], self.testhelper.sub_old.id)
        self.assertEqual(oldgroup['feedback_shortformat'], 'true')



    def _putas(self, username, data):
        self.client.login(username=username, password='******')
        return self.client.rest_put(self.url, data)

    def _test_putas(self, username):
        self.testhelper.add_to_path('uni;sub.cur.a1.g1:candidate(student1).d1')
        self.testhelper.add_to_path('uni;sub.cur.a1.g2:candidate(student2).d1')
        self.testhelper.add_to_path('uni;sub.cur.a1.g3:candidate(student3).d1')

        self.testhelper.add_to_path('uni;sub.old.a1.g3:candidate(student3):examiner(examiner1).d1')
        oldg3_delivery = self.testhelper.add_delivery(self.testhelper.sub_old_a1_g3)
        self.testhelper.add_feedback(oldg3_delivery,
                                     verdict=dict(grade='C', points=40,
                                                  is_passing_grade=True))

        g1 = self.testhelper.sub_cur_a1_g1
        g2 = self.testhelper.sub_cur_a1_g2
        g3 = self.testhelper.sub_cur_a1_g3
        content, response = self._putas(username,
                                        [{'id': g1.id,
                                          'newfeedback_shortformat': 'true'},
                                         {'id': g3.id,
                                          'newfeedback_shortformat': 'true'}])
        self.assertEquals(response.status_code, 200)

        g1 = self.testhelper.reload_from_db(g1)
        self.assertEquals(g1.feedback.grade, 'approved')
        self.assertEquals(g1.feedback.delivery.delivery_type, deliverytypes.ALIAS)

        g2 = self.testhelper.reload_from_db(g2)
        self.assertEquals(g2.feedback, None)

        g3 = self.testhelper.reload_from_db(g3)
        self.assertEquals(g3.feedback.grade, 'approved')
        self.assertEquals(g3.feedback.delivery.delivery_type, deliverytypes.ALIAS)
        self.assertEquals(g3.feedback.delivery.alias_delivery, oldg3_delivery)

    def test_put_as_assignmentadmin(self):
        self._test_putas('adm')

    def test_put_as_superuser(self):
        self.testhelper.create_superuser('super')
        self._test_putas('super')

    def test_put_as_nobody(self):
        self.testhelper.create_user('nobody')
        content, response = self._putas('nobody', [])
        self.assertEquals(response.status_code, 403)


    def test_put_shortformat_validationerror(self):
        self.testhelper.add_to_path('uni;sub.cur.a1.g1:candidate(student1).d1')
        g1 = self.testhelper.sub_cur_a1_g1
        content, response = self._putas('adm',
            [{'id': g1.id,
              'newfeedback_shortformat': 'invalidstuff'}])
        self.assertEquals(response.status_code, 400)
        self.assertEquals(content['errors'][0], u'Must be one of: true, false.')
class TestPassedPreviousPeriod(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.create_user('student1', 'Student One')
        self.testhelper.add(nodes='uni',
            subjects=['sub:admin(subadmin)'],
            periods=['p1:begins(-14):ends(6)',
                     'p2:begins(-2):ends(6)'],
            assignments=['a1:pub(1)'],
            assignmentgroups=[
                'g1:candidate(student1):examiner(examiner1)'
            ],
            deadlines=['d1:ends(10)']
        )
        self.assignment = self.testhelper.sub_p1_a1

    def _loginTo(self, username, assignmentid):
        self.loginTo(username, '/assignment/{id}/@@passed-previous-period'.format(id=assignmentid))

    def _find_gridrows(self, grid):
        return grid.find_elements_by_css_selector('.x-grid-row')

    def _get_row_by_group(self, grid, group):
        cssselector = '.groupinfo_{id}'.format(id=group.id)
        self.waitFor(grid, lambda g: len(grid.find_elements_by_css_selector(cssselector)) == 1)
        for row in self._find_gridrows(grid):
            matches = row.find_elements_by_css_selector(cssselector)
            if len(matches) > 0:
                return row
        raise ValueError('Could not find any rows matching the following group: {0}.'.format(group))

    def _click_group_selector(self, grid, group):
        self._get_row_by_group(grid, group).find_element_by_css_selector('.x-grid-row-checker').click()

    def _get_selectgroupsgrid(self):
        return self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_selectpassedpreviousgroupssgrid')

    def _get_editgradegrid(self):
        return self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editpassedpreviousgroupssgrid')

    def _set_grade_for_group(self, group, value):
        grid = self._get_editgradegrid()
        row = self._get_row_by_group(grid, group)
        tds = row.find_elements_by_css_selector('td')
        tds[1].click()
        editorselector = '.x-grid-editor input[type=text]'
        inputfield = self.waitForAndFindElementByCssSelector(editorselector)
        inputfield.clear()
        inputfield.send_keys(value)
        grid.click()
        self.waitForNotDisplayed(inputfield)

    def _wait_for_rowcount(self, grid, rowcount):
        self.waitFor(grid, lambda g: len(self._find_gridrows(g)) == rowcount)

    def _click_show_hidden_groups_checkbox(self):
        button = self.waitForAndFindElementByCssSelector(
            '.devilry_subjectadmin_passedpreviousperiodoverview .showUnRecommendedCheckbox input[type=button]')
        button.click()

    def test_render(self):
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p1_a1_g1, {'grade': 'B', 'points': 86, 'is_passing_grade': True})
        )
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)

        p2_a1_g1 = self.testhelper.sub_p2_a1_g1
        grid = self._get_selectgroupsgrid()
        self.assertEquals(len(self._find_gridrows(grid)), 1)
        row = self._get_row_by_group(grid, p2_a1_g1)
        self.assertEqual(row.find_element_by_css_selector('.groupinfo .names').text.strip(), 'Student One')
        self.assertEqual(row.find_element_by_css_selector('.groupinfo .usernames').text.strip(), 'student1')

    def _test_whyignored(self, group, whyignored_class, whyignored_text):
        grid = self._get_selectgroupsgrid()
        self.assertEquals(len(self._find_gridrows(grid)), 0)
        self._click_show_hidden_groups_checkbox()
        self._wait_for_rowcount(grid, 1)
        row = self._get_row_by_group(grid, group)
        self.assertEqual(row.find_element_by_css_selector('.groupinfo .names').text.strip(), 'Student One')
        cssselector = '.oldgroup_or_ignoredinfo .whyignored_{0}'.format(whyignored_class)
        self.assertEqual(1,
            len(row.find_elements_by_css_selector(cssselector)))
        self.assertEqual(row.find_element_by_css_selector('.oldgroup_or_ignoredinfo .whyignored').text.strip(),
            whyignored_text)

    def test_ignored_only_failing_grade_in_previous(self):
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p1_a1_g1, {'grade': 'F', 'points': 0, 'is_passing_grade': False})
        )
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._test_whyignored(self.testhelper.sub_p2_a1_g1,
            'only_failing_grade_in_previous',
            'The student has delivered this assignment previously, but never achieved a passing grade.')

    def test_ignored_has_alias_feedback(self):
        # Add an alias delivery to the P2 assignment
        delivery = self.testhelper.add_delivery(self.testhelper.sub_p2_a1_g1)
        delivery.delivery_type = deliverytypes.ALIAS
        delivery.save()
        self.testhelper.add_feedback(delivery, verdict={'grade': 'B', 'points': 86, 'is_passing_grade': True})

        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._test_whyignored(self.testhelper.sub_p2_a1_g1,
            'has_alias_feedback',
            'Is already marked as previously passed.')

    def test_ignored_has_feedback(self):
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p2_a1_g1, {'grade': 'A', 'points': 70, 'is_passing_grade': True})
        )
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._test_whyignored(self.testhelper.sub_p2_a1_g1,
            'has_feedback',
            'Group has feedback for this assignment.')

    def test_allignoredmessage(self):
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        warning = self.waitForAndFindElementByCssSelector('.devilry_extjsextras_floatingalertmessagelist .no-nonignoredgroups-warning')
        self.assertIn(
            'We did not detect any groups that Devilry does not believe should '\
            'be ignored. Use the checkbox below the grid to see and select ignored groups.',
            warning.text)


    def _click_nextbutton(self):
        button = self.waitForAndFindElementByCssSelector(
            '.devilry_subjectadmin_passedpreviousperiodoverview .nextButton button')
        self.waitForEnabled(button)
        button.click()

    def _click_savebutton(self):
        button = self.waitForAndFindElementByCssSelector(
            '.devilry_subjectadmin_passedpreviousperiodoverview .saveButton button')
        self.waitForEnabled(button)
        button.click()

    def test_page2_boolwidget(self):
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p1_a1_g1, {'grade': 'B', 'points': 86, 'is_passing_grade': True})
        )
        self._click_nextbutton()
        self.waitForText('Make sure you really want to mark these groups')
        pagetwosidebar = self.waitForAndFindElementByCssSelector(
            '.devilry_subjectadmin_passedpreviousperiodoverview .pageTwoSidebar')
        self.assertNotIn('Grade format', pagetwosidebar.text)

    def _set_grade_editor(self, assignment, gradeeditorid, config):
        assignment.gradeeditor_config.gradeeditorid = gradeeditorid
        assignment.gradeeditor_config.config = config
        assignment.gradeeditor_config.save()

    def test_page2_nonboolwidget_sidebar(self):
        self._set_grade_editor(self.testhelper.sub_p2_a1, 'basicform',
            json.dumps({'approvedLimit': 12}))
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p1_a1_g1, {'grade': 'B', 'points': 86, 'is_passing_grade': True})
        )
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._click_nextbutton()
        pagetwosidebar = self.waitForAndFindElementByCssSelector(
            '.devilry_subjectadmin_passedpreviousperiodoverview .pageTwoSidebar')
        gradeformathelp = self.waitForAndFindElementByCssSelector('.gradeformat-help', within=pagetwosidebar)
        self.assertIn('Must be a number. 12 points is required to pass.', gradeformathelp.text)


    def _test_save_autodetected(self, gradeeditor, config=None):
        self._set_grade_editor(self.testhelper.sub_p2_a1, gradeeditor, config)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 0)
        self.testhelper.create_feedbacks(
            (self.testhelper.sub_p1_a1_g1, {'grade': 'B', 'points': 86, 'is_passing_grade': True})
        )
        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._click_nextbutton()
        self._click_savebutton()
        successmessage = self.waitForAndFindElementByCssSelector(
            '.devilry_extjsextras_floatingalertmessagelist .passed-previously-sync-success')
        self.assertIn('Marked 1 groups as previously passed.', successmessage.text)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 1)

    def test_save_approved_autodetected(self):
        self._test_save_autodetected('approved')

    def test_save_basicform_autodetected(self):
        self._test_save_autodetected('basicform', config=json.dumps({'approvedLimit': 12}))


    def _test_save_manual(self, gradeeditor, config=None, value=None):
        self._set_grade_editor(self.testhelper.sub_p2_a1, gradeeditor, config)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 0)

        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._click_show_hidden_groups_checkbox()
        group = self.testhelper.sub_p2_a1_g1
        self._click_group_selector(self._get_selectgroupsgrid(), group)
        self._click_nextbutton()

        if value:
            self._set_grade_for_group(group, value)
        self._click_savebutton()
        successmessage = self.waitForAndFindElementByCssSelector(
            '.devilry_extjsextras_floatingalertmessagelist .passed-previously-sync-success')
        self.assertIn('Marked 1 groups as previously passed.', successmessage.text)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 1)


    def test_save_approved_manual(self):
        self._test_save_manual('approved')

    def test_save_basicform_manual(self):
        self._test_save_manual('basicform', config=json.dumps({'approvedLimit': 12}), value='20')


    def _test_save_manual_error(self, gradeeditor, config=None, invalidvalue=None):
        self._set_grade_editor(self.testhelper.sub_p2_a1, gradeeditor, config)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 0)

        self._loginTo('subadmin', self.testhelper.sub_p2_a1.id)
        self._click_show_hidden_groups_checkbox()
        group = self.testhelper.sub_p2_a1_g1
        self._click_group_selector(self._get_selectgroupsgrid(), group)
        self._click_nextbutton()

        self._set_grade_for_group(group, invalidvalue)
        self._click_savebutton()
        errormessage = self.waitForAndFindElementByCssSelector(
            '.devilry_extjsextras_floatingalertmessagelist .passed-previously-invalid-grade-value')
        self.assertIn('At least one group has an invalid grade.', errormessage.text)
        self.assertEquals(self.testhelper.sub_p2_a1_g1_d1.deliveries.count(), 0)

    def test_save_basicform_error(self):
        self._test_save_manual_error('basicform', config=json.dumps({'approvedLimit': 12}), invalidvalue='3')