## $Id: test_browser.py 8599 2012-06-02 08:11:58Z 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 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.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()
        # 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