## $Id: test_browser.py 9755 2012-12-01 10:05:53Z 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.students.accommodation import BedTicket
from waeup.kofa.testing import FunctionalTestCase
from waeup.kofa.interfaces import (
    IExtFileStore, IFileStoreNameChooser)
from waeup.kofa.students.interfaces import IStudentsUtils
from waeup.aaue.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'].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

        # 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_1']
        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("Create ticket").click()
        self.assertMatches('...Amount could not be determined...',
                           self.browser.contents)
        self.app['configuration']['2004'].school_fee_base = 6666.0
        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
        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_1']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...This type of payment has already been made...',
                           self.browser.contents)
        self.browser.open(self.payments_path + '/addop')
        # Also the other payments can be made
        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)
        # 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_1']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        # We can't add the 2nd instalment ticket because the
        # first one has not yet been approved.
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...1st school fee instalment has not yet been paid...',
                           self.browser.contents)
        # Ok, then we approve the first instalment ...
        self.browser.open(self.payments_path)
        ctrl = self.browser.getControl(name='val_id')
        p_id = ctrl.options[0]
        self.browser.open(self.payments_path + '/' + p_id + '/approve')
        # ... add the second instalment ...
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        # ... approve the second instalment ...
        ctrl = self.browser.getControl(name='val_id')
        p_id = ctrl.options[1]
        self.browser.open(self.payments_path + '/' + p_id + '/approve')
        # ... and finally add the 1st instalment for the next session
        # provided that student is returning.
        IWorkflowState(self.student).setState('returning')
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Session configuration object is not...',
                           self.browser.contents)
        # Uups, we forgot to add a session configuration for next session
        configuration = createObject('waeup.SessionConfiguration')
        configuration.academic_session = 2005
        self.app['configuration'].addSessionConfiguration(configuration)
        self.app['configuration']['2005'].school_fee_base = 7777.0
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           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.getLink("Add current session payment ticket").click()
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...Session configuration object is not...',
                           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))
        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
        self.app['configuration']['2004'].clearance_fee = 1234.0
        self.app['configuration']['2004'].school_fee_base = 6666.0
        utils = getUtility(IStudentsUtils)

        configuration = createObject('waeup.SessionConfiguration')
        configuration.academic_session = 2005
        self.app['configuration'].addSessionConfiguration(configuration)
        self.app['configuration']['2005'].school_fee_base = 7777.0

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

        IWorkflowState(self.student).setState('cleared')
        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
        self.assertEqual(payment.p_level, 100)
        self.assertEqual(payment.p_session, 2004)
        self.assertEqual(payment.amount_auth, 6666.0)
        self.assertEqual(payment.p_item, u'CERT1')
        self.assertEqual(error, None)

        # Add penalty fee ...
        # ... for cleared
        self.app['configuration']['2004'].penalty_ug = 99.0
        # ... for returning
        self.app['configuration']['2005'].penalty_ug = 88.0
        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
        self.assertEqual(payment.amount_auth, 6765.0)
        IWorkflowState(self.student).setState('returning')
        error, payment = utils.setPaymentDetails('schoolfee_1',self.student)
        self.assertEqual(payment.p_level, 200)
        self.assertEqual(payment.p_session, 2005)
        self.assertEqual(payment.amount_auth, 7865.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, 1234.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)

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

    def test_student_course_registration(self):

        # Add more courses
        self.course2 = createObject('waeup.Course')
        self.course2.code = 'COURSE2'
        self.course2.semester = 2
        self.course2.credits = 10
        self.course2.passmark = 40
        self.app['faculties']['fac1']['dep1'].courses.addCourse(
            self.course2)
        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
            self.course2, level=100)
        self.course3 = createObject('waeup.Course')
        self.course3.code = 'COURSE3'
        self.course3.semester = 3
        self.course3.credits = 10
        self.course3.passmark = 40
        self.app['faculties']['fac1']['dep1'].courses.addCourse(
            self.course3)
        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
            self.course3, level=100)

        # Login as student
        self.browser.open(self.login_path)
        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()
        # 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()
        # Student has not paid second instalment, therefore a level
        # with two course ticket was created (semester 1 and combined)
        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
        self.browser.getLink("100").click()
        self.browser.getLink("Edit course list").click()
        self.browser.getControl("Add course ticket").click()
        # Student can't add second semester course
        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
        self.assertFalse('<option value="COURSE2">' in self.browser.contents)

        # Let's remove level and see what happens after 2nd instalment payment
        del(self.student['studycourse']['100'])
        payment2 = createObject('waeup.StudentOnlinePayment')
        payment2.p_category = u'schoolfee_2'
        payment2.p_session = self.student.current_session
        self.student['payments']['anykey'] = payment2
        self.browser.open(self.studycourse_path)
        self.browser.getLink("Add course list").click()
        self.browser.getControl("Create course list now").click()
        # Still only 2 tickets have been created since payment ticket
        # was not paid
        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
        payment2.p_state = u'paid'
        del(self.student['studycourse']['100'])
        self.browser.open(self.studycourse_path)
        self.browser.getLink("Add course list").click()
        self.browser.getControl("Create course list now").click()
        # Now 2nd semester course has been added
        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 3)
        # Student can add second semester course
        self.browser.getLink("100").click()
        self.browser.getLink("Edit course list").click()
        self.browser.getControl("Add course ticket").click()
        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
        self.assertTrue('<option value="COURSE2">' in self.browser.contents)
        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
        return