#-*- mode: python; mode: fold -*-
# (C) Copyright 2005 AixtraWare <http://aixtraware.de>
# Author: Joachim Schmitz <js@aixtraware.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# 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.
#
# $Id: WAeUPTables.py 1686 2007-04-14 13:21:49Z joachim $

from zope.interface import implements
from Globals import InitializeClass
from Products.ZCatalog.ZCatalog import ZCatalog
from Products.ZCatalog.ProgressHandler import ZLogHandler
from AccessControl import ClassSecurityInfo
from Products.CMFCore.permissions import ModifyPortalContent

import DateTime,time
import csv,re
import logging
import Globals
p_home = Globals.package_home(globals())
i_home = Globals.INSTANCE_HOME

from interfaces import IWAeUPTable

class AttributeHolder(object):
    pass

def dict2ob(dict):
    ob = AttributeHolder()
    for key, value in dict.items():
        setattr(ob, key, value)
    return ob


class WAeUPTable(ZCatalog): ###(

    implements(IWAeUPTable)
    security = ClassSecurityInfo()

    def refreshCatalog(self, clear=0, pghandler=None):
        """ don't refresh for a normal table """

        if self.REQUEST and self.REQUEST.RESPONSE:
            self.REQUEST.RESPONSE.redirect(
              URL1 +
              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20refresh%20not%20implemented')

    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
        """ clears the whole enchilada """
        #self._catalog.clear()

        if REQUEST and RESPONSE:
            RESPONSE.redirect(
              URL1 +
              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')

    def addRecord(self, **data):
        # The uid is the same as "bed".
        uid = data[self.key]
        res = self.searchResults({"%s" % self.key : uid})
        if len(res) > 0:
            raise ValueError("More than one record with uid %s" % uid)
        self.catalog_object(dict2ob(data), uid=uid)
        return uid

    def deleteRecord(self, uid):
        #import pdb;pdb.set_trace()
        self.uncatalog_object(uid)

    def searchAndSetRecord(self, **data):
        raise NotImplemented

    def modifyRecord(self, **data):
        #records = self.searchResults(uid=uid)
        uid = data[self.key]
        records = self.searchResults({"%s" % self.key : uid})
        if len(records) > 1:
            # Can not happen, but anyway...
            raise ValueError("More than one record with uid %s" % uid)
        if len(records) == 0:
            raise KeyError("No record for uid %s" % uid)
        record = records[0]
        record_data = {}
        for field in self.schema() + self.indexes():
            record_data[field] = getattr(record, field)
        # Add the updated data:
        record_data.update(data)
        self.catalog_object(dict2ob(record_data), uid)

    def reindexIndex(self, name, REQUEST,pghandler=None):
        if isinstance(name, str):
            name = (name,)
        paths = self._catalog.uids.items()
        i = 0
        #import pdb;pdb.set_trace()
        for p,rid in paths:
            i += 1
            metadata = self.getMetadataForRID(rid)
            record_data = {}
            for field in name:
                record_data[field] = metadata.get(field)
            uid = metadata.get(self.key)
            self.catalog_object(dict2ob(record_data), uid, idxs=name,
                                update_metadata=0)

    security.declareProtected(ModifyPortalContent,"exportAllRecords")
    def exportAllRecords(self):
        "export a WAeUPTable"
        #import pdb;pdb.set_trace()
        fields = [field for field in self.schema()]
        format = ','.join(['"%%(%s)s"' % fn for fn in fields])
        csv = []
        csv.append(','.join(['"%s"' % fn for fn in fields]))
        for uid in self._catalog.uids:
            records = self.searchResults({"%s" % self.key : uid})
            if len(records) > 1:
                # Can not happen, but anyway...
                raise ValueError("More than one record with uid %s" % uid)
            if len(records) == 0:
                raise KeyError("No record for uid %s" % uid)
            rec = records[0]
            csv.append(format % rec)
        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
        open("%s/import/%s-%s.csv" % (i_home,self.getId(),current),"w+").write('\n'.join(csv))

###)

class AccommodationTable(WAeUPTable): ###(

    meta_type = 'WAeUP Accommodation Tool'
    name = "accommodation"
    key = "bed"
    def __init__(self):
        WAeUPTable.__init__(self, 'portal_accommodation')

    def searchAndReserveBed(self, student_id,bed_type):
        records = self.searchResults({'student' : student_id})
        if len(records) > 0:
            return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)

        records = [r for r in self.searchResults({'bed_type' : bed_type}) if not r.student]
        #import pdb;pdb.set_trace()
        if len(records) == 0:
            return -2,"No bed available"
        rec = records[0]
        self.modifyRecord(bed=rec.bed,student=student_id)
        s_logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
        s_logger.info('%s reserved bed %s' % (student_id,rec.bed))
        return 1,rec.bed


InitializeClass(AccommodationTable)

###)

class PinTable(WAeUPTable): ###(
    from ZODB.POSException import ConflictError
    meta_type = 'WAeUP Pin Tool'
    name = "pins"
    key = 'pin'
    def __init__(self):
        WAeUPTable.__init__(self, 'portal_pins')


    def searchAndSetRecord(self, uid, student_id,prefix):
        #records = self.searchResults(uid=uid)
        records = self.searchResults(student = student_id)
        #import pdb;pdb.set_trace()
        if len(records) > 0:
            for r in records:
                if r.pin != uid and r.prefix_batch.startswith(prefix):
                    return -2
        records = self.searchResults({"%s" % self.key : uid})
        if len(records) > 1:
            # Can not happen, but anyway...
            raise ValueError("More than one record with uid %s" % uid)
        if len(records) == 0:
            return -1
        record = records[0]
        if record.student == "":
            record_data = {}
            for field in self.schema() + self.indexes():
                record_data[field] = getattr(record, field)
            # Add the updated data:
            record_data['student'] = student_id
            try:
                self.catalog_object(dict2ob(record_data), uid)
                return 1
            except ConflictError:
                return 2
        if record.student.upper() != student_id.upper():
            return 0
        if record.student.upper() == student_id.upper():
            return 2
        return -3

InitializeClass(PinTable)

###)

class PumeResultsTable(WAeUPTable): ###(

    meta_type = 'WAeUP PumeResults Tool'
    name = "pumeresults"
    key = "jamb_reg_no"
    def __init__(self):
        WAeUPTable.__init__(self, 'portal_pumeresults')


InitializeClass(PumeResultsTable)

###)

class StudentsCatalog(WAeUPTable): ###(
    security = ClassSecurityInfo()

    meta_type = 'WAeUP Students Catalog'
    name = "students_catalog"
    key = "id"
    interesting_types = ('Student',
                         'StudentApplication',
                         'StudentCLearance',
                         'StudentPersonal',
                         'StudentStudyCourse',
                         )

    def __init__(self):
        WAeUPTable.__init__(self, 'students_catalog')
        return

    def get_review_state(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc): ###(
        "return the students review_state from portal_catalog"
        cat_res = self.portal_catalog(id = sid)
        if len(cat_res) != 1:
            return None
        return cat_res[0].review_state

    def get_course(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students study_course"
        if study_course_doc is None:
            return None
        return getattr(study_course_doc,'study_course',None)

    def get_department(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students department"
        if study_course_doc is None:
            return None
        certificate_res = self.portal_catalog(id = study_course_doc.study_course)
        if len(certificate_res) != 1:
            return None
        return certificate_res[0].getPath().split('/')[-3]

    def get_email(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students email from the personal"
        if personal_doc is None:
            return None
        return getattr(personal_doc,'email',None)

    def get_entry_mode(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students entry_mode from the application"
        if application_doc is None:
            return None
        return getattr(application_doc,'entry_mode',None)

    def get_jamb_reg_no(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students jamb_reg_no from the application"
        if application_doc is None:
            return None
        return getattr(application_doc,'jamb_reg_no',None)

    def get_faculty(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students faculty"
        if study_course_doc is None:
            return None
        certificate_res = self.portal_catalog(id = study_course_doc.study_course)
        if len(certificate_res) != 1:
            return None
        return certificate_res[0].getPath().split('/')[-4]

    def get_level(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students study_course"
        if study_course_doc is None:
            return None
        #from pdb import set_trace;set_trace()
        return getattr(study_course_doc,'current_level',None)

    def get_matric_no(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students matric_no from the clearance "
        if clearance_doc is None:
            return None
        return getattr(clearance_doc,'matric_no',None)

    def get_name(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students name from the personal"
        if personal_doc is None:
            return None
        doc = personal_doc
        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)

    def get_phone(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students phone from the personal"
        if personal_doc is None:
            return None
        return getattr(personal_doc,'phone',None)

    def get_sex(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students sex from the personal"
        if personal_doc is None:
            return None
        return getattr(personal_doc,'sex',None)

    def get_verdict(self,sid,application_doc,clearance_doc,personal_doc,study_course_doc):
        "return the students study_course"
        if study_course_doc is None:
            return None
        return getattr(study_course_doc,'current_verdict',None)
    ###)

    def refreshCatalog(self, clear=0, pghandler=None): ###(
        """ re-index everything we can find """
        students_folder = self.portal_url.getPortalObject().campus.students

        cat = self._catalog
        paths = self._catalog.uids.items()
        if clear:
            paths = tuple(paths)
            cat.clear()

        num_objects = len(paths)
        if pghandler:
            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
        for i in xrange(num_objects):
            if pghandler: pghandler.report(i)
            p = paths[i]
            sid = p[0]
            pcat_res = self.portal_catalog(id=sid)
            if len(pcat_res) != 1:
                continue
            student_brain = pcat_res[0]
            student_obj = student_brain.getObject()
            if student_obj.hasObject('application'):
                application_doc = getattr(student_obj,'application').getContent()
            else:
                application_doc = None
            if student_obj.hasObject('clearance'):
                clearance_doc = getattr(student_obj,'clearance').getContent()
            else:
                clearance_doc = None
            if student_obj.hasObject('personal'):
                personal_doc = getattr(student_obj,'personal').getContent()
            else:
                personal_doc = None
            if student_obj.hasObject('study_course'):
                study_course_doc = getattr(student_obj,'study_course').getContent()
            else:
                study_course_doc = None
            data = {}
            data['id'] = sid
            for field in self.schema():
                function = getattr(self,"get_%s" % field, None)
                if function is None:
                    continue
                value = function(sid,application_doc,clearance_doc,personal_doc,study_course_doc)
                if value is not None:
                    data[field] = value
            self.modifyRecord(**data)
        if pghandler: pghandler.finish()
    ###)

    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1):
        """ clear the catalog, then re-index everything """

        elapse = time.time()
        c_elapse = time.clock()

        pgthreshold = self._getProgressThreshold()
        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
        self.refreshCatalog(clear=0, pghandler=handler)

        elapse = time.time() - elapse
        c_elapse = time.clock() - c_elapse

        RESPONSE.redirect(
            URL1 +
            '/manage_catalogAdvanced?manage_tabs_message=' +
            urllib.quote('Catalog Updated \n'
                         'Total time: %s\n'
                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))


    security.declarePrivate('notify_event_listener')
    def notify_event_listener(self,event_type,object,infos):
        "listen for events"
        pt = object.portal_type
        mt = object.meta_type
        students_catalog = self.students_catalog
        #if pt not in self.interesting_types:
        #    return
        #print "%(pt)s\n %(event_type)s \n %(infos)s\n" % vars()
        if not infos.has_key('rpath'):
            return
        if pt == 'Student' and event_type == "workflow":
            pass
        elif mt == 'StudentApplication':
            if event_type not in ('sys_modify_object'):
                return
            print "%(pt)s\n %(event_type)s \n %(infos)s\n" % vars()
            from pdb import set_trace;set_trace()
            jamb_reg_no = getattr(object,'jamb_reg_no',None)
            if jamb_reg_no is None:
                return
            student_id = infos['rpath'].split('/')[2]
            self.fixName(student_id)
            student_entry = students_catalog(id = student_id)[0]
            if student_entry.jamb_reg_no == jamb_reg_no:
                return
            students_catalog.modifyRecord(id = student_id,
                                          jamb_reg_no = jamb_reg_no)
        elif mt == 'StudentPersonal':
            if event_type not in ('sys_modify_object'):
                return
            print "%(pt)s\n %(event_type)s \n %(infos)s\n" % vars()
            student_id = infos['rpath'].split('/')[2]
            self.fixName(student_id)


InitializeClass(StudentsCatalog)

###)

class CoursesCatalog(WAeUPTable): ###(

    meta_type = 'WAeUP Courses Catalog'
    name = "students_catalog"
    key = "code"
    def __init__(self):
        WAeUPTable.__init__(self, 'courses_catalog')


InitializeClass(CoursesCatalog)
###)

class OnlinePaymentsImport(WAeUPTable): ###(

    meta_type = 'WAeUP Online Payment Transactions'
    name = "online_payments_import"
    key = "order_id"
    def __init__(self):
        WAeUPTable.__init__(self, self.name)


InitializeClass(CoursesCatalog)
###)

class ReturningImport(WAeUPTable): ###(

    meta_type = 'Returning Import Table'
    name = "returning_import"
    key = "matric_no"
    def __init__(self):
        WAeUPTable.__init__(self, 'returning_import')


InitializeClass(ReturningImport)
###)

class ResultsImport(WAeUPTable): ###(

    meta_type = 'Results Import Table'
    name = "results_import"
    key = "key"
    def __init__(self):
        WAeUPTable.__init__(self, 'results_import')


InitializeClass(ResultsImport)

###)

class PaymentsCatalog(WAeUPTable): ###(

    meta_type = 'WAeUP Payments Catalog'
    name = "students_catalog"
    key = "id"
    def __init__(self):
        WAeUPTable.__init__(self, 'payments_catalog')


InitializeClass(PaymentsCatalog)

###)

# BBB:
AccomodationTable = AccommodationTable
