Changeset 9420


Ignore:
Timestamp:
25 Oct 2012, 21:52:10 (12 years ago)
Author:
Henrik Bettermann
Message:

Reorganize ICourseTicket. Add ICourseTicketImport which validates a new field called level_session.

Customize checkConversion of CourseTicketProcessor?:
If level_session is provided in row the importer checks if
the parent studylevel exists and if its level_session
attribute corresponds with the expected value in row. The error message
then tells us why course result import fails.

Location:
main/waeup.kofa/trunk/src/waeup/kofa
Files:
2 deleted
9 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.kofa/trunk/src/waeup/kofa/browser/batchprocessing.txt

    r9418 r9420  
    9898    'Applicant Processor', 'Applicants Container Processor',
    9999    'CertificateCourse Processor', 'Certificate Processor',
    100     'Course Processor', 'Course Result Processor (special processor)',
     100    'Course Processor',
    101101    'CourseTicket Processor',
    102102    'Department Processor', 'Faculty Processor',
  • main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py

    r9418 r9420  
    4343    IStudent, IStudentStudyCourse,
    4444    IStudentUpdateByRegNo, IStudentUpdateByMatricNo,
    45     IStudentStudyLevel, ICourseTicket,
     45    IStudentStudyLevel, ICourseTicketImport,
    4646    IStudentOnlinePayment, IStudentVerdictUpdate)
    4747from waeup.kofa.students.workflow import  (
     
    305305    grok.baseclass()
    306306
    307     # additional available  fields
     307    # additional available fields
    308308    # beside 'student_id', 'reg_number' and 'matric_number'
    309309    additional_fields = []
     
    541541
    542542    name = u'CourseTicket Processor'
    543     iface = ICourseTicket
     543    iface = ICourseTicketImport
    544544    factory_name = 'waeup.CourseTicket'
    545545
     
    547547    additional_fields = ['level', 'code']
    548548    additional_headers = ['level', 'code']
     549
     550    @property
     551    def available_fields(self):
     552        fields = [
     553            'student_id','reg_number','matric_number',
     554            'mandatory', 'score', 'carry_over', 'automatic',
     555            'level_session'
     556            ] + self.additional_fields
     557        return sorted(fields)
    549558
    550559    def getParent(self, row, site):
     
    579588        obj.dcode = entries[0].__parent__.__parent__.code
    580589        obj.title = entries[0].title
    581         if getattr(obj, 'credits', None) is None:
    582             obj.credits = entries[0].credits
    583         if getattr(obj, 'passmark', None) is None:
    584             obj.passmark = entries[0].passmark
     590        #if getattr(obj, 'credits', None) is None:
     591        obj.credits = entries[0].credits
     592        #if getattr(obj, 'passmark', None) is None:
     593        obj.passmark = entries[0].passmark
    585594        obj.semester = entries[0].semester
    586595        parent[row['code']] = obj
     
    602611        errs, inv_errs, conv_dict = super(
    603612            CourseTicketProcessor, self).checkConversion(row, mode=mode)
    604 
    605         # We have to check if course really exists.
     613        if mode == 'remove':
     614            return errs, inv_errs, conv_dict
     615        # In update and create mode we have to check if course really exists.
    606616        # This is not done by the converter.
    607617        catalog = getUtility(ICatalog, name='courses_catalog')
     
    610620            errs.append(('code','non-existent'))
    611621            return errs, inv_errs, conv_dict
     622        # If level_session is provided in row we have to check if
     623        # the parent studylevel exists and if its level_session
     624        # attribute corresponds with the expected value in row.
     625        level_session = row.get('level_session', IGNORE_MARKER)
     626        if level_session not in (IGNORE_MARKER, ''):
     627            site = grok.getSite()
     628            studylevel = self.getParent(row, site)
     629            if studylevel is not None:
     630                if studylevel.level_session != level_session:
     631                    errs.append(('level_session','does not match %s'
     632                        % studylevel.level_session))
     633            else:
     634                errs.append(('level','does not exist'))
    612635        return errs, inv_errs, conv_dict
    613636
     
    793816        notify(grok.ObjectModifiedEvent(obj.__parent__))
    794817        return
    795 
    796 class CourseResultProcessor(CourseTicketProcessor):
    797     """A special batch processor for course results objects.
    798 
    799     Import course results, compares session and creates study level.
    800     """
    801     grok.implements(IBatchProcessor)
    802     grok.provides(IBatchProcessor)
    803     grok.context(Interface)
    804     util_name = 'courseresultprocessor'
    805     grok.name(util_name)
    806 
    807     name = u'Course Result Processor (special processor)'
    808     iface = ICourseTicket
    809     factory_name = 'waeup.CourseTicket'
    810 
    811     location_fields = []
    812     additional_fields = ['level', 'code']
    813     additional_headers = ['level', 'code']
    814 
    815     def getParent(self, row, site):
    816         student = self._getStudent(row, site)
    817         if student is None:
    818             return None
    819         return student['studycourse'].get(row['level'])
    820 
    821     def getEntry(self, row, site):
    822         level = self.getParent(row, site)
    823         if level is None:
    824             return None
    825         return level.get(row['code'])
    826 
    827     def updateEntry(self, obj, row, site):
    828         """Update obj to the values given in row.
    829         """
    830         items_changed = super(CourseTicketProcessor, self).updateEntry(
    831             obj, row, site)
    832         parent = self.getParent(row, site)
    833         student = self.getParent(row, site).__parent__.__parent__
    834         student.__parent__.logger.info(
    835             '%s - Course ticket in %s updated: %s'
    836             % (student.student_id,  parent.level, items_changed))
    837         return
    838 
    839     def addEntry(self, obj, row, site):
    840         parent = self.getParent(row, site)
    841         catalog = getUtility(ICatalog, name='courses_catalog')
    842         entries = list(catalog.searchResults(code=(row['code'],row['code'])))
    843         obj.fcode = entries[0].__parent__.__parent__.__parent__.code
    844         obj.dcode = entries[0].__parent__.__parent__.code
    845         obj.title = entries[0].title
    846         if getattr(obj, 'credits', None) is None:
    847             obj.credits = entries[0].credits
    848         if getattr(obj, 'passmark', None) is None:
    849             obj.passmark = entries[0].passmark
    850         obj.semester = entries[0].semester
    851         parent[row['code']] = obj
    852         return
    853 
    854     def delEntry(self, row, site):
    855         raise NotImplementedError('method not implemented')
    856 
    857     def checkConversion(self, row, mode='ignore'):
    858         """Validates all values in row.
    859         """
    860         errs, inv_errs, conv_dict = super(
    861             CourseTicketProcessor, self).checkConversion(row, mode=mode)
    862 
    863         # We have to check if course really exists.
    864         # This is not done by the converter.
    865         catalog = getUtility(ICatalog, name='courses_catalog')
    866         entries = catalog.searchResults(code=(row['code'],row['code']))
    867         if len(entries) == 0:
    868             errs.append(('code','non-existent'))
    869             return errs, inv_errs, conv_dict
    870         return errs, inv_errs, conv_dict
  • main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py

    r9411 r9420  
    11841184    grok.require('waeup.manageStudent')
    11851185    label = _('Add course ticket')
    1186     form_fields = grok.AutoFields(ICourseTicketAdd).omit(
    1187         'score', 'automatic', 'carry_over', 'credits', 'passmark')
     1186    form_fields = grok.AutoFields(ICourseTicketAdd)
    11881187    pnav = 4
    11891188
     
    12221221    grok.require('waeup.viewStudent')
    12231222    form_fields = grok.AutoFields(ICourseTicket)
    1224     grok.template('courseticketpage')
    12251223    pnav = 4
    12261224
     
    12371235    grok.name('manage')
    12381236    grok.require('waeup.manageStudent')
    1239     form_fields = grok.AutoFields(ICourseTicket).omit('credits', 'passmark')
    1240     grok.template('courseticketmanagepage')
     1237    form_fields = grok.AutoFields(ICourseTicket)
     1238    form_fields['title'].for_display = True
     1239    form_fields['fcode'].for_display = True
     1240    form_fields['dcode'].for_display = True
     1241    form_fields['semester'].for_display = True
     1242    form_fields['passmark'].for_display = True
     1243    form_fields['credits'].for_display = True
     1244    form_fields['mandatory'].for_display = True
     1245    form_fields['automatic'].for_display = True
    12411246    pnav = 4
    12421247
     
    21812186    grok.name('ctadd')
    21822187    grok.require('waeup.handleStudent')
    2183     form_fields = grok.AutoFields(ICourseTicketAdd).omit(
    2184         'score', 'mandatory', 'automatic', 'carry_over', 'credits', 'passmark')
     2188    form_fields = grok.AutoFields(ICourseTicketAdd)
    21852189
    21862190    def update(self):
  • main/waeup.kofa/trunk/src/waeup/kofa/students/export.py

    r9316 r9420  
    210210    #: Fieldnames considered by this exporter
    211211    fields = tuple(sorted(iface_names(ICourseTicket) +
    212         ['level', 'code', 'title',
    213         'semester', 'fcode', 'dcode'])) + ('student_id', 'certcode')
     212        ['level', 'code'])) + ('student_id', 'certcode')
    214213
    215214    #: The title under which this exporter will be displayed
  • main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py

    r9334 r9420  
    2828from waeup.kofa.students.vocabularies import (
    2929    StudyLevelSource, contextual_reg_num_source, contextual_mat_num_source,
    30     GenderSource, nats_vocab,
     30    GenderSource, nats_vocab
    3131    )
    3232from waeup.kofa.payments.interfaces import (
    3333    IPaymentsContainer, IOnlinePayment)
    3434from waeup.kofa.university.vocabularies import (
    35     CourseSource, StudyModeSource, CertificateSource)
     35    CourseSource, StudyModeSource, CertificateSource, SemesterSource)
    3636
    3737# VerdictSource can't be placed into the vocabularies module because it
     
    488488
    489489class ICourseTicket(IKofaObject):
    490     """A course ticket.
     490    """An interface for course tickets.
    491491
    492492    """
    493493    code = Attribute('code of the original course')
    494     title = Attribute('title of the original course')
    495     credits = Attribute('credits of the original course')
    496     passmark = Attribute('passmark of the original course')
    497     semester = Attribute('semester of the original course')
    498     fcode = Attribute('faculty code of the original course')
    499     dcode = Attribute('department code of the original course')
    500494    certcode = Attribute('certificate code of the study course')
     495
     496    title = schema.TextLine(
     497        title = _(u'Title'),
     498        required = False,
     499        )
     500
     501    fcode = schema.TextLine(
     502        title = _(u'Faculty Code'),
     503        required = False,
     504        )
     505
     506    dcode = schema.TextLine(
     507        title = _(u'Department Code'),
     508        required = False,
     509        )
     510
     511    semester = schema.Choice(
     512        title = _(u'Semester/Term'),
     513        source = SemesterSource(),
     514        required = False,
     515        )
     516
     517    passmark = schema.Int(
     518        title = _(u'Passmark'),
     519        required = False,
     520        )
     521
     522    credits = schema.Int(
     523        title = _(u'Credits'),
     524        required = False,
     525        )
    501526
    502527    mandatory = schema.Bool(
     
    504529        default = False,
    505530        required = False,
    506         readonly = False,
    507531        )
    508532
     
    511535        default = 0,
    512536        required = False,
    513         readonly = False,
     537        )
     538
     539    carry_over = schema.Bool(
     540        title = _(u'Carry-over Course'),
     541        default = False,
     542        required = False,
    514543        )
    515544
     
    518547        default = False,
    519548        required = False,
    520         readonly = True,
    521         )
    522 
    523     carry_over = schema.Bool(
    524         title = _(u'Carry-over Course'),
    525         default = False,
    526         required = False,
    527         readonly = False,
    528         )
    529 
    530     credits = schema.Int(
    531         title = _(u'Credits'),
    532         required = False,
    533         )
    534 
    535     passmark = schema.Int(
    536         title = _(u'Passmark'),
    537         required = False,
    538         )
     549        )
     550
    539551
    540552    def getLevel():
     
    546558        """
    547559
    548 class ICourseTicketAdd(ICourseTicket):
     560class ICourseTicketAdd(IKofaObject):
    549561    """An interface for adding course tickets.
    550562
     
    556568        )
    557569
     570class ICourseTicketImport(ICourseTicket):
     571    """An interface for importing course results and nothing more.
     572
     573    """
     574    score = schema.Int(
     575        title = _(u'Score'),
     576        required = False,
     577        readonly = False,
     578        )
     579
     580    level_session = schema.Choice(
     581        title = _(u'Level Session'),
     582        source = academic_sessions_vocab,
     583        required = False,
     584        readonly = False,
     585        )
     586
    558587class IStudentAccommodation(IKofaObject):
    559588    """A container for student accommodation objects.
  • main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py

    r9316 r9420  
    121121        super(CourseTicket, self).__init__()
    122122        self.code = None
    123         self.title = None
    124         self.fcode = None
    125         self.dcode = None
    126         self.semester = None
    127123        return
    128124
  • main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_courseticket_data.csv

    r7665 r9420  
    1 reg_number,matric_number,level,code,score,mandatory
    2 1,,100,COURSE1,1,True
    3 2,,100,COURSE1,2,False
    4 ,100002,100,COURSE1,3,False
    5 1,,nonsense,COURSE1,5,
    6 1,,100,NONSENSE,5,
     1reg_number,matric_number,level,code,score,mandatory,level_session
     21,,100,COURSE1,1,True,
     32,,100,COURSE1,2,False,
     4,100002,100,COURSE1,3,False,
     51,,nonsense,COURSE1,5,,
     61,,100,NONSENSE,5,,
     71,,200,COURSE1,6,,2004
     81,,300,COURSE1,6,,2008
     91,,300,COURSE1,6,,200888
  • main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py

    r9302 r9420  
    733733
    734734    def test_import(self):
    735 
    736735        num, num_warns, fin_file, fail_file = self.processor.doImport(
    737736            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
    738 
    739         self.assertEqual(num_warns,2)
     737        fail_file = open(fail_file).read()
     738        self.assertEqual(num_warns,5)
     739        self.assertEqual(fail_file,
     740            'reg_number,code,mandatory,level,level_session,score,matric_number,--ERRORS--\r\n'
     741            '1,COURSE1,,nonsense,,5,,Not all parents do exist yet. Skipping\r\n'
     742            '1,NONSENSE,,100,,5,,code: non-existent\r\n'
     743            '1,COURSE1,,200,2004,6,,level_session: does not match 2008\r\n'
     744            '1,COURSE1,,300,2008,6,,level: does not exist\r\n'
     745            '1,COURSE1,,300,200888,6,,level_session: Invalid value; level: does not exist\r\n')
    740746        assert self.processor.entryExists(
    741747            dict(reg_number='1', level='100', code='COURSE1'),
     
    772778        num, num_warns, fin_file, fail_file = self.processor.doImport(
    773779            self.csv_file, COURSETICKET_HEADER_FIELDS,'update')
    774         self.assertEqual(num_warns,2)
     780        fail_file = open(fail_file).read()
     781        self.assertEqual(num_warns,5)
     782        self.assertEqual(fail_file,
     783            'reg_number,code,mandatory,level,level_session,score,matric_number,--ERRORS--\r\n'
     784            '1,COURSE1,<IGNORE>,nonsense,<IGNORE>,5,<IGNORE>,Cannot update: no such entry\r\n'
     785            '1,NONSENSE,<IGNORE>,100,<IGNORE>,5,<IGNORE>,code: non-existent\r\n'
     786            '1,COURSE1,<IGNORE>,200,2004,6,<IGNORE>,level_session: does not match 2008\r\n'
     787            '1,COURSE1,<IGNORE>,300,2008,6,<IGNORE>,level: does not exist\r\n'
     788            '1,COURSE1,<IGNORE>,300,200888,6,<IGNORE>,level_session: Invalid value; level: does not exist\r\n')
    775789        shutil.rmtree(os.path.dirname(fin_file))
    776790
     
    786800        num, num_warns, fin_file, fail_file = self.processor.doImport(
    787801            self.csv_file, COURSETICKET_HEADER_FIELDS,'remove')
    788         self.assertEqual(num_warns,2)
     802        self.assertEqual(num_warns,5)
    789803        assert self.processor.entryExists(
    790804            dict(reg_number='1', level='100', code='COURSE1'), self.app) is False
  • main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py

    r9412 r9420  
    161161        self.certificate.school_fee_1 = 40000.0
    162162        self.certificate.school_fee_2 = 20000.0
    163         self.app['faculties']['fac1'] = Faculty(code='fac1')
    164         self.app['faculties']['fac1']['dep1'] = Department(code='dep1')
     163        self.app['faculties']['fac1'] = Faculty(code=u'fac1')
     164        self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1')
    165165        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
    166166            self.certificate)
Note: See TracChangeset for help on using the changeset viewer.