Ignore:
Timestamp:
20 Sep 2012, 08:22:52 (12 years ago)
Author:
uli
Message:

Merge changes from trunk r9171:9207.

Location:
main/waeup.kofa/branches/uli-async-update
Files:
24 edited
3 copied

Legend:

Unmodified
Added
Removed
  • main/waeup.kofa/branches/uli-async-update

  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/applicants/browser.py

    r9169 r9208  
    10421042        return
    10431043
    1044     @action(_('Get login credentials'), style='primary')
     1044    @action(_('Send login credentials to email address'), style='primary')
    10451045    def register(self, **data):
    10461046        if not self.captcha_result.is_valid:
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/applicants/tests/test_browser.py

    r9169 r9208  
    935935        self.browser.getControl(name="form.phone.area").value = '555'
    936936        self.browser.getControl(name="form.phone.ext").value = '6666666'
    937         self.browser.getControl("Get login credentials").click()
     937        self.browser.getControl("Send login credentials").click()
    938938        self.assertEqual(self.browser.url,
    939939            self.container_path + '/registration_complete?email=xx%40yy.zz')
     
    962962        self.browser.getControl(name="form.reg_number").value = 'anynumber'
    963963        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
    964         self.browser.getControl("Get login credentials").click()
     964        self.browser.getControl("Send login credentials").click()
    965965        self.assertTrue('No application record found.'
    966966            in self.browser.contents)
     
    972972        self.browser.getControl(name="form.reg_number").value = '1234'
    973973        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
    974         self.browser.getControl("Get login credentials").click()
     974        self.browser.getControl("Send login credentials").click()
    975975        self.assertTrue('An error occurred.' in self.browser.contents)
    976976        # Let's set this attribute manually
     
    981981        self.browser.getControl(name="form.reg_number").value = '1234'
    982982        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
    983         self.browser.getControl("Get login credentials").click()
     983        self.browser.getControl("Send login credentials").click()
    984984        # Anonymous is not informed that firstname verification failed.
    985985        # It seems that the record doesn't exist.
     
    991991        self.browser.getControl(name="form.firstname").value = 'John'
    992992        self.browser.getControl(name="form.reg_number").value = '1234'
    993         self.browser.getControl("Get login credentials").click()
     993        self.browser.getControl("Send login credentials").click()
    994994        self.assertTrue('Your password has already been set and used.'
    995995            in self.browser.contents)
     
    10051005        self.browser.getControl(name="form.reg_number").value = '1234'
    10061006        self.browser.getControl(name="form.email").value = 'new@yy.zz'
    1007         self.browser.getControl("Get login credentials").click()
     1007        self.browser.getControl("Send login credentials").click()
    10081008        # Yeah, we succeded ...
    10091009        self.assertTrue('Your registration was successful.'
     
    10211021        self.browser.getControl(name="form.identifier").value = '1234'
    10221022        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
    1023         self.browser.getControl("Get login credentials").click()
     1023        self.browser.getControl("Send login credentials").click()
    10241024        self.assertTrue('No record found' in self.browser.contents)
    10251025        self.applicant.email = 'aa@aa.ng'
     
    10291029        self.browser.getControl(name="form.identifier").value = '1234'
    10301030        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
    1031         self.browser.getControl("Get login credentials").click()
     1031        self.browser.getControl("Send login credentials").click()
    10321032        self.assertTrue(
    10331033            'An email with your user name and password has been sent'
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/batchprocessing.txt

    r9169 r9208  
    9797    'Course Processor', 'CourseTicket Processor',
    9898    'Department Processor', 'Faculty Processor',
     99    'Hostel Processor',
    99100    'Student Payment Processor', 'Student Processor',
    100101    'StudentStudyCourse Processor (update only)',
     
    427428    >>> browser.open('http://localhost/app/datacenter')
    428429
    429 Prepare a CSV file for certificate courses:
     430Prepare a CSV file for users:
    430431
    431432    >>> open('users.csv', 'wb').write(
    432433    ... """name,title,public_name,email,phone,roles
    433434    ... uli,Uli Fouquet,Chief Developer,uli@abc.de,+49-234-567
    434     ... henrik, Henrik Bettermann,Admin,henrik@abc.de,+49-234-567,['waeup.PortalManager']
     435    ... henrik,Henrik Bettermann,Admin,henrik@abc.de,+49-234-567,['waeup.PortalManager']
    435436    ... """)
    436437
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/browser.txt

    r9169 r9208  
    8181  >>> browser.getControl(name="form.identifier").value = 'forgetful'
    8282  >>> browser.getControl(name="form.email").value = 'aa@aa.ng'
    83   >>> browser.getControl("Get login credentials").click()
     83  >>> browser.getControl("Send login credentials").click()
    8484  >>> print browser.contents
    8585  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/pages.py

    r9177 r9208  
    22182218        return None
    22192219
    2220     @action(_('Get login credentials'), style='primary')
     2220    @action(_('Send login credentials to email address'), style='primary')
    22212221    def request(self, **data):
    22222222        if not self.captcha_result.is_valid:
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/static/jquery.dataTables.min.js

    r6820 r9208  
    1515 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    1616 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
     17 *
     18 * Customized by Henrik Bettermann: this.aLengthMenu, this._iDisplayLength, this._iDisplayEnd
     19 *
    1720 */
    1821(function(j,ra,p){j.fn.dataTableSettings=[];var D=j.fn.dataTableSettings;j.fn.dataTableExt={};var n=j.fn.dataTableExt;n.sVersion="1.7.6";n.sErrMode="alert";n.iApiIndex=0;n.oApi={};n.afnFiltering=[];n.aoFeatures=[];n.ofnSearch={};n.afnSortData=[];n.oStdClasses={sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",
     
    3841[];this.aoColumns=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",bRegex:false,bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripClasses=[];this.asDestoryStrips=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=false;this.aoOpenRows=
    3942[];this.sDom="lfrtip";this.sPaginationType="two_button";this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.bAjaxDataGet=true;this.fnServerData=function(a,b,c){j.ajax({url:a,data:b,success:c,dataType:"json",cache:false,error:function(d,f){f=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})};
    40 this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=","+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100,1000,5000];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=10;this._iDisplayStart=0;this._iDisplayEnd=10;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=n.oStdClasses;this.bSorted=this.bFiltered=false;this.oInit=null}function r(a){return function(){var b=[A(this[n.iApiIndex])].concat(Array.prototype.slice.call(arguments));
     43this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=","+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100,1000,5000];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=1000;this._iDisplayStart=0;this._iDisplayEnd=1000;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=n.oStdClasses;this.bSorted=this.bFiltered=false;this.oInit=null}function r(a){return function(){var b=[A(this[n.iApiIndex])].concat(Array.prototype.slice.call(arguments));
    4144return n.oApi[a].apply(this,b)}}function s(a){var b,c;if(a.bInitialised===false)setTimeout(function(){s(a)},200);else{sa(a);U(a);K(a,true);a.oFeatures.bAutoWidth&&$(a);b=0;for(c=a.aoColumns.length;b<c;b++)if(a.aoColumns[b].sWidth!==null)a.aoColumns[b].nTh.style.width=v(a.aoColumns[b].sWidth);if(a.oFeatures.bSort)O(a);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}if(a.sAjaxSource!==null&&!a.oFeatures.bServerSide)a.fnServerData.call(a.oInstance,a.sAjaxSource,[],function(d){for(b=0;b<d.aaData.length;b++)u(a,
    4245d.aaData[b]);a.iInitDisplayStart=a._iDisplayStart;if(a.oFeatures.bSort)O(a);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}K(a,false);w(a,d)});else if(!a.oFeatures.bServerSide){K(a,false);w(a)}}}function w(a,b){a._bInitComplete=true;if(typeof a.fnInitComplete=="function")typeof b!="undefined"?a.fnInitComplete.call(a.oInstance,a,b):a.fnInitComplete.call(a.oInstance,a)}function y(a,b,c){o(a.oLanguage,b,"sProcessing");o(a.oLanguage,b,"sLengthMenu");o(a.oLanguage,b,"sEmptyTable");o(a.oLanguage,
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/templates/datacenterfinishedpage.pt

    r9169 r9208  
    33    <tr>
    44      <th i18n:translate="">File</th>
    5       <th i18n:translate="">Size</th>
     5      <th i18n:translate="">Datasets</th>
    66      <th i18n:translate="">Upload Date</th>
    77    </tr>
     
    1111      <td><a tal:attributes="href python: 'download?filename=' + file.rel_path"
    1212             tal:content="file/name">FILENAME</a></td>
    13       <td tal:content="file/size">12 k</td>
     13      <td tal:content="python: file.lines-1">12 k</td>
    1414      <td tal:content="file/uploaddate">Mar 12, 2008</td>
    1515    </tr>
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/browser/templates/datacenterpage.pt

    r9169 r9208  
    1616          <th></th>
    1717          <th i18n:translate="">File</th>
    18           <th i18n:translate="">Size</th>
     18          <th i18n:translate="">Datasets</th>
    1919          <th i18n:translate="">Upload Date</th>
    2020        </tr>
     
    2828          <td><a tal:attributes="href python: 'download?filename=' + file.name"
    2929                 tal:content="file/name">FILENAME</a></td>
    30           <td tal:content="file/size">12 k</td>
     30          <td tal:content="python: file.lines - 1">100</td>
    3131          <td tal:content="file/uploaddate">Mar 12, 2008</td>
    3232        </tr>
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/browser.py

    r9166 r9208  
    9393
    9494    def title(self):
    95         co = self.context.getBedCoordinates()
     95        co = self.context.coordinates
    9696        return _('Block ${a}, Room ${b}, Bed ${c}',
    9797            mapping = {'a':co[1], 'b':co[2], 'c':co[3]})
     
    130130    form_fields = grok.AutoFields(IHostelsContainer)
    131131    taboneactions = [_('Save')]
    132     tabtwoactions = [_('Add hostel'), _('Remove selected')]
     132    tabtwoactions = [_('Add hostel'),
     133        _('Clear all hostels'),
     134        _('Remove selected')]
    133135
    134136    def update(self):
     
    164166        return
    165167
     168    @jsaction(_('Clear all hostels'))
     169    def clearHostels(self, **data):
     170        self.context.clearAllHostels()
     171        self.flash(_('All hostels cleared.'))
     172        write_log_message(self, 'all hostels cleared')
     173        self.redirect(self.url(self.context, '@@manage')+'?tab2')
     174        return
     175
    166176    @action(_('Save'), style='primary')
    167177    def save(self, **data):
     
    232242    tabtwoactions = [_('Update all beds'),
    233243        _('Switch reservation of selected beds'),
    234         _('Release selected beds')]
     244        _('Release selected beds'),
     245        _('Clear hostel')]
    235246    not_occupied = NOT_OCCUPIED
    236247
     
    243254        tabs.need()
    244255        datatable.need()
     256        warning.need()
    245257        self.tab1 = self.tab2 = ''
    246258        qs = self.request.get('QUERY_STRING', '')
     
    327339        return
    328340
     341    @jsaction(_('Clear hostel'))
     342    def clearHostel(self, **data):
     343        self.context.clearHostel()
     344        self.flash(_('Hostel cleared.'))
     345        write_log_message(self, 'cleared')
     346        self.redirect(self.url(self.context, '@@manage')+'?tab2')
     347        return
     348
    329349class BedManageFormPage(KofaEditFormPage):
    330350    """ View to edit bed data
     
    334354    grok.require('waeup.manageHostels')
    335355    form_fields = grok.AutoFields(IBedAllocateStudent).omit(
    336         'bed_id').omit('bed_number').omit('bed_type')
     356        'bed_id', 'bed_number', 'bed_type')
    337357    label = _('Allocate student')
    338358    pnav = 5
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/browser_templates/containerpage.pt

    r8685 r9208  
    2121      <th i18n:translate="">Id</th>
    2222      <th i18n:translate="">Name</th>
     23      <th i18n:translate="">Booked Beds</th>
    2324    </tr>
    2425  </thead>
    2526  <tbody>
    26     <tr tal:repeat="value context/values">
    27       <td> <a tal:attributes="href value/__name__">
    28           <span tal:content="value/hostel_id">ID</span></a></td>
    29       <td tal:content="value/hostel_name">NAME</td>
    30     </tr>
     27    <tal:tbody repeat="value context/values">
     28      <tr tal:define = "stats value/bed_statistics">
     29        <td> <a tal:attributes="href value/__name__">
     30            <span tal:content="value/hostel_id">ID</span></a></td>
     31        <td tal:content="value/hostel_name">NAME</td>
     32        <td>
     33          <span tal:replace="stats/booked">10</span> of <span tal:replace="stats/total">100</span>
     34        </td>
     35      </tr>
     36    </tal:tbody>
    3137  </tbody>
    3238</table>
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/container.py

    r8686 r9208  
    5151        return
    5252
     53    def clearAllHostels(self):
     54        """Clear all hostels.
     55        """
     56        for hostel in self.values():
     57            hostel.clearHostel()
     58        return
     59
    5360    logger_name = 'waeup.kofa.${sitename}.hostels'
    5461    logger_filename = 'hostels.log'
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/hostel.py

    r8685 r9208  
    2222from zope.event import notify
    2323from zope.component import getUtility
     24from zope.component.interfaces import IFactory
    2425from datetime import datetime
    2526from waeup.kofa.utils.helpers import attrs_to_fields
     
    4041        target = self.__name__
    4142        return grok.getSite()['hostels'].logger_info(ob_class,target,comment)
     43
     44    @property
     45    def bed_statistics(self):
     46        total = len(self.keys())
     47        booked = 0
     48        for value in self.values():
     49            if value.owner != NOT_OCCUPIED:
     50                booked += 1
     51        return {'booked':booked, 'total':total}
     52
     53    def clearHostel(self):
     54        """Remove all beds
     55        """
     56        keys = [i for i in self.keys()] # create deep copy
     57        for bed in keys:
     58            del self[bed]
     59        return
    4260
    4361    def addBed(self, bed):
     
    128146    grok.provides(IBed)
    129147
    130     def getBedCoordinates(self):
     148    @property
     149    def coordinates(self):
    131150        """Determine the coordinates from the bed_id.
    132151        """
    133152        return self.bed_id.split('_')
     153
     154    # The following property attributes are only needed
     155    # for the exporter to ease evaluation with  Excel.
     156
     157    @property
     158    def hall(self):
     159        return self.coordinates[0]
     160
     161    @property
     162    def block(self):
     163        return self.coordinates[1]
     164
     165    @property
     166    def room(self):
     167        return self.coordinates[2]
     168
     169    @property
     170    def bed(self):
     171        return self.coordinates[3]
     172
     173    @property
     174    def special_handling(self):
     175        return self.bed_type.split('_')[0]
     176
     177    @property
     178    def sex(self):
     179        return self.bed_type.split('_')[1]
     180
     181    @property
     182    def bt(self):
     183        return self.bed_type.split('_')[2]
     184
    134185
    135186    def bookBed(self, student_id):
     
    145196        """
    146197        sh, sex, bt = self.bed_type.split('_')
    147         hostel_id, block, room_nr, bed = self.getBedCoordinates()
     198        hostel_id, block, room_nr, bed = self.coordinates
    148199        hostel = self.__parent__
    149200        beds_for_fresh = getattr(hostel,'beds_for_fresh',[])
     
    221272Bed = attrs_to_fields(Bed)
    222273
     274class HostelFactory(grok.GlobalUtility):
     275    """A factory for hostels.
     276
     277    We need this factory for the hostel processor.
     278    """
     279    grok.implements(IFactory)
     280    grok.name(u'waeup.Hostel')
     281    title = u"Create a new hostel.",
     282    description = u"This factory instantiates new hostel instances."
     283
     284    def __call__(self, *args, **kw):
     285        return Hostel()
     286
     287    def getInterfaces(self):
     288        return implementedBy(Hostel)
     289
     290
    223291@grok.subscribe(IBedTicket, grok.IObjectRemovedEvent)
    224292def handle_bedticket_removed(bedticket, event):
     
    229297        bedticket.bed.owner = NOT_OCCUPIED
    230298        notify(grok.ObjectModifiedEvent(bedticket.bed))
     299
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/interfaces.py

    r9169 r9208  
    1717##
    1818from datetime import datetime
    19 from zope.interface import invariant, Invalid
     19from zope.interface import invariant, Invalid, Attribute
    2020from zope import schema
    2121from waeup.kofa.interfaces import (
     
    5858        )
    5959
     60    def clearAllHostels():
     61        """Clear all hostels.
     62        """
     63
    6064class IHostel(IKofaObject):
    6165    """A base representation of hostels.
     
    6367    """
    6468
     69    bed_statistics = Attribute('Number of booked and total beds')
     70
    6571    def loggerInfo(ob_class, comment):
    6672        """Adds an INFO message to the log file
     73        """
     74
     75    def clearHostel():
     76        """Remove all beds.
    6777        """
    6878
     
    189199    """
    190200
     201    coordinates = Attribute('The coordinates of the bed from bed_id')
     202
    191203    def loggerInfo(ob_class, comment):
    192204        """Adds an INFO message to the log file
    193205        """
    194206
    195     def getBedCoordinates():
    196         """Determine the coordinates from bed_id.
    197         """
    198207    def bookBed(student_id):
    199208        """Book a bed for a student.
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/hostels/tests.py

    r8686 r9208  
    1919Tests for hostels and their UI components.
    2020"""
     21import os
    2122import shutil
    2223import tempfile
     
    3637from waeup.kofa.hostels.container import HostelsContainer
    3738from waeup.kofa.hostels.hostel import Hostel, Bed
     39from waeup.kofa.hostels.batching import HostelProcessor
     40from waeup.kofa.hostels.export import BedExporter, HostelExporter
    3841from waeup.kofa.testing import (FunctionalLayer, FunctionalTestCase)
    3942from waeup.kofa.students.student import Student
    4043from waeup.kofa.students.accommodation import BedTicket
    4144from waeup.kofa.university.department import Department
     45
     46HOSTEL_SAMPLE_DATA = open(
     47    os.path.join(os.path.dirname(__file__), 'sample_hostel_data.csv'),
     48    'rb').read()
     49
     50HOSTEL_HEADER_FIELDS = HOSTEL_SAMPLE_DATA.split(
     51    '\n')[0].split(',')
    4252
    4353class HostelsContainerTestCase(FunctionalTestCase):
     
    142152        # Create a bed
    143153        bed = Bed()
    144         bed.bed_id = u'xyz'
     154        bed.bed_id = u'hall_block_room_bed'
    145155        bed.bed_number = 1
    146         bed.bed_type = u'abc'
     156        bed.bed_type = u'a_b_c'
    147157        self.app['hostels'][hostel.hostel_id][bed.bed_id] = bed
    148158
     
    157167        self.browser.handleErrors = False
    158168
     169        self.logfile = os.path.join(
     170            self.app['datacenter'].storage, 'logs', 'hostels.log')
     171
    159172    def tearDown(self):
    160173        super(HostelsFullSetup, self).tearDown()
     
    174187        # We can find a certain bed
    175188        cat = queryUtility(ICatalog, name='beds_catalog')
    176         results = cat.searchResults(bed_type=(u'abc', u'abc'))
     189        results = cat.searchResults(bed_type=(u'a_b_c', u'a_b_c'))
    177190        results = [x for x in results] # Turn results generator into list
    178191        assert len(results) == 1
    179         assert results[0] is self.app['hostels']['hall-x']['xyz']
     192        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
    180193
    181194    def test_search_by_owner(self):
    182195        # We can find a certain bed
    183         myobj = self.app['hostels']['hall-x']['xyz']
     196        myobj = self.app['hostels']['hall-x']['hall_block_room_bed']
    184197        myobj.owner = u'abc'
    185198        notify(grok.ObjectModifiedEvent(myobj))
     
    188201        results = [x for x in results] # Turn results generator into list
    189202        assert len(results) == 1
    190         assert results[0] is self.app['hostels']['hall-x']['xyz']
     203        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
    191204
    192205class HostelsUITests(HostelsFullSetup):
     
    300313        self.assertMatches(
    301314          '...No allocated bed selected...', self.browser.contents)
    302         # Managers can manually allocate studenst after cancellation
     315        # Managers can manually allocate students after cancellation
    303316        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
    304317        self.browser.getControl(name="form.owner").value = [self.student_id]
     
    334347        # Also the number of the bed has changed.
    335348        self.assertFalse(new_number == old_number)
     349        # The number of occupied bed are displayed on container page.
     350        self.browser.open(self.container_path)
     351        self.assertTrue('1 of 8' in self.browser.contents)
    336352        # Remove entire hostel
    337353        self.browser.open(self.manage_container_path)
     
    346362        results = [x for x in results]
    347363        assert len(results) == 0
     364
     365    def test_clear_hostels(self):
     366        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
     367        self.browser.open(self.container_path)
     368        self.browser.getLink("Manage accommodation").click()
     369        self.browser.getControl("Add hostel").click()
     370        self.browser.getControl("Create hostel").click()
     371        hall = self.app['hostels']['hall-1']
     372        hall.blocks_for_female = ['A','B']
     373        hall.rooms_per_floor = 1
     374        hall.beds_for_fresh = ['A']
     375        hall.beds_for_returning = ['B']
     376        hall.beds_for_final = ['C']
     377        hall.beds_for_all = ['D','E']
     378        self.browser.open(self.container_path + '/hall-1/manage')
     379        self.browser.getControl("Update all beds").click()
     380        cat = queryUtility(ICatalog, name='beds_catalog')
     381        results = cat.searchResults(bed_type=(None, None))
     382        self.assertEqual(len(results), 11)
     383        self.browser.getControl("Clear hostel").click()
     384        self.assertEqual(len(self.app['hostels']['hall-1']), 0)
     385        # Only the bed in hall-x remains in the catalog.
     386        results = cat.searchResults(bed_type=(None, None))
     387        self.assertEqual(len(results), 1)
     388        # We can clear all hostels at the same time.
     389        self.browser.open(self.manage_container_path)
     390        self.browser.getControl("Clear all hostels").click()
     391        results = cat.searchResults(bed_type=(None, None))
     392        self.assertEqual(len(results), 0)
     393        # Both actions have been logged.
     394        logcontent = open(self.logfile).read()
     395        self.assertTrue('INFO - zope.mgr - hostels.browser.HostelManageFormPage'
     396                        ' - hall-1 - cleared' in logcontent)
     397        self.assertTrue('zope.mgr - hostels.browser.HostelsContainerManagePage'
     398                        ' - hostels - all hostels cleared' in logcontent)
     399
     400class ExportTests(HostelsFullSetup):
     401
     402    layer = FunctionalLayer
     403
     404    def setUp(self):
     405        super(ExportTests, self).setUp()
     406        self.workdir = tempfile.mkdtemp()
     407        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
     408        return
     409
     410    def test_export_hostels(self):
     411        exporter = HostelExporter()
     412        exporter.export_all(self.app, self.outfile)
     413        result = open(self.outfile, 'rb').read()
     414        self.assertEqual(
     415            result,
     416            'beds_for_all,beds_for_final,beds_for_fresh,beds_for_pre,'
     417            'beds_for_returning,beds_reserved,blocks_for_female,'
     418            'blocks_for_male,floors_per_block,hostel_id,hostel_name,'
     419            'rooms_per_floor,sort_id,special_handling\r\n,,,,,[],,,1,'
     420            'hall-x,Hall 1,2,10,regular\r\n'
     421            )
     422        return
     423
     424    def test_export_beds(self):
     425        exporter = BedExporter()
     426        exporter.export_all(self.app, self.outfile)
     427        result = open(self.outfile, 'rb').read()
     428        self.assertEqual(
     429            result,
     430            'bed_id,bed_number,bed_type,owner,hall,block,room,bed,'
     431            'special_handling,sex,bt\r\nhall_block_room_bed,1,a_b_c,,'
     432            'hall,block,room,bed,a,b,c\r\n'
     433            )
     434        return
     435
     436class HostelProcessorTest(HostelsFullSetup):
     437
     438    layer = FunctionalLayer
     439
     440    def test_import(self):
     441        self.processor = HostelProcessor()
     442        self.workdir = tempfile.mkdtemp()
     443        self.csv_file = os.path.join(self.workdir, 'sample_student_data.csv')
     444        open(self.csv_file, 'wb').write(HOSTEL_SAMPLE_DATA)
     445        num, num_warns, fin_file, fail_file = self.processor.doImport(
     446            self.csv_file, HOSTEL_HEADER_FIELDS)
     447        self.assertEqual(num_warns,0)
     448        self.assertEqual(len(self.app['hostels'].keys()), 11) # including hall-x
     449        self.assertEqual(self.app['hostels'][
     450            'block-a-upper-hostel'].hostel_id,'block-a-upper-hostel')
     451        self.assertEqual(self.app['hostels'][
     452            'block-a-upper-hostel'].beds_for_final, ['A', 'B'])
     453        logcontent = open(self.logfile).read()
     454        self.assertTrue(
     455            "Hostel Processor - block-a-upper-hostel - "
     456            "Record updated: beds_for_pre=['G'], floors_per_block=1, "
     457            "beds_for_final=['A', 'B'], rooms_per_floor=32, "
     458            "blocks_for_male=[], hostel_id=block-a-upper-hostel, "
     459            "sort_id=20, beds_for_returning=['C', 'D'], "
     460            "hostel_name=Block A Upper Hostel, beds_for_fresh=['E', 'F'], "
     461            "blocks_for_female=['A'], beds_for_all=[], beds_reserved=[]"
     462            in logcontent)
     463        shutil.rmtree(os.path.dirname(fin_file))
     464        return
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/permissions.py

    r9169 r9208  
    213213                     'waeup.uploadStudentFile', 'waeup.showStudents',
    214214                     'waeup.viewStudentsContainer','waeup.viewStudentsTab',
     215                     'waeup.handleAccommodation',
    215216                     'waeup.viewHostels', 'waeup.manageHostels',
    216217                     'waeup.editUser'
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/browser.py

    r9169 r9208  
    361361        return
    362362
     363class ExportPDFAdmissionSlipPage(UtilityView, grok.View):
     364    """Deliver a PDF Admission slip.
     365    """
     366    grok.context(IStudent)
     367    grok.name('admission_slip.pdf')
     368    grok.require('waeup.viewStudent')
     369    prefix = 'form'
     370
     371    form_fields = grok.AutoFields(IStudentBase).select('student_id', 'reg_number')
     372
     373    @property
     374    def label(self):
     375        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     376        return translate(_('Admission Letter of'),
     377            'waeup.kofa', target_language=portal_language) \
     378            + ' %s' % self.context.display_fullname
     379
     380    def render(self):
     381        students_utils = getUtility(IStudentsUtils)
     382        return students_utils.renderPDFAdmissionLetter(self,
     383            self.context.student)
     384
    363385class StudentBaseManageFormPage(KofaEditFormPage):
    364386    """ View to manage student base data
     
    12341256        if error is not None:
    12351257            self.flash(error)
    1236             if 'Would you like' in error:
     1258            if 'previous session' in error:
    12371259                self.redirect(self.url(self.context) + '/@@addpp')
    12381260                return
     
    14091431    buttonname = _('Create bed ticket')
    14101432    notice = ''
     1433    with_ac = True
    14111434
    14121435    def update(self, SUBMIT=None):
     
    14491472            self.redirect(self.url(self.context))
    14501473            return
    1451         self.ac_series = self.request.form.get('ac_series', None)
    1452         self.ac_number = self.request.form.get('ac_number', None)
     1474        if self.with_ac:
     1475            self.ac_series = self.request.form.get('ac_series', None)
     1476            self.ac_number = self.request.form.get('ac_number', None)
    14531477        if SUBMIT is None:
    14541478            return
    1455         pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
    1456         code = get_access_code(pin)
    1457         if not code:
    1458             self.flash(_('Activation code is invalid.'))
    1459             return
     1479        if self.with_ac:
     1480            pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
     1481            code = get_access_code(pin)
     1482            if not code:
     1483                self.flash(_('Activation code is invalid.'))
     1484                return
    14601485        # Search and book bed
    14611486        cat = queryUtility(ICatalog, name='beds_catalog', default=None)
     
    14631488            owner=(student.student_id,student.student_id))
    14641489        if len(entries):
    1465             # If bed space has bee manually allocated use this bed
     1490            # If bed space has been manually allocated use this bed
    14661491            bed = [entry for entry in entries][0]
    14671492        else:
     
    14791504                    mapping = {'a':acc_details['bt']}))
    14801505                return
    1481         # Mark pin as used (this also fires a pin related transition)
    1482         if code.state == USED:
    1483             self.flash(_('Activation code has already been used.'))
    1484             return
    1485         else:
    1486             comment = _(u'invalidated')
    1487             # Here we know that the ac is in state initialized so we do not
    1488             # expect an exception, but the owner might be different
    1489             if not invalidate_accesscode(
    1490                 pin,comment,self.context.student.student_id):
    1491                 self.flash(_('You are not the owner of this access code.'))
     1506        if self.with_ac:
     1507            # Mark pin as used (this also fires a pin related transition)
     1508            if code.state == USED:
     1509                self.flash(_('Activation code has already been used.'))
    14921510                return
     1511            else:
     1512                comment = _(u'invalidated')
     1513                # Here we know that the ac is in state initialized so we do not
     1514                # expect an exception, but the owner might be different
     1515                if not invalidate_accesscode(
     1516                    pin,comment,self.context.student.student_id):
     1517                    self.flash(_('You are not the owner of this access code.'))
     1518                    return
    14931519        # Create bed ticket
    14941520        bedticket = createObject(u'waeup.BedTicket')
    1495         bedticket.booking_code = pin
     1521        if self.with_ac:
     1522            bedticket.booking_code = pin
    14961523        bedticket.booking_session = acc_details['booking_session']
    14971524        bedticket.bed_type = acc_details['bt']
    14981525        bedticket.bed = bed
    14991526        hall_title = bed.__parent__.hostel_name
    1500         coordinates = bed.getBedCoordinates()[1:]
     1527        coordinates = bed.coordinates[1:]
    15011528        block, room_nr, bed_nr = coordinates
    15021529        bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = {
     
    15211548    grok.require('waeup.handleAccommodation')
    15221549    form_fields = grok.AutoFields(IBedTicket)
     1550    form_fields['booking_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
    15231551    pnav = 4
    15241552
     
    15471575    def label(self):
    15481576        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
    1549         return translate(_('Bed Allocation: '),
     1577        #return translate(_('Bed Allocation: '),
     1578        #    'waeup.kofa', target_language=portal_language) \
     1579        #    + ' %s' % self.context.bed_coordinates
     1580        return translate(_('Bed Allocation Slip'),
    15501581            'waeup.kofa', target_language=portal_language) \
    1551             + ' %s' % self.context.bed_coordinates
     1582            + ' %s' % self.context.getSessionString()
    15521583
    15531584    def render(self):
     
    16141645        self.context.bed = new_bed
    16151646        hall_title = new_bed.__parent__.hostel_name
    1616         coordinates = new_bed.getBedCoordinates()[1:]
     1647        coordinates = new_bed.coordinates[1:]
    16171648        block, room_nr, bed_nr = coordinates
    16181649        bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = {
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/browser_templates/basepage.pt

    r7811 r9208  
    1818          <tal:password replace="view/hasPassword" />
    1919      </td>
    20     <tr>
     20    </tr>
    2121    <tal:files content="structure provider:files" />
    2222  </tbody>
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/interfaces.py

    r9169 r9208  
    156156    current_level = Attribute('The current level of the student')
    157157    current_mode = Attribute('The current mode of the student')
     158    current_verdict = Attribute('The current verdict of the student')
    158159    fullname = Attribute('All name parts separated by hyphens')
    159160    display_fullname = Attribute('The fullname of an applicant')
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/student.py

    r9169 r9208  
    134134        level = getattr(
    135135            self.get('studycourse', None), 'current_level', None)
     136        return level
     137
     138    @property
     139    def current_verdict(self):
     140        level = getattr(
     141            self.get('studycourse', None), 'current_verdict', None)
    136142        return level
    137143
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/tests/test_browser.py

    r9169 r9208  
    403403
    404404    layer = FunctionalLayer
     405
     406    def test_student_properties(self):
     407        self.student['studycourse'].current_level = 100
     408        self.assertEqual(self.student.current_level, 100)
     409        self.student['studycourse'].current_session = 2011
     410        self.assertEqual(self.student.current_session, 2011)
     411        self.student['studycourse'].current_verdict = 'A'
     412        self.assertEqual(self.student.current_verdict, 'A')
     413        return
    405414
    406415    def test_basic_auth(self):
     
    11831192        self.assertTrue(
    11841193            'You logged in.' in self.browser.contents)
    1185         # Student can upload a passport picture
     1194        # Admitted student can upload a passport picture
    11861195        self.browser.open(self.student_path + '/change_portrait')
    11871196        ctrl = self.browser.getControl(name='passportuploadedit')
     
    11941203            '<img align="middle" height="125px" src="passport.jpg" />'
    11951204            in self.browser.contents)
     1205        # Students can open admission letter
     1206        self.browser.getLink("Base Data").click()
     1207        self.browser.getLink("Download admission letter").click()
     1208        self.assertEqual(self.browser.headers['Status'], '200 Ok')
     1209        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
    11961210        # Student can view the clearance data
     1211        self.browser.open(self.student_path)
    11971212        self.browser.getLink("Clearance Data").click()
    11981213        # Student can't open clearance edit form before starting clearance
     
    15841599
    15851600    def test_student_previous_payments(self):
     1601        configuration = createObject('waeup.SessionConfiguration')
     1602        configuration.academic_session = 2000
     1603        configuration.clearance_fee = 3456.0
     1604        configuration.booking_fee = 123.4
     1605        self.student['studycourse'].entry_session = 2002
     1606        self.app['configuration'].addSessionConfiguration(configuration)
     1607        configuration2 = createObject('waeup.SessionConfiguration')
     1608        configuration2.academic_session = 2003
     1609        configuration2.clearance_fee = 3456.0
     1610        configuration2.booking_fee = 123.4
     1611        self.student['studycourse'].entry_session = 2002
     1612        self.app['configuration'].addSessionConfiguration(configuration2)
    15861613        # Login
    15871614        self.browser.open(self.login_path)
     
    16061633        # Previous session payment form is provided
    16071634        self.browser.getControl(name="form.p_category").value = ['schoolfee']
     1635        self.browser.getControl(name="form.p_session").value = ['2000']
     1636        self.browser.getControl(name="form.p_level").value = ['300']
     1637        self.browser.getControl("Create ticket").click()
     1638        self.assertMatches('...The previous session must not fall below...',
     1639                           self.browser.contents)
     1640        self.browser.getControl(name="form.p_category").value = ['schoolfee']
    16081641        self.browser.getControl(name="form.p_session").value = ['2004']
     1642        self.browser.getControl(name="form.p_level").value = ['300']
     1643        self.browser.getControl("Create ticket").click()
     1644        self.assertMatches('...This is not a previous session...',
     1645                           self.browser.contents)
     1646        self.browser.getControl(name="form.p_category").value = ['schoolfee']
     1647        self.browser.getControl(name="form.p_session").value = ['2003']
    16091648        self.browser.getControl(name="form.p_level").value = ['300']
    16101649        self.browser.getControl("Create ticket").click()
     
    16191658
    16201659        # Payment session is properly set
    1621         self.assertEqual(self.student['payments'][value].p_session, 2004)
     1660        self.assertEqual(self.student['payments'][value].p_session, 2003)
    16221661        self.assertEqual(self.student['payments'][value].p_level, 300)
    16231662
     
    19541993        self.browser.getControl(name="form.identifier").value = '123'
    19551994        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
    1956         self.browser.getControl("Get login credentials").click()
     1995        self.browser.getControl("Send login credentials").click()
    19571996        self.assertTrue('An email with' in self.browser.contents)
    19581997
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/utils.py

    r9169 r9208  
    138138        f_label = formatted_label(size=12) % _('Study Course')
    139139        f_label = Paragraph(f_label, style["Normal"])
    140         f_text = formatted_text(studentview.context.certcode, size=12)
     140        f_text = formatted_text(
     141            studentview.context['studycourse'].certificate.longtitle(), size=12)
     142        f_text = Paragraph(f_text, style["Normal"])
     143        data_right.append([f_label,f_text])
     144
     145        f_label = formatted_label(size=12) % _('Department')
     146        f_label = Paragraph(f_label, style["Normal"])
     147        f_text = formatted_text(
     148            studentview.context[
     149            'studycourse'].certificate.__parent__.__parent__.longtitle(),
     150            size=12)
     151        f_text = Paragraph(f_text, style["Normal"])
     152        data_right.append([f_label,f_text])
     153
     154        f_label = formatted_label(size=12) % _('Faculty')
     155        f_label = Paragraph(f_label, style["Normal"])
     156        f_text = formatted_text(
     157            studentview.context[
     158            'studycourse'].certificate.__parent__.__parent__.__parent__.longtitle(),
     159            size=12)
    141160        f_text = Paragraph(f_text, style["Normal"])
    142161        data_right.append([f_label,f_text])
     
    309328                return _('Study course data are incomplete.'), None
    310329            if previous_session:
     330                if previous_session < student['studycourse'].entry_session:
     331                    return _('The previous session must not fall below '
     332                             'your entry session.'), None
     333                if previous_session > student['studycourse'].current_session - 1:
     334                    return _('This is not a previous session.'), None
    311335                if previous_level == 100:
    312336                    amount = getattr(certificate, 'school_fee_1', 0.0)
     
    328352                    amount = getattr(certificate, 'school_fee_2', 0.0)
    329353        elif category == 'clearance':
    330             p_item = student['studycourse'].certificate.code
     354            try:
     355                p_item = student['studycourse'].certificate.code
     356            except (AttributeError, TypeError):
     357                return _('Study course data are incomplete.'), None
    331358            amount = academic_session.clearance_fee
    332359        elif category == 'bed_allocation':
     
    371398        entry_session = studycourse.entry_session
    372399        current_level = studycourse.current_level
    373         if not (entry_session and current_level and certificate):
    374             return
     400        if None in (entry_session, current_level, certificate):
     401            return d
    375402        end_level = certificate.end_level
    376403        if current_level == 10:
     
    398425        return available_beds[0]
    399426
     427    def renderPDFAdmissionLetter(self, view, student=None):
     428        """Render pdf admission letter.
     429        """
     430        # XXX: we have to fix the import problems here.
     431        from waeup.kofa.browser.interfaces import IPDFCreator
     432        from waeup.kofa.browser.pdf import format_html, NOTE_STYLE
     433        if student is None:
     434            return
     435        style = getSampleStyleSheet()
     436        creator = getUtility(IPDFCreator)
     437        data = []
     438        doc_title = view.label
     439        author = '%s (%s)' % (view.request.principal.title,
     440                              view.request.principal.id)
     441        footer_text = view.label
     442        if getattr(student, 'student_id', None) is not None:
     443            footer_text = "%s - %s - " % (student.student_id, footer_text)
     444
     445        # Admission text
     446        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     447        inst_name = grok.getSite()['configuration'].name
     448        text = trans(_(
     449            'This is to inform you that you have been provisionally'
     450            ' admitted into ${a} as follows:', mapping = {'a': inst_name}),
     451            portal_language)
     452        html = format_html(text)
     453        data.append(Paragraph(html, NOTE_STYLE))
     454        data.append(Spacer(1, 20))
     455
     456        # Student data
     457        data.append(render_student_data(view))
     458
     459        # Insert history
     460        data.append(Spacer(1, 20))
     461        datelist = student.history.messages[0].split()[0].split('-')
     462        creation_date = u'%s/%s/%s' % (datelist[2], datelist[1], datelist[0])
     463        text = trans(_(
     464            'Your Kofa student record was created on ${a}.',
     465            mapping = {'a': creation_date}),
     466            portal_language)
     467        html = format_html(text)
     468        data.append(Paragraph(html, NOTE_STYLE))
     469
     470        # Create pdf stream
     471        view.response.setHeader(
     472            'Content-Type', 'application/pdf')
     473        pdf_stream = creator.create_pdf(
     474            data, None, doc_title, author=author, footer=footer_text,
     475            note=None)
     476        return pdf_stream
     477
    400478    def renderPDF(self, view, filename='slip.pdf', student=None,
    401479                  studentview=None, tableheader=None, tabledata=None,
     
    428506
    429507        # Insert widgets
    430         data.append(Paragraph(view.title, style["Heading3"]))
    431         portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
    432         separators = getattr(self, 'SEPARATORS_DICT', {})
    433         table = creator.getWidgetsTable(
    434             view.form_fields, view.context, None, lang=portal_language,
    435             separators=separators)
    436         data.append(table)
     508        if view.form_fields:
     509            data.append(Paragraph(view.title, style["Heading3"]))
     510            portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     511            separators = getattr(self, 'SEPARATORS_DICT', {})
     512            table = creator.getWidgetsTable(
     513                view.form_fields, view.context, None, lang=portal_language,
     514                separators=separators)
     515            data.append(table)
    437516
    438517        # Insert scanned docs
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/viewlets.py

    r9169 r9208  
    150150
    151151class ContactActionButton(ManageActionButton):
    152     grok.order(4)
     152    grok.order(5)
    153153    grok.context(IStudent)
    154154    grok.view(StudentBaseDisplayFormPage)
     
    166166    target = 'manage_base'
    167167
     168class AdmissionSlipActionButton(ManageActionButton):
     169    grok.order(4)
     170    grok.context(IStudent)
     171    grok.view(StudentBaseDisplayFormPage)
     172    grok.require('waeup.viewStudent')
     173    icon = 'actionicon_pdf.png'
     174    text = _('Download admission letter')
     175    target = 'admission_slip.pdf'
     176
    168177class StudentTransfernButton(ManageActionButton):
    169     grok.order(5)
     178    grok.order(6)
    170179    grok.context(IStudent)
    171180    grok.view(StudentBaseDisplayFormPage)
     
    176185
    177186class StudentDeactivateActionButton(ManageActionButton):
    178     grok.order(6)
     187    grok.order(7)
    179188    grok.context(IStudent)
    180189    grok.view(StudentBaseDisplayFormPage)
     
    196205
    197206class StudentActivateActionButton(ManageActionButton):
    198     grok.order(6)
     207    grok.order(7)
    199208    grok.context(IStudent)
    200209    grok.view(StudentBaseDisplayFormPage)
     
    624633    @property
    625634    def targets(self):
    626         student_url = self.view.application_url() + (
    627             '/students/%s' % self.request.principal.id)
    628         #app_slip = getUtility(IExtFileStore).getFileByContext(
    629         #    self.context.student, 'application_slip')
     635        student = grok.getSite()['students'][self.request.principal.id]
     636        student_url = self.view.url(student)
     637        app_slip = getUtility(IExtFileStore).getFileByContext(
     638            student, 'application_slip')
    630639        targets = []
    631         #if app_slip:
    632         #    targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},]
     640        if app_slip:
     641            targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},]
    633642        targets += [
    634643            {'url':student_url, 'title':'Base Data'},
  • main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/userscontainer.py

    r9169 r9208  
    112112        If `filepath` is ``None``, a raw string with CSV data is returned.
    113113        """
    114         writer, outfile = self.get_csv_writer(filepath)
    115114        users = site.get('users', {})
    116115        return self.export(users.values(), filepath)
Note: See TracChangeset for help on using the changeset viewer.