## $Id: test_browser.py 14931 2017-12-21 23:02:33Z henrik $
##
## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import os
import shutil
import tempfile
from StringIO import StringIO
from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
from zope.component.hooks import setSite, clearSite
from zope.component import getUtility, createObject
from zope.interface import verify
from waeup.kofa.app import University
from waeup.kofa.students.tests.test_browser import (
    StudentsFullSetup, SAMPLE_IMAGE)
from waeup.kofa.students.accommodation import BedTicket
from waeup.kofa.testing import FunctionalTestCase
from waeup.kofa.browser.tests.test_pdf import samples_dir
from waeup.kofa.interfaces import (
    IExtFileStore, IFileStoreNameChooser)
from waeup.kofa.students.interfaces import IStudentsUtils
from waeup.fceokene.testing import FunctionalLayer


class StudentProcessorTest(FunctionalTestCase):
    """Perform some batching tests.
    """

    layer = FunctionalLayer

    def setUp(self):
        super(StudentProcessorTest, self).setUp()
        # Setup a sample site for each test
        app = University()
        self.dc_root = tempfile.mkdtemp()
        app['datacenter'].setStoragePath(self.dc_root)

        # Prepopulate the ZODB...
        self.getRootFolder()['app'] = app
        # we add the site immediately after creation to the
        # ZODB. Catalogs and other local utilities are not setup
        # before that step.
        self.app = self.getRootFolder()['app']
        # Set site here. Some of the following setup code might need
        # to access grok.getSite() and should get our new app then
        setSite(app)


    def tearDown(self):
        super(StudentProcessorTest, self).tearDown()
        shutil.rmtree(self.workdir)
        shutil.rmtree(self.dc_root)
        clearSite()
        return

class StudentUITests(StudentsFullSetup):
    """Tests for customized student class views and pages
    """

    layer = FunctionalLayer

    def setUp(self):
        super(StudentUITests, self).setUp()

        bedticket = BedTicket()
        bedticket.booking_session = 2004
        bedticket.bed_type = u'any bed type'
        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
        bedticket.bed_coordinates = u'My bed coordinates'
        self.student['accommodation'].addBedTicket(bedticket)

    def test_manage_payments(self):
        # Add missing configuration data
        self.app['configuration']['2004'].clearance_fee = 120.0
        self.app['configuration']['2004'].booking_fee = 150.0
        self.app['configuration']['2004'].maint_fee = 180.0

        # Managers can add online payment tickets
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.payments_path)
        self.browser.getLink("Add current session payment ticket").click()
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Wrong state...',
                           self.browser.contents)
        IWorkflowState(self.student).setState('cleared')
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        ctrl = self.browser.getControl(name='val_id')
        value = ctrl.options[0]
        self.browser.getLink(value).click()
        self.assertMatches('...Amount Authorized...',
                           self.browser.contents)
        # Managers can open payment slip
        self.browser.getLink("Download payment slip").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
        # Set ticket paid
        ticket = self.student['payments'].items()[0][1]
        ticket.p_state = 'paid'
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...This type of payment has already been made...',
                           self.browser.contents)
        # Remove all payments so that we can add a school fee payment again
        keys = [i for i in self.student['payments'].keys()]
        for payment in keys:
            del self.student['payments'][payment]
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.certificate.study_mode = 'nce_sw'
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['third_semester']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...could not be determined...',
                           self.browser.contents)
        self.certificate.study_mode = 'nce_ft'
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['third_semester']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...', self.browser.contents)
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(
            name="form.p_category").value = ['bed_allocation']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(
            name="form.p_category").value = ['hostel_maintenance']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['clearance']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.certificate.study_mode = 'pd_ft'
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        # In state returning we can add a new school fee ticket since
        # p_session and p_level is different
        IWorkflowState(self.student).setState('returning')
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        # Uups, we forgot to add a session configuration for next session
        self.assertTrue('Session configuration object is not available.'
            in self.browser.contents)
        configuration = createObject('waeup.SessionConfiguration')
        configuration.academic_session = 2005
        self.app['configuration'].addSessionConfiguration(configuration)
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)

        # In state admitted school fee can't be determined
        IWorkflowState(self.student).setState('admitted')
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Wrong state...',
                           self.browser.contents)

    def test_student_payments(self):
        # Login
        IWorkflowState(self.student).setState('returning')
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.student_id
        self.browser.getControl(name="form.password").value = 'spwd'
        self.browser.getControl("Login").click()
        self.browser.open(self.student_path + '/payments')
        self.assertTrue(
          'Add current session payment ticket' in self.browser.contents)
        self.assertFalse(
          'Add previous session payment ticket' in self.browser.contents)
        return

    def test_get_returning_data(self):
        # Student is in level 100, session 2004 with verdict A
        utils = getUtility(IStudentsUtils)
        self.assertEqual(utils.getReturningData(self.student),(2005, 200))
        self.student['studycourse'].current_verdict = 'C'
        self.assertEqual(utils.getReturningData(self.student),(2005, 110))
        self.student['studycourse'].current_verdict = 'D'
        self.assertEqual(utils.getReturningData(self.student),(2005, 100))
        self.student['studycourse'].current_verdict = 'O'
        self.assertEqual(utils.getReturningData(self.student),(2004, 110))
        return

    def test_set_payment_details(self):
        self.app['configuration']['2004'].booking_fee = 150.0
        self.app['configuration']['2004'].maint_fee = 180.0
        self.app['configuration']['2004'].clearance_fee = 120.0
        utils = getUtility(IStudentsUtils)

        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment, None)
        self.assertEqual(error, u'Wrong state.')

        IWorkflowState(self.student).setState('cleared')
        self.certificate.study_mode = 'pd_ft'
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 70450.0)
        self.assertEqual(payment.p_item, u'CERT1')
        self.assertEqual(error, None)

        IWorkflowState(self.student).setState('returning')
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual('Session configuration object is not available.', error)
        configuration = createObject('waeup.SessionConfiguration')
        configuration.academic_session = 2005
        self.app['configuration'].addSessionConfiguration(configuration)
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment.p_level, 200)
        self.assertEqual(payment.p_session, 2005)
        self.assertEqual(payment.amount_auth, 35450.0)
        self.assertEqual(payment.p_item, u'CERT1')
        self.assertEqual(error, None)

        # UG returning students pay 56150
        self.certificate.study_mode = 'ug_ft'
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment.amount_auth,  56300.0)
        self.assertEqual(error, None)
        # UG cleared students pay 65650
        IWorkflowState(self.student).setState('cleared')
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment.amount_auth, 65800.0)
        self.assertEqual(error, None)

        # NCE student payment can be disabled by
        # setting the base school fee to -1
        IWorkflowState(self.student).setState('returning')
        configuration = createObject('waeup.SessionConfiguration')
        self.app['configuration']['2004'].school_fee_base = -1.0
        self.certificate.study_mode = 'nce_ft'
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(error, u'School fee payment is disabled.')

        error, payment = utils.setPaymentDetails('clearance',self.student)
        self.assertEqual(error, u'Acceptance Fee payments not allowed.')
        IWorkflowState(self.student).setState('cleared')
        error, payment = utils.setPaymentDetails('clearance',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 270.0)
        self.assertEqual(payment.p_item, u'CERT1')
        self.assertEqual(error, None)

        self.student['studycourse'].current_session = 2005
        error, payment = utils.setPaymentDetails('hostel_maintenance',self.student)
        self.assertEqual(payment, None)
        self.assertEqual(error, 'No bed space allocated.')
        self.student['studycourse'].current_session = 2004

        error, payment = utils.setPaymentDetails('hostel_maintenance',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 1026.0)
        self.assertEqual(payment.p_item, u'My bed coordinates')
        self.assertEqual(error, None)

        error, payment = utils.setPaymentDetails('third_semester',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 8088.0)
        self.assertEqual(payment.p_item, u'')
        self.assertEqual(error, None)

        self.certificate.study_mode = u'nce_sw'
        error, payment = utils.setPaymentDetails('hostel_maintenance',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 697.5)  # 62.5% * 876 + 150
        self.assertEqual(payment.p_item, u'My bed coordinates')
        self.assertEqual(error, None)

        error, payment = utils.setPaymentDetails('bed_allocation',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 300.0)
        self.assertEqual(payment.p_item, u'')
        self.assertEqual(error, None)

        error, payment = utils.setPaymentDetails('schoolfee',self.student, 2004, 100)
        self.assertEqual(error, u'Previous session payment not yet implemented.')
        return

    def test_student_start_clearance(self):
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.student_id
        self.browser.getControl(name="form.password").value = 'spwd'
        self.browser.getControl("Login").click()

        IWorkflowInfo(self.student).fireTransition('admit')
        self.browser.open(self.student_path + '/change_portrait')
        image = open(SAMPLE_IMAGE, 'rb')
        ctrl = self.browser.getControl(name='passportuploadedit')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_photo.jpg')
        self.browser.getControl(
            name='upload_passportuploadedit').click()
        self.browser.open(self.student_path + '/start_clearance')
        # In Okene the ug students start clearance with activation code ...
        self.assertTrue('Activation Code:' in self.browser.contents)
        self.browser.getControl("Start clearance now").click()
        self.assertTrue('Activation code is invalid' in self.browser.contents)
        # ... and nce students without.
        self.certificate.study_mode = 'nce_ft'
        self.browser.open(self.student_path + '/start_clearance')
        self.assertFalse('Activation Code:' in self.browser.contents)
        self.browser.getControl("Start clearance now").click()
        self.assertTrue(
            'Clearance process has been started' in self.browser.contents)

    def test_open_slips(self):
        # Managers can open clearance slip
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.student_path + '/view_clearance')
        self.browser.getLink("Download clearance slip").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')

    def test_student_accommodation(self):
        del self.student['accommodation']['2004']
        self.certificate.study_mode = 'ug_pt'
        # Login
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.student_id
        self.browser.getControl(name="form.password").value = 'spwd'
        self.browser.getControl("Login").click()

        # Students can book accommodation without AC ...
        self.browser.open(self.acco_path)
        IWorkflowInfo(self.student).fireTransition('admit')
        self.browser.getControl("Book accommodation").click()
        self.assertFalse('Activation Code:' in self.browser.contents)
        self.browser.getControl("Create bed ticket").click()
        # Bed is randomly selected but, since there is only
        # one bed for this student, we know that
        self.assertEqual(self.student['accommodation']['2004'].bed_coordinates,
            'Hall 1, Block A, Room 101, Bed A (regular_male_fr)')
        self.assertEqual(self.student['accommodation']['2004'].display_coordinates,
            '(see payment slip)')
        # But the bed coordinates are hidden.
        self.assertFalse('Hall 1, Block A, Room 101, Bed A'
            in self.browser.contents)
        self.assertTrue('<td>(see payment slip)</td>'
            in self.browser.contents)
        return

    def test_admission_slip_link(self):
        # Login
        IWorkflowState(self.student).setState('admitted')
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.student_id
        self.browser.getControl(name="form.password").value = 'spwd'
        self.browser.getControl("Login").click()
        self.assertFalse(
          'Download admission letter' in self.browser.contents)
        IWorkflowState(self.student).setState('clearance started')
        self.browser.open(self.student_path)
        self.assertTrue(
          'Download admission letter' in self.browser.contents)
        return

    def test_payment_disabled(self):
        self.certificate.study_mode = 'nce_ft'
        IWorkflowState(self.student).setState('cleared')
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.payments_path)
        self.browser.getLink("Add current session payment ticket").click()
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.app['configuration']['2004'].payment_disabled = ['sf_nce1']
        self.browser.getLink("Add current session payment ticket").click()
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...This category of payments has been disabled...',
                           self.browser.contents)
        self.certificate.study_mode = 'ug_ft'
        self.browser.open(self.payments_path)
        self.browser.getLink("Add current session payment ticket").click()
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        return

    def test_student_course_registration(self):
        IWorkflowState(self.student).setState('school fee paid')
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.student_id
        self.browser.getControl(name="form.password").value = 'spwd'
        self.browser.getControl("Login").click()
        # Now students can add the current study level
        self.browser.getLink("Study Course").click()
        self.browser.getLink("Add course list").click()
        self.assertMatches('...Add current level 100 (Year 1)...',
                           self.browser.contents)
        self.browser.getControl("Create course list now").click()
        # Students can't open the customized pdf course registration slip
        self.browser.open(
            self.student_path + '/studycourse/100/course_registration_slip.pdf')
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'course_registration_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample PDF course_registration_slip.pdf written to %s" % path