Changeset 13935 for main/waeup.kofa/trunk/src/waeup/kofa/students/tests
- Timestamp:
- 14 Jun 2016, 01:38:12 (9 years ago)
- Location:
- main/waeup.kofa/trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.kofa/trunk
- Property svn:mergeinfo changed
/main/waeup.kofa/branches/uli-scores-upload merged: 13913-13934
- Property svn:mergeinfo changed
-
main/waeup.kofa/trunk/src/waeup/kofa
- Property svn:mergeinfo changed
/main/waeup.kofa/branches/uli-scores-upload/src/waeup/kofa merged: 13913-13934
- Property svn:mergeinfo changed
-
main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
r13908 r13935 1 # -*- coding: utf-8 -*- 1 2 ## $Id$ 2 3 ## … … 45 46 from waeup.kofa.university.faculty import Faculty 46 47 from waeup.kofa.university.department import Department 47 from waeup.kofa.interfaces import IUserAccount, IJobManager 48 from waeup.kofa.interfaces import IUserAccount, IJobManager, VALIDATED, CREATED 48 49 from waeup.kofa.authentication import LocalRoleSetEvent 49 50 from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED … … 55 56 SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg') 56 57 SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp') 58 URL_LECTURER_LANDING = 'http://localhost/app/my_courses' 57 59 58 60 … … 1807 1809 self.assertTrue(self.student_id in self.browser.contents) 1808 1810 1809 def test_handle_courses_by_lecturer(self):1810 self.app['users'].addUser('mrslecturer', 'mrslecturersecret')1811 self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'1812 self.app['users']['mrslecturer'].title = u'Mercedes Benz'1813 # Add course ticket1814 studylevel = createObject(u'waeup.StudentStudyLevel')1815 studylevel.level = 1001816 studylevel.level_session = 20041817 self.student['studycourse'].addStudentStudyLevel(1818 self.certificate, studylevel)1819 # Assign local Lecturer role for a certificate.1820 course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']1821 prmlocal = IPrincipalRoleManager(course)1822 prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')1823 notify(LocalRoleSetEvent(1824 course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))1825 # Login as lecturer.1826 self.browser.open(self.login_path)1827 self.browser.getControl(name="form.login").value = 'mrslecturer'1828 self.browser.getControl(1829 name="form.password").value = 'mrslecturersecret'1830 self.browser.getControl("Login").click()1831 self.assertMatches('...You logged in...', self.browser.contents)1832 # Lecturer is on landing page1833 self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)1834 # Lecturer can click the 'My Roles' link.1835 self.browser.getLink("My Roles").click()1836 self.assertMatches(1837 '...<div>Academics Officer (view only)</div>...',1838 self.browser.contents)1839 self.assertMatches(1840 '...<a href="http://localhost/app/'1841 'faculties/fac1/dep1/courses/COURSE1">...',1842 self.browser.contents)1843 self.assertFalse('(<span>COURSE1</span>)' in self.browser.contents)1844 # Lecturer can click the 'My Courses' link ...1845 self.browser.getLink("My Courses").click()1846 # ... and is is back on landing page1847 self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)1848 # The lecturer can go to her course ...1849 self.browser.getLink("COURSE1").click()1850 # Lecturers can neither filter students ...1851 self.assertRaises(1852 Unauthorized, self.browser.open,1853 "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/students")1854 # ... nor access the student ...1855 self.assertRaises(1856 Unauthorized, self.browser.open, self.student_path)1857 # ... nor the respective course ticket since1858 # editing course tickets by lecturers is not feasible.1859 self.assertTrue('COURSE1' in self.student['studycourse']['100'].keys())1860 course_ticket_path = self.student_path + '/studycourse/100/COURSE1'1861 self.assertRaises(1862 Unauthorized, self.browser.open, course_ticket_path)1863 # Course results can be batch edited via the edit_courses view.1864 self.app['faculties']['fac1']['dep1'].score_editing_disabled = True1865 self.browser.open(1866 "http://localhost/app/faculties/fac1/dep1/courses/COURSE1")1867 self.browser.getLink("Update scores").click()1868 self.assertTrue('Score editing disabled' in self.browser.contents)1869 self.app['faculties']['fac1']['dep1'].score_editing_disabled = False1870 self.browser.getLink("Update scores").click()1871 self.assertTrue(1872 'Current academic session not set' in self.browser.contents)1873 self.app['configuration'].current_academic_session = 20041874 self.browser.getLink("Update scores").click()1875 self.assertFalse(1876 '<input type="text" name="scores" class="span1" />'1877 in self.browser.contents)1878 IWorkflowState(self.student).setState('courses validated')1879 # Student must be in state 'courses validated'.1880 self.browser.open(1881 'http://localhost/app/faculties/fac1/'1882 'dep1/courses/COURSE1/edit_scores')1883 self.assertTrue(1884 'input type="text" name="scores"' in self.browser.contents)1885 self.browser.getControl(name="scores", index=0).value = '55'1886 self.browser.getControl("Update scores").click()1887 # New score has been set.1888 self.assertEqual(1889 self.student['studycourse']['100']['COURSE1'].score, 55)1890 # Score editing has been logged.1891 logfile = os.path.join(1892 self.app['datacenter'].storage, 'logs', 'students.log')1893 logcontent = open(logfile).read()1894 self.assertTrue('mrslecturer - students.browser.EditScoresPage - '1895 'K1000000 100/COURSE1 score updated (55)' in logcontent)1896 # Non-integer scores won't be accepted.1897 self.browser.open(1898 'http://localhost/app/faculties/fac1/'1899 'dep1/courses/COURSE1/edit_scores')1900 self.assertTrue('value="55" />' in self.browser.contents)1901 self.browser.getControl(name="scores", index=0).value = 'abc'1902 self.browser.getControl("Update scores").click()1903 self.assertTrue('Error: Score(s) of Anna Tester have not be updated'1904 in self.browser.contents)1905 # Scores can be removed.1906 self.browser.open(1907 'http://localhost/app/faculties/fac1/'1908 'dep1/courses/COURSE1/edit_scores')1909 self.browser.getControl(name="scores", index=0).value = ''1910 self.browser.getControl("Update scores").click()1911 self.assertEqual(1912 self.student['studycourse']['100']['COURSE1'].score, None)1913 logcontent = open(logfile).read()1914 self.assertTrue('mrslecturer - students.browser.EditScoresPage - '1915 'K1000000 100/COURSE1 score updated (None)' in logcontent)1916 # A course ticket slip can be downloaded1917 self.browser.open(1918 'http://localhost/app/faculties/fac1/'1919 'dep1/courses/COURSE1/coursetickets.pdf')1920 self.assertEqual(self.browser.headers['Status'], '200 Ok')1921 self.assertEqual(self.browser.headers['Content-Type'],1922 'application/pdf')1923 path = os.path.join(samples_dir(), 'coursetickets.pdf')1924 open(path, 'wb').write(self.browser.contents)1925 print "Sample PDF coursetickets.pdf written to %s" % path1926 1927 1811 def test_change_current_mode(self): 1928 1812 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') … … 3898 3782 ) 3899 3783 3900 def test_course_download_lecturer(self):3901 # We add study level 100 to the student's studycourse3902 studylevel = StudentStudyLevel()3903 studylevel.level = 1003904 studylevel.level_session = 20043905 IWorkflowState(self.student).setState('courses validated')3906 self.student['studycourse'].addStudentStudyLevel(3907 self.certificate,studylevel)3908 course1_path = 'http://localhost/app/faculties/fac1/dep1/courses/COURSE1'3909 # Create lecturer3910 self.app['users'].addUser('mrlecturer', 'mrlecturersecret')3911 self.app['users']['mrlecturer'].email = 'mrlecturer@foo.ng'3912 self.app['users']['mrlecturer'].title = 'Carlo Intelligent'3913 prmglobal = IPrincipalRoleManager(self.course)3914 prmglobal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrlecturer')3915 # Login as lecturer3916 self.browser.open(self.login_path)3917 self.browser.getControl(name="form.login").value = 'mrlecturer'3918 self.browser.getControl(name="form.password").value = 'mrlecturersecret'3919 self.browser.getControl("Login").click()3920 self.assertMatches('...You logged in...', self.browser.contents)3921 self.browser.open(course1_path)3922 self.assertFalse('Export' in self.browser.contents)3923 self.browser.getLink("Update scores").click()3924 self.assertTrue('Current academic session not set' in self.browser.contents)3925 self.app['configuration'].current_academic_session = 20043926 self.browser.getLink("Update scores").click()3927 self.browser.getLink("Download editable tickets").click()3928 self.assertEqual(self.browser.headers['Status'], '200 Ok')3929 self.assertEqual(self.browser.headers['Content-Type'],3930 'text/csv; charset=UTF-8')3931 self.assertEqual(self.browser.contents, 'matric_number,student_id,'3932 'display_fullname,level,code,level_session,score\r\n234,'3933 'K1000000,Anna Tester,100,COURSE1,2004,\r\n')3934 3935 3784 def test_export_departmet_officers(self): 3936 3785 # Create department officer … … 4004 3853 self.browser.getControl("Discard").click() 4005 3854 self.assertEqual(len(self.app['datacenter'].running_exports), 0) 3855 3856 3857 UPLOAD_CSV_TEMPLATE = ( 3858 'matric_number,student_id,display_fullname,level,code,level_session,' 3859 'score\r\n' 3860 '234,K1000000,Anna Tester,100,COURSE1,2004,%s\r\n') 3861 3862 class LecturerUITests(StudentsFullSetup): 3863 # Tests for UI actions when acting as lecturer. 3864 3865 def login_as_lecturer(self): 3866 self.app['users'].addUser('mrslecturer', 'mrslecturersecret') 3867 self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng' 3868 self.app['users']['mrslecturer'].title = u'Mercedes Benz' 3869 # Add course ticket 3870 studylevel = createObject(u'waeup.StudentStudyLevel') 3871 studylevel.level = 100 3872 studylevel.level_session = 2004 3873 self.student['studycourse'].addStudentStudyLevel( 3874 self.certificate, studylevel) 3875 # Assign local Lecturer role for a certificate. 3876 course = self.app['faculties']['fac1']['dep1'].courses['COURSE1'] 3877 prmlocal = IPrincipalRoleManager(course) 3878 prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer') 3879 notify(LocalRoleSetEvent( 3880 course, 'waeup.local.Lecturer', 'mrslecturer', granted=True)) 3881 # Login as lecturer. 3882 self.browser.open(self.login_path) 3883 self.browser.getControl(name="form.login").value = 'mrslecturer' 3884 self.browser.getControl( 3885 name="form.password").value = 'mrslecturersecret' 3886 self.browser.getControl("Login").click() 3887 # Store reused urls/paths 3888 self.course_url = ( 3889 'http://localhost/app/faculties/fac1/dep1/courses/COURSE1') 3890 self.edit_scores_url = '%s/edit_scores' % self.course_url 3891 # Set standard parameters 3892 self.app['configuration'].current_academic_session = 2004 3893 self.app['faculties']['fac1']['dep1'].score_editing_disabled = False 3894 IWorkflowState(self.student).setState(VALIDATED) 3895 3896 @property 3897 def stud_log_path(self): 3898 return os.path.join( 3899 self.app['datacenter'].storage, 'logs', 'students.log') 3900 3901 def test_lecturer_lands_on_landing_page(self): 3902 # lecturers can login and will be led to landing page. 3903 self.login_as_lecturer() 3904 self.assertMatches('...You logged in...', self.browser.contents) 3905 self.assertEqual(self.browser.url, URL_LECTURER_LANDING) 3906 3907 def test_my_roles_link_works(self): 3908 # lecturers can see their roles 3909 self.login_as_lecturer() 3910 self.browser.getLink("My Roles").click() 3911 self.assertTrue( 3912 "<div>Academics Officer (view only)</div>" 3913 in self.browser.contents) 3914 self.assertTrue( 3915 '<a href="%s">' % self.course_url in self.browser.contents) 3916 3917 def test_my_roles_page_contains_backlink(self): 3918 # we can get back from 'My Roles' view to landing page 3919 self.login_as_lecturer() 3920 self.browser.getLink("My Roles").click() 3921 self.browser.getLink("My Courses").click() 3922 self.assertEqual(self.browser.url, URL_LECTURER_LANDING) 3923 3924 def test_lecturers_can_reach_their_courses(self): 3925 # lecturers get links to their courses on the landing page 3926 self.login_as_lecturer() 3927 self.browser.getLink("COURSE1").click() 3928 self.assertEqual(self.browser.url, self.course_url) 3929 3930 def test_lecturers_student_access_is_restricted(self): 3931 # lecturers are not able to change other student data 3932 self.login_as_lecturer() 3933 # Lecturers can neither filter students ... 3934 self.assertRaises( 3935 Unauthorized, self.browser.open, '%s/students' % self.course_url) 3936 # ... nor access the student ... 3937 self.assertRaises( 3938 Unauthorized, self.browser.open, self.student_path) 3939 # ... nor the respective course ticket since editing course 3940 # tickets by lecturers is not feasible. 3941 self.assertTrue('COURSE1' in self.student['studycourse']['100'].keys()) 3942 course_ticket_path = self.student_path + '/studycourse/100/COURSE1' 3943 self.assertRaises( 3944 Unauthorized, self.browser.open, course_ticket_path) 3945 3946 def test_score_editing_requires_department_permit(self): 3947 # we get a warning if we try to update score while we are not allowed 3948 self.login_as_lecturer() 3949 self.app['faculties']['fac1']['dep1'].score_editing_disabled = True 3950 self.browser.open(self.course_url) 3951 self.browser.getLink("Update scores").click() 3952 self.assertTrue('Score editing disabled' in self.browser.contents) 3953 self.app['faculties']['fac1']['dep1'].score_editing_disabled = False 3954 self.browser.open(self.course_url) 3955 self.browser.getLink("Update scores").click() 3956 self.assertFalse('Score editing disabled' in self.browser.contents) 3957 3958 def test_score_editing_requires_validated_students(self): 3959 # we can edit only scores of students whose courses have been 3960 # validated. 3961 self.login_as_lecturer() 3962 # set invalid student state 3963 IWorkflowState(self.student).setState(CREATED) 3964 self.browser.open(self.edit_scores_url) 3965 self.assertRaises( 3966 LookupError, self.browser.getControl, name="scores") 3967 # set valid student state 3968 IWorkflowState(self.student).setState(VALIDATED) 3969 self.browser.open(self.edit_scores_url) 3970 self.assertTrue( 3971 self.browser.getControl(name="scores:list") is not None) 3972 3973 def test_score_editing_offers_only_current_scores(self): 3974 # only scores from current academic session can be edited 3975 self.login_as_lecturer() 3976 IWorkflowState(self.student).setState('courses validated') 3977 # with no academic session set 3978 self.app['configuration'].current_academic_session = None 3979 self.browser.open(self.edit_scores_url) 3980 self.assertRaises( 3981 LookupError, self.browser.getControl, name="scores") 3982 # with wrong academic session set 3983 self.app['configuration'].current_academic_session = 1999 3984 self.browser.open(self.edit_scores_url) 3985 self.assertRaises( 3986 LookupError, self.browser.getControl, name="scores") 3987 # with right academic session set 3988 self.app['configuration'].current_academic_session = 2004 3989 self.browser.reload() 3990 self.assertTrue( 3991 self.browser.getControl(name="scores:list") is not None) 3992 3993 def test_score_editing_can_change_scores(self): 3994 # we can really change scores via edit_scores view 3995 self.login_as_lecturer() 3996 self.assertEqual( 3997 self.student['studycourse']['100']['COURSE1'].score, None) 3998 self.browser.open(self.edit_scores_url) 3999 self.browser.getControl(name="scores:list", index=0).value = '55' 4000 self.browser.getControl("Update scores").click() 4001 # the new value is stored in data 4002 self.assertEqual( 4003 self.student['studycourse']['100']['COURSE1'].score, 55) 4004 # the new value is displayed on page/prefilled in form 4005 self.assertEqual( 4006 self.browser.getControl(name="scores:list", index=0).value, '55') 4007 # The change has been logged 4008 with open(self.stud_log_path, 'r') as fd: 4009 self.assertTrue( 4010 'mrslecturer - students.browser.EditScoresPage - ' 4011 'K1000000 100/COURSE1 score updated (55)' in fd.read()) 4012 4013 def test_scores_editing_scores_must_be_integers(self): 4014 # Non-integer scores won't be accepted. 4015 self.login_as_lecturer() 4016 self.browser.open(self.edit_scores_url) 4017 self.browser.getControl(name="scores:list", index=0).value = 'abc' 4018 self.browser.getControl("Update scores").click() 4019 self.assertTrue( 4020 'Error: Score(s) of following students have not been updated ' 4021 '(only integers are allowed): Anna Tester.' 4022 in self.browser.contents) 4023 4024 def test_scores_editing_allows_score_removal(self): 4025 # we can remove scores, once they were set 4026 self.login_as_lecturer() 4027 # without a prior value, we cannot remove 4028 self.student['studycourse']['100']['COURSE1'].score = None 4029 self.browser.open(self.edit_scores_url) 4030 self.browser.getControl(name="scores:list", index=0).value = '' 4031 self.browser.getControl("Update scores").click() 4032 logcontent = open(self.stud_log_path, 'r').read() 4033 self.assertFalse('COURSE1 score updated (None)' in logcontent) 4034 # now retry with some value set 4035 self.student['studycourse']['100']['COURSE1'].score = 55 4036 self.browser.getControl(name="scores:list", index=0).value = '' 4037 self.browser.getControl("Update scores").click() 4038 logcontent = open(self.stud_log_path, 'r').read() 4039 self.assertTrue('COURSE1 score updated (None)' in logcontent) 4040 4041 def test_lecturers_can_download_course_tickets(self): 4042 # A course ticket slip can be downloaded 4043 self.login_as_lecturer() 4044 pdf_url = '%s/coursetickets.pdf' % self.course_url 4045 self.browser.open(pdf_url) 4046 self.assertEqual(self.browser.headers['Status'], '200 Ok') 4047 self.assertEqual( 4048 self.browser.headers['Content-Type'], 'application/pdf') 4049 path = os.path.join(samples_dir(), 'coursetickets.pdf') 4050 open(path, 'wb').write(self.browser.contents) 4051 print "Sample PDF coursetickets.pdf written to %s" % path 4052 4053 def test_lecturers_can_download_scores_as_csv(self): 4054 # Lecturers can download course scores as CSV. 4055 self.login_as_lecturer() 4056 self.browser.open(self.edit_scores_url) 4057 self.browser.getLink("Download editable tickets").click() 4058 self.assertEqual(self.browser.headers['Status'], '200 Ok') 4059 self.assertEqual(self.browser.headers['Content-Type'], 4060 'text/csv; charset=UTF-8') 4061 self.assertEqual(self.browser.contents, 'matric_number,student_id,' 4062 'display_fullname,level,code,level_session,score\r\n234,' 4063 'K1000000,Anna Tester,100,COURSE1,2004,\r\n') 4064 4065 def test_scores_csv_upload_available(self): 4066 # lecturers can upload a CSV file to set values. 4067 self.login_as_lecturer() 4068 # set value to change from 4069 self.student['studycourse']['100']['COURSE1'].score = 55 4070 self.browser.open(self.edit_scores_url) 4071 upload_ctrl = self.browser.getControl(name='uploadfile:file') 4072 upload_file = StringIO(UPLOAD_CSV_TEMPLATE % '65') 4073 upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv') 4074 self.browser.getControl("Update scores").click() 4075 # value changed 4076 self.assertEqual( 4077 self.student['studycourse']['100']['COURSE1'].score, 65) 4078 4079 def test_scores_csv_upload_ignored(self): 4080 # for many type of file contents we simply ignore uploaded data 4081 self.login_as_lecturer() 4082 self.student['studycourse']['100']['COURSE1'].score = 55 4083 self.browser.open(self.edit_scores_url) 4084 for content, mimetype, name in ( 4085 # empty file 4086 ('', 'text/foo', 'my.foo'), 4087 # plain ASCII text, w/o comma 4088 ('abcdef' * 200, 'text/plain', 'my.txt'), 4089 # plain UTF-8 text, with umlauts 4090 ('umlauts: äöü', 'text/plain', 'my.txt'), 4091 # csv file with only a header row 4092 ('student_id,score', 'text/csv', 'my.csv'), 4093 # csv with student_id column missing 4094 ('foo,score\r\nbar,66\r\n', 'text/csv', 'my.csv'), 4095 # csv with score column missing 4096 ('student_id,foo\r\nK1000000,bar\r\n', 'text/csv', 'my.csv'), 4097 # csv with non number as score value 4098 (UPLOAD_CSV_TEMPLATE % 'not-a-number', 'text/csv', 'my.csv'), 4099 ): 4100 upload_ctrl = self.browser.getControl(name='uploadfile:file') 4101 upload_ctrl.add_file(StringIO(content), mimetype, name) 4102 self.browser.getControl("Update scores").click() 4103 self.assertEqual( 4104 self.student['studycourse']['100']['COURSE1'].score, 55) 4105 self.assertFalse( 4106 'Uploaded file contains illegal data' in self.browser.contents) 4107 4108 def test_scores_csv_upload_warn_illegal_chars(self): 4109 # for some types of files we issue a warning if upload data 4110 # contains illegal chars (and ignore the data) 4111 self.login_as_lecturer() 4112 self.student['studycourse']['100']['COURSE1'].score = 55 4113 self.browser.open(self.edit_scores_url) 4114 for content, mimetype, name in ( 4115 # plain ASCII text, commas, control chars 4116 ('abv,qwe\n\r\r\t\b\n' * 20, 'text/plain', 'my.txt'), 4117 # image data (like a JPEG image) 4118 (open(SAMPLE_IMAGE, 'rb').read(), 'image/jpg', 'my.jpg'), 4119 ): 4120 upload_ctrl = self.browser.getControl(name='uploadfile:file') 4121 upload_ctrl.add_file(StringIO(content), mimetype, name) 4122 self.browser.getControl("Update scores").click() 4123 self.assertEqual( 4124 self.student['studycourse']['100']['COURSE1'].score, 55) 4125 self.assertTrue( 4126 'Uploaded file contains illegal data' in self.browser.contents)
Note: See TracChangeset for help on using the changeset viewer.