## $Id: test_browser.py 8722 2012-06-14 08:07:41Z 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
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
from waeup.kofa.testing import FunctionalTestCase
from waeup.kofa.interfaces import (
    IExtFileStore, IFileStoreNameChooser)
from waeup.kofa.students.batching import StudentProcessor
from waeup.kofa.students.interfaces import IStudentsUtils
from waeup.fceokene.students.batching import CustomStudentProcessor
from waeup.fceokene.testing import FunctionalLayer
from waeup.fceokene.students.interfaces import (
    ICustomStudentStudyCourse, ICustomStudent,
    ICustomStudentStudyLevel, ICustomCourseTicket)


STUDENT_SAMPLE_DATA = open(
    os.path.join(os.path.dirname(__file__), 'sample_student_data.csv'),
    'rb').read()

STUDENT_HEADER_FIELDS = STUDENT_SAMPLE_DATA.split(
    '\n')[0].split(',')

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)

        self.processor_base = StudentProcessor()
        self.processor = CustomStudentProcessor()
        self.workdir = tempfile.mkdtemp()
        self.csv_file = os.path.join(self.workdir, 'sample_student_data.csv')
        open(self.csv_file, 'wb').write(STUDENT_SAMPLE_DATA)

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

    def test_import(self):
        # We have an empty column 'date_of_birth' in  the import file.
        # The original processor will fail because 'date_of_birth' is required
        # in the base package.
        num, num_warns, fin_file, fail_file = self.processor_base.doImport(
            self.csv_file, STUDENT_HEADER_FIELDS)
        self.assertEqual(num_warns,3)
        assert len(self.app['students'].keys()) == 0
        # The customized processor does not complain since 'date_of_birth' is
        # not required in the custom package.
        num, num_warns, fin_file, fail_file = self.processor.doImport(
            self.csv_file, STUDENT_HEADER_FIELDS)
        #print open(fail_file).read()
        self.assertEqual(num_warns,0)
        assert len(self.app['students'].keys()) == 3
        shutil.rmtree(os.path.dirname(fin_file))


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

    layer = FunctionalLayer

    def test_classes(self):
        # Let's see if objects created in the customized
        # portal really implement the customized interfaces
        verify.verifyObject(ICustomStudent, self.student)
        verify.verifyObject(
            ICustomStudentStudyCourse, self.student['studycourse'])
        studylevel = createObject(u'waeup.StudentStudyLevel')
        verify.verifyObject(ICustomStudentStudyLevel, studylevel)
        ticket = createObject(u'waeup.CourseTicket')
        verify.verifyObject(ICustomCourseTicket, ticket)
        IWorkflowState(self.student).setState('returning')
        # Let's see if next_session_allowed works as expected
        # A, ug_ft, 100
        self.assertTrue(self.student['studycourse'].next_session_allowed)
        # O, ug_ft, 100
        self.student['studycourse'].current_verdict = 'O'
        self.assertTrue(self.student['studycourse'].next_session_allowed)
        # O, ug_ft, 200
        self.student['studycourse'].current_level = 200
        self.assertFalse(self.student['studycourse'].next_session_allowed)
        # O, de_ft, 200
        self.student['studycourse'].certificate.study_mode = 'de_ft'
        self.assertTrue(self.student['studycourse'].next_session_allowed)
        # O, ph_ft, 300
        self.student['studycourse'].certificate.study_mode = 'ph_ft'
        self.student['studycourse'].current_level = 300
        self.assertTrue(self.student['studycourse'].next_session_allowed)
        # O, ph_ft, 400
        self.student['studycourse'].current_level = 400
        self.assertFalse(self.student['studycourse'].next_session_allowed)

        # Now we convert the certificate into a postgraduate certificate
        IWorkflowState(self.student).setState('school fee paid')
        self.certificate.study_mode = 'pg_ft'
        # ... and voila next session registration is allowed
        self.assertTrue(self.student['studycourse'].next_session_allowed)

    def test_manage_access(self):
        # Managers can access the pages of students
        # and can perform actions
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        # The student created in the base package is an ug student
        self.browser.open(self.student_path)
        self.browser.getLink("Clearance Data").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.clearance_path)
        self.browser.getLink("Manage").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.manage_clearance_path)
        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
        self.browser.getControl("Save").click()
        self.assertMatches('...Form has been saved...',
                           self.browser.contents)
        self.assertMatches('...First Sitting Record...',
                           self.browser.contents)
        # Managers can open clearance slip of ug students
        self.browser.open(self.student_path + '/clearance.pdf')
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
        # There is no pg field in the clearance form
        self.assertFalse('Second Higher Education Record'
            in self.browser.contents)
        # Now we change the study mode ...
        self.certificate.study_mode = 'pg_ft'
        self.browser.open(self.clearance_path)
        # ... and additional pg clearance fields appear
        self.assertMatches('...Second Higher Education Record...',
                           self.browser.contents)
        # But also fields from the ug form are displayed
        self.assertMatches('...First Sitting Record...',
                           self.browser.contents)
        # Managers can open clearance slip of pg students
        self.browser.open(self.student_path + '/clearance.pdf')
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')

    def test_manage_payments(self):
        # Add missing configuration data
        self.app['configuration']['2004'].gown_fee = 150.0
        self.app['configuration']['2004'].transfer_fee = 90.0
        #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.getControl("Add online payment ticket").click()
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Amount could not be determined...',
                           self.browser.contents)
        IWorkflowState(self.student).setState('cleared')
        self.browser.open(self.payments_path + '/addop')
        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("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("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 = ['gown']
        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 = ['transfer']
        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.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()
        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('...Amount could not be determined...',
                           self.browser.contents)

        # If the session configuration doesn't exist an error message will
        # be shown. No other requirement is being checked.
        del self.app['configuration']['2004']
        self.browser.open(self.payments_path)
        self.browser.getControl("Add online payment ticket").click()
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Session configuration object is not...',
                           self.browser.contents)

    def test_student_access(self):
        # Students can edit clearance data
        IWorkflowState(self.student).setState('cleared')
        self.student.clearance_locked = False
        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()
        # Even in state admitted students can't change the portait if
        # application slip exists.
        IWorkflowState(self.student).setState('admitted')
        self.browser.open(self.student_path)
        self.assertTrue('Change portrait' in self.browser.contents)
        file_store = getUtility(IExtFileStore)
        applicant_slip = 'My application slip'
        file_id = IFileStoreNameChooser(self.student).chooseName(
            attr="application_slip.pdf")
        file_store.createFile(file_id, StringIO(applicant_slip))
        self.browser.open(self.student_path)
        self.assertFalse('Change portrait' in self.browser.contents)
        self.browser.open(self.student_path + '/change_portrait')
        self.assertTrue('The requested form is locked' in self.browser.contents)
        # Student can view and edit clearance data
        self.browser.getLink("Clearance Data").click()
        self.browser.getLink("Edit").click()
        self.assertTrue('Save' in self.browser.contents)

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

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

        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment, None)
        self.assertEqual(error, u'Amount could not be determined.')

        IWorkflowState(self.student).setState('cleared')
        error, payment = utils.setPaymentDetails('schoolfee',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 40000.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(payment.p_level, 200)
        self.assertEqual(payment.p_session, 2005)
        self.assertEqual(payment.amount_auth, 20000.0)
        self.assertEqual(payment.p_item, u'CERT1')
        self.assertEqual(error, None)

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

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

        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, 180.0)
        self.assertEqual(payment.p_item, u'')
        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, 150.0)
        self.assertEqual(payment.p_item, u'')
        self.assertEqual(error, None)

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