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/students
Files:
2 deleted
8 edited

Legend:

Unmodified
Added
Removed
  • 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.