Changeset 4789


Ignore:
Timestamp:
11 Jan 2010, 19:01:31 (15 years ago)
Author:
uli
Message:

Merge changes from ulif-layout back into trunk (finally).

Location:
waeup/trunk
Files:
12 deleted
23 edited
110 copied

Legend:

Unmodified
Added
Removed
  • waeup/trunk

    • Property svn:ignore set to
      develop-eggs
      bin
      parts
      .installed.cfg
  • waeup/trunk/buildout.cfg

    r3516 r4789  
    11[buildout]
    22develop = .
    3 parts = app data zopectl i18n test
     3parts = app data zopectl i18n test waeupdocs coverage-detect coverage-report
    44find-links = http://download.zope.org/distribution/
    55newest = false
    6 extends= http://grok.zope.org/releaseinfo/grok-0.13.cfg
     6extends= http://grok.zope.org/releaseinfo/grok-1.0.cfg
    77versions = versions
     8
     9[versions]
     10zc.sourcefactory = 0.3.5
     11zope.testing = 3.7.0
     12z3c.testsetup = 0.4.0
     13hurry.workflow = 0.10
     14hurry.query = 0.9.2
     15zope.xmlpickle = 3.4.0
     16hurry.yui = 2.7.0
     17hurry.zoperesource = 0.4
     18megrok.layout = 0.9
     19
    820
    921[app]
     
    2941                         />
    3042
     43              <grant permission="zope.View"
     44                     principal="zope.Authenticated" />
     45              <grant permission="zope.app.dublincore.view"
     46                     principal="zope.Authenticated" />
     47
    3148              <!-- Replace the following directive if you don't want
    3249                   public access -->
     
    3552              <grant permission="zope.app.dublincore.view"
    3653                     principal="zope.Anybody" />
     54
     55              <grant permission="waeup.Public"
     56                     principal="zope.Everybody" />
    3757
    3858              <role id="zope.Manager" title="Site Manager" />
     
    5777defaults = ['--tests-pattern', '^f?tests$', '-v']
    5878
     79# Collect test coverage data.
     80[coverage-detect]
     81recipe = zc.recipe.testrunner
     82eggs = waeup
     83defaults = ['--tests-pattern', '^f?tests$', '-v', '--coverage', 'coverage']
     84
     85# Create a coverage report.
     86# Make sure to run bin/coverage-detect to collect the data for the report
     87# first!
     88[coverage-report]
     89recipe = zc.recipe.egg
     90eggs = z3c.coverage
     91scripts = coverage
     92arguments = ('${buildout:parts-directory}/coverage-detect/coverage', '${buildout:parts-directory}/coverage-detect/coverage/report')
     93
     94
     95
    5996# this section named so that the i18n scripts are called bin/i18n...
    6097[i18n]
     
    64101location = src/waeup
    65102output = locales
     103
     104[waeupdocs]
     105recipe = z3c.recipe.sphinxdoc
     106eggs = waeup [docs]
     107default.css =
     108layout.html =
     109
  • waeup/trunk/setup.py

    r3520 r4789  
    2121      install_requires=['setuptools',
    2222                        'grok',
     23                        'grokui.admin',
    2324                        'hurry.query',
    2425                        'hurry.workflow',
    2526                        # Add extra requirements here
     27                        'zope.xmlpickle',
     28                        'hurry.yui',
     29                        'hurry.zoperesource',
     30                        'zc.sourcefactory',
     31                        'megrok.layout',
    2632                        ],
     33      extras_require=dict(
     34        docs=['Sphinx',
     35              'z3c.recipe.sphinxdoc',
     36             ],
     37        ),
    2738      entry_points="""
    2839      # Add entry points here
  • waeup/trunk/src/waeup/README.txt

    r3523 r4789  
    55
    66A portal software for student registration.
     7
     8Prerequisites
     9=============
     10
     11Before we can work with the environment, we have to register all the
     12utilities, adapters, etc. We grok the `waeup` package to do that::
     13
     14  >>> import grok
     15  >>> grok.testing.grok('waeup')
    716
    817Universities
     
    2231
    2332  >>> u.name
    24   u'Unnamed'
     33  u'Sample University'
    2534
    26 Universities are basically also containers for faculties.
     35Universities are basically also containers for faculties, students and
     36hostels::
     37
     38  >>> u['faculties']
     39  <waeup.university.facultycontainer.FacultyContainer object at 0x...>
     40
     41  >>> u['students']
     42  <waeup.student.studentcontainer.StudentContainer object at 0x...>
     43
     44  >>> u['hostels']
     45  <waeup.hostel.hostelcontainer.HostelContainer object at 0x...>
     46
     47We can export universities. For this we lookup an appropriate exporter
     48first::
     49
     50  >>> from waeup.interfaces import IWAeUPExporter
     51  >>> exporter = IWAeUPExporter(u)
     52  >>> exporter
     53  <waeup.utils.importexport.Exporter object at 0x...>
     54
     55Now we can trigger the export::
     56
     57  >>> exporter.export()
     58  <cStringIO.StringO object at 0x...>
     59
     60We can also get an XML representation as file. If we do not provide a
     61filepath, we will get an instance of `cStringIO.StringIO`, i.e. a
     62memory file::
     63
     64  >>> from waeup.interfaces import IWAeUPXMLExporter
     65  >>> exporter = IWAeUPXMLExporter(u)
     66  >>> f = exporter.export()
     67  >>> f
     68  <cStringIO.StringO object at 0x...>
     69
     70  >>> print f.read()
     71  <?xml version="1.0" encoding="utf-8" ?>
     72  <pickle>
     73    <initialized_object id="...">
     74  ...
     75  </pickle>
     76
    2777
    2878Faculties
     
    3484We can create faculties easily::
    3585
    36   >>> from waeup.app import Faculty
     86  >>> from waeup.university.faculty import Faculty
    3787  >>> f = Faculty()
    3888  >>> f
    39   <waeup.app.Faculty object at 0x...>
     89  <waeup.university.faculty.Faculty object at 0x...>
    4090
    4191Also faculties want to be named::
    4292
    43   >>> f.name
     93  >>> f.title
    4494  u'Unnamed Faculty'
    4595
  • waeup/trunk/src/waeup/__init__.py

    r3521 r4789  
     1import grok
     2from waeup.interfaces import IWAeUPObject
     3from zope.annotation.attribute import AttributeAnnotations
     4from zope.annotation.interfaces import IAnnotations
     5
     6class WAeUPAttributeAnnotations(AttributeAnnotations, grok.Adapter):
     7    """An adapter to IAnnotations for any WAeUPObject.
     8
     9    Providing this adapter, each WAeUP object becomes (attribute)
     10    annotatable.
     11    """
     12    grok.provides(IAnnotations)
     13    grok.context(IWAeUPObject)
  • waeup/trunk/src/waeup/app.py

    r3530 r4789  
    11import grok
    2 from grok import index
    3 from zope.interface import Interface
    4 from interfaces import IUniversity
    5 from setup import app_setup
    6 from viewlets import MainArea
     2from zope.app.authentication.authentication import PluggableAuthentication
     3from zope.app.security.interfaces import IAuthentication
     4from zope.component import createObject
     5
     6from waeup.interfaces import IUniversity, ICSVDataReceivers
     7from waeup.authentication import setup_authentication
     8from waeup.datacenter import DataCenter
     9from waeup.users import UserContainer
    710
    811class University(grok.Application, grok.Container):
    9     grok.implements(IUniversity)
    10 
    11     def __init__(self, name=u'Unnamed', **kw):
     12    """A university.
     13    """
     14    grok.implements(IUniversity, ICSVDataReceivers)
     15    # Setup authentication for this app. Note: this is only
     16    # initialized, when a new instance of this app is created.
     17    grok.local_utility(
     18        PluggableAuthentication, provides = IAuthentication,
     19        setup = setup_authentication)
     20   
     21    def __init__(self, name=u'Sample University', **kw):
    1222        super(University, self).__init__(**kw)
    1323        self.name = name
    14         app_setup.setup(self)
     24        self.setup()
    1525
    16 class Content(grok.Viewlet):
    17     grok.viewletmanager(MainArea)
    18     grok.context(IUniversity)
    19 
    20 
    21 class Edit(grok.EditForm):
    22     grok.context(University)
    23     grok.AutoFields(IUniversity)
     26    def setup(self):
     27        self['students'] = createObject(u'waeup.StudentContainer')
     28        self['hostels'] = createObject(u'waeup.HostelContainer')
     29        self['faculties'] = createObject(u'waeup.FacultyContainer')
     30        self['users'] = UserContainer()
     31        self['datacenter'] = DataCenter()
  • waeup/trunk/src/waeup/browser.txt

    r3523 r4789  
    1 The waeup package
    2 ********************
     1Browsing the WAeUP portal
     2*************************
     3
     4Here we visit all parts of a WAeUP portal using a browser.
    35
    46:Test-Layer: functional
     
    3436  >>> browser.open('http://localhost/myuniversity')
    3537  >>> print browser.contents
    36   <html>...
    37   ...<h1>Congratulations!</h1>...
     38  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     39  ...Welcome to the...
    3840  ...
    3941
    4042We can also get an edit view of a university::
    4143
    42   >>> browser.open('http://localhost/myuniversity/edit')
    43   >>> print browser.contents
    44   <html>...
    45   ...<form action="http://localhost/myuniversity/edit"
     44  >>> browser.open('http://localhost/myuniversity/manage')
     45  >>> print browser.contents
     46  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     47  ...<form action="http://localhost/myuniversity/manage"
    4648  ...
    4749
    4850The edit form contains the default value for the university name::
    4951
    50   >>> 'Unnamed' in browser.contents
     52  >>> 'Sample University' in browser.contents
    5153  True
     54
     55We can export a university as XML::
     56
     57  >>> browser.open('http://localhost/myuniversity/export.xml')
     58  >>> print browser.contents
     59  <?xml version="1.0" encoding="utf-8" ?>
     60  <pickle>
     61  ...
     62  </pickle>
     63
     64  >>> print browser.headers
     65  Status: 200 Ok
     66  Content-Length: ...
     67  Content-Type: text/xml; charset=UTF-8
     68  X-Powered-By: Zope (www.zope.org), Python (www.python.org)
     69
    5270
    5371Faculties
    5472=========
    5573
    56 Wie can add faculties, by calling and submitting an add form of an
    57 university. The add form can be retrieved by calling the
    58 ``addfaculty`` view of an university::
    59 
    60   >>> browser.open('http://localhost/myuniversity/addfaculty')
    61   >>> print browser.contents
    62   <html>...
     74Faculties are stored in a special container of `IUniversity`
     75instances. The container is called ``faculties`` and provides an
     76add-form to add new faculties::
     77
     78  >>> browser.open('http://localhost/myuniversity/faculties/add')
     79  >>> print browser.contents
     80  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
    6381  ...<span class="required">*</span><span>Name of Faculty</span>
    6482  ...
     
    6684We fill in a new name for our new faculty::
    6785
    68   >>> ctrl = browser.getControl(name='form.name')
     86  >>> ctrl = browser.getControl(name='form.title')
    6987  >>> ctrl.value = 'TestFac'
    7088
     89Furthermore we add a prefix and a code (kind of abbreviation):
     90
     91  >>> browser.getControl(name='form.code').value = 'TF'
     92
    7193Finally we click on 'Add Faculty' to add the new thing::
    7294
    73   >>> browser.getControl('Add Faculty').click()
    74   >>> #print browser.contents
     95  >>> browser.getControl('Add faculty').click()
    7596
    7697We can view a faculty by browsing a URL like this::
    7798
    78   >>> browser.open('http://localhost/myuniversity/TestFac')
     99  >>> browser.open('http://localhost/myuniversity/faculties/TF')
    79100
    80101Afterwards, the faculty should be visible:
    81102
    82   >>> browser.open('http://localhost/myuniversity/')
    83   >>> print browser.contents
    84   <html...
    85   ...<h2>Your faculties:</h2>
    86   ...<a href="http://localhost/myuniversity/TestFac">TestFac</a>
    87   ...
    88 
    89 
    90 
     103  >>> browser.open('http://localhost/myuniversity/faculties')
     104  >>> print browser.contents
     105  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     106  ...<h2>Registered Faculties</h2>
     107  ...<td><a href="http://localhost/myuniversity/faculties/TF">TF</a></td><td>Faculty of TestFac</td>
     108  ...
     109
     110We can 'visit' each faculty by clicking on the appropriate link:
     111
     112  >>> browser.getLink('TF').click()
     113  >>> print browser.contents
     114  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     115  ...<h2>Faculty of TestFac (TF)</h2>...
     116  ...
     117
     118Modifying faculties
     119-------------------
     120
     121A faculty can directly be reached by its code:
     122
     123  >>> browser.open('http://localhost/myuniversity/faculties/TF')
     124
     125We can change the settings for a faculty by clicking on the
     126provided 'Edit  settings' button:
     127
     128  >>> browser.getLink('Edit settings').click()
     129
     130Let's set a new title and save the form:
     131
     132  >>> browser.getControl(name='form.title').value = "My test faculty"
     133  >>> browser.getControl(name='form.actions.save').click()
     134
     135Clicking 'Save' we will stay on the settings form. So we can change
     136the department again. This time we will return to the overview page
     137afterwards:
     138
     139  >>> browser.getControl(name='form.title').value = "My renamed faculty"
     140  >>> ctrl = browser.getControl("Save and return")
     141  >>> ctrl.click()
     142
     143If we go to the settings page and click ``Cancel`` nothing will be
     144changed:
     145
     146  >>> browser.getLink('Edit settings').click()
     147  >>> browser.getControl(name='form.title').value = "Blah"
     148  >>> browser.getControl('Cancel').click()
     149
     150Our faculty was indeed renamed to ``My renamed faculty`` and not to
     151``Blah``:
     152
     153  >>> browser.open('http://localhost/myuniversity/faculties')
     154  >>> print browser.contents
     155  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     156  ...<h2>Registered Faculties</h2>
     157  ...<td><a href="http://localhost/myuniversity/faculties/TF">TF</a></td><td>Faculty of My renamed faculty</td>
     158  ...
     159
     160
     161Departments
     162===========
     163
     164Adding departments
     165------------------
     166
     167Departments are stored in :class:`IFaculty` instances with their code
     168as key. Faculties therefore are also department containers. Faculties
     169provides an add-form to add new departments:
     170
     171  >>> browser.open('http://localhost/myuniversity/faculties/TF/add')
     172  >>> print browser.contents
     173  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     174  ...<span class="required">*</span><span>Name of Department</span>
     175  ...
     176
     177We fill in a new name for our new department:
     178
     179  >>> ctrl = browser.getControl(name='form.title')
     180  >>> ctrl.value = 'TestDept'
     181
     182Furthermore we add a code (kind of abbreviation):
     183
     184  >>> browser.getControl(name='form.code').value = 'TD'
     185
     186Finally we click on 'Add Department' to add the new thing::
     187
     188  >>> browser.getControl('Add department').click()
     189
     190If we try to register a department under the same code twice we will
     191get an error:
     192
     193  >>> browser.open('http://localhost/myuniversity/faculties/TF/add')
     194  >>> ctrl = browser.getControl(name='form.title')
     195  >>> ctrl.value = 'Another TestDept with same code'
     196  >>> browser.getControl(name='form.code').value = 'TD'
     197  >>> browser.getControl('Add department').click()
     198  >>> print browser.contents
     199  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     200  ...<div>The name chosen already exists in the database</div>
     201  ...
     202
     203We can view a department by browsing a URL like this:
     204
     205  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     206
     207Afterwards, the department should be visible:
     208
     209  >>> browser.open('http://localhost/myuniversity/faculties/TF')
     210  >>> print browser.contents
     211  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     212  ...<h2>Registered Departments:</h2>
     213  ...<td><a href="http://localhost/myuniversity/faculties/TF/TD">TD</a></td><td>Department of TestDept</td>
     214  ...
     215
     216
     217Modifying departments
     218---------------------
     219
     220We can change the settings for a department by clicking on the
     221provided 'Edit settings' button:
     222
     223  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     224  >>> browser.getLink('Edit settings').click()
     225
     226Let's set a new title and save the form:
     227
     228  >>> browser.getControl(name='form.title').value = "My test dept"
     229  >>> browser.getControl(name='form.actions.save').click()
     230
     231Clicking 'Save' we will stay on the settings form. So we can change
     232the department again. This time we will return to the overview page
     233afterwards:
     234
     235  >>> browser.getControl(name='form.title').value = "My renamed dept"
     236  >>> ctrl = browser.getControl("Save and return")
     237  >>> ctrl.click()
     238
     239If we go to the settings page and click ``Cancel`` nothing will be
     240changed:
     241
     242  >>> browser.getLink('Edit settings').click()
     243  >>> browser.getControl(name='form.title').value = "Blah"
     244  >>> browser.getControl('Cancel').click()
     245
     246Our department was indeed renamed to ``My renamed dept`` and not to
     247``Blah``:
     248
     249  >>> browser.open('http://localhost/myuniversity/faculties/TF')
     250  >>> print browser.contents
     251  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     252  ...<h2>Registered Departments:</h2>
     253  ...<td><a href="http://localhost/myuniversity/faculties/TF/TD">TD</a></td><td>Department of My renamed dept</td>
     254  ...
     255
     256
     257Courses
     258=======
     259
     260Once we have a department, we can add courses.
     261
     262Adding courses
     263--------------
     264
     265Courses are stored in :class:`ICourseContainer` instances with their
     266code as key. CourseContainers are normally availabe as `course`
     267attribute of :class:`waeup.university.department.Department`
     268instances.
     269
     270To ease the life of users we do not require to browse the
     271coursecontainers (which have a rather flat user interface), but
     272provide adding of courses in department views.
     273
     274Each department provides a ``Add course`` action button near top.
     275
     276Departments provide an add-form to add new courses:
     277
     278  >>> dept_url = 'http://localhost/myuniversity/faculties/TF/TD'
     279  >>> browser.open(dept_url + '/addcourse')
     280  >>> print browser.contents
     281  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     282  ...<span class="required">*</span><span>Title of course</span>
     283  ...
     284
     285We fill in a name for our new course:
     286
     287  >>> ctrl = browser.getControl(name='form.title')
     288  >>> ctrl.value = 'My Course 1'
     289
     290Furthermore we add a code (kind of abbreviation):
     291
     292  >>> browser.getControl(name='form.code').value = 'COURSE1'
     293
     294This course will take place in the the first semester, so we set the
     295`semester` value to 1:
     296
     297  >>> ctrl = browser.getControl(name='form.semester')
     298  >>> ctrl.options
     299  ['0', '1', '2', '3']
     300
     301  >>> ctrl.displayOptions
     302  ['N/A', 'First Semester', 'Second Semester', 'Combined']
     303
     304  >>> ctrl.value = ['1']
     305
     306Finally, we create the course:
     307
     308  >>> browser.getControl('Add course').click()
     309
     310If we try to register a course under the same code twice we will
     311get an error:
     312
     313  >>> browser.open(dept_url + '/addcourse')
     314  >>> ctrl = browser.getControl(name='form.title')
     315  >>> ctrl.value = 'Another course with same code'
     316  >>> browser.getControl(name='form.code').value = 'COURSE1'
     317  >>> browser.getControl('Add course').click()
     318  >>> print browser.contents
     319  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     320  ...<div>The code chosen already exists in the database</div>
     321  ...
     322
     323Our course will be linked under the code on the department page:
     324
     325  >>> browser.open(dept_url)
     326  >>> browser.getLink('COURSE1').click()
     327  >>> browser.url
     328  'http://localhost/myuniversity/faculties/TF/TD/courses/COURSE1'
     329
     330Before we really add a course we can cancel the action and will be
     331redirected to the department page:
     332
     333  >>> browser.open(dept_url + '/addcourse')
     334  >>> browser.getControl('Cancel').click()
     335  >>> browser.url
     336  'http://localhost/myuniversity/faculties/TF/TD'
     337
     338
     339Modifying courses
     340-----------------
     341
     342We can change the settings for a course by clicking on the provided
     343'Edit settings' link:
     344
     345  >>> browser.open(dept_url + '/courses/COURSE1')
     346  >>> browser.getLink('Edit settings').click()
     347
     348When modifying a course, we cannot change the code any more:
     349
     350  >>> browser.getControl(name='form.code')
     351  Traceback (most recent call last):
     352  ...
     353  LookupError: name 'form.code'
     354
     355Let's set a new title and save the form:
     356
     357  >>> browser.getControl(name='form.title').value = "My test course"
     358  >>> browser.getControl(name='form.actions.save').click()
     359
     360Clicking 'Save' we will stay on the settings form. So we can change
     361the course again. This time we will return to the overview page
     362afterwards:
     363
     364  >>> browser.getControl(name='form.title').value = "My renamed course"
     365  >>> ctrl = browser.getControl("Save and return")
     366  >>> ctrl.click()
     367
     368If we go to the settings page and click ``Cancel`` nothing will be
     369changed:
     370
     371  >>> browser.getLink('Edit settings').click()
     372  >>> browser.getControl(name='form.title').value = "Blah"
     373  >>> browser.getControl('Cancel').click()
     374
     375Our course was indeed renamed to ``My renamed course`` and not to
     376``Blah``:
     377
     378  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     379  >>> print browser.contents
     380  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     381  ...<div name="thecoursetable">
     382  ...<td>My renamed course</td>...
     383  ...
     384
     385
     386Deleting courses
     387----------------
     388
     389We can delete courses by browsing the containing department and
     390clicking on the appropriate 'Delete' button. As we have only one
     391course, there is only one 'Delete' button yet:
     392
     393  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     394  >>> 'My renamed course' in browser.contents
     395  True
     396
     397  >>> browser.getControl('Delete').click()
     398
     399  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     400  >>> 'My renamed course' in browser.contents
     401  False
     402
     403
     404Certificates
     405============
     406
     407Once we have a department, we can add also certificats.
     408
     409Adding certificates
     410-------------------
     411
     412Certificates are stored in :class:`ICertificateContainer` instances
     413with their code as key. CertificateContainers are normally availabe as
     414`certificates` attribute of
     415:class:`waeup.university.department.Department` instances.
     416
     417To ease the life of users we do not require to browse the
     418certificatecontainers (which have in fact no user interface), but
     419provide adding of certificates in department views.
     420
     421Each department provides a ``Add certificate`` action button near top.
     422
     423Departments provide an add-form to add new certificates:
     424
     425  >>> dept_url = 'http://localhost/myuniversity/faculties/TF/TD'
     426  >>> browser.open(dept_url + '/addcertificate')
     427  >>> print browser.contents
     428  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     429  ...<span class="required">*</span><span>title</span>
     430  ...
     431
     432We fill in a name for our new cert:
     433
     434  >>> ctrl = browser.getControl(name='form.title')
     435  >>> ctrl.value = 'My Certificate 1'
     436
     437Furthermore we add a code (kind of abbreviation):
     438
     439  >>> browser.getControl(name='form.code').value = 'CERT1'
     440
     441Set the remaining required fields:
     442
     443  >>> browser.getControl(name='form.category').value = 'UME'
     444  >>> browser.getControl(name='form.study_mode').value = 'combined'
     445  >>> browser.getControl(name='form.start_level').value = '100'
     446  >>> browser.getControl(name='form.end_level').value = '400'
     447  >>> browser.getControl(name='form.application_category').value = 'UME'
     448  >>> browser.getControl(name='form.m_prefix').value = 'something'
     449  >>> browser.getControl(name='form.max_pass').value = '400'
     450
     451Finally, we create the certificate:
     452
     453  >>> browser.getControl('Add certificate').click()
     454
     455If we try to register a certificate under the same code twice we will
     456get an error:
     457
     458  >>> browser.open(dept_url + '/addcertificate')
     459  >>> ctrl = browser.getControl(name='form.title')
     460  >>> ctrl.value = 'Another cert with same code'
     461  >>> browser.getControl(name='form.code').value = 'CERT1'
     462  >>> browser.getControl(name='form.category').value = 'UME'
     463  >>> browser.getControl(name='form.study_mode').value = 'combined'
     464  >>> browser.getControl(name='form.start_level').value = '100'
     465  >>> browser.getControl(name='form.end_level').value = '400'
     466  >>> browser.getControl(name='form.application_category').value = 'UME'
     467  >>> browser.getControl(name='form.m_prefix').value = 'something'
     468  >>> browser.getControl(name='form.max_pass').value = '400'
     469
     470  >>> browser.getControl('Add certificate').click()
     471  >>> print browser.contents
     472  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     473  ...<div>The name chosen already exists in the database</div>
     474  ...
     475
     476Our certificate will be linked under the code on the department page:
     477
     478  >>> browser.open(dept_url)
     479  >>> browser.getLink('CERT1').click()
     480  >>> browser.url
     481  'http://localhost/myuniversity/faculties/TF/TD/certificates/CERT1'
     482
     483Before we really add a certificate we can cancel the action and will be
     484redirected to the department page:
     485
     486  >>> browser.open(dept_url + '/addcertificate')
     487  >>> browser.getControl('Cancel').click()
     488  >>> browser.url
     489  'http://localhost/myuniversity/faculties/TF/TD'
     490
     491
     492Modifying certificates
     493----------------------
     494
     495We can change the settings for a cert by clicking on the provided
     496'Edit settings' link:
     497
     498  >>> browser.open(dept_url + '/certificates/CERT1')
     499  >>> browser.getLink('Edit settings').click()
     500
     501When modifying a certificate, we cannot change the code any more:
     502
     503  >>> browser.getControl(name='form.code')
     504  Traceback (most recent call last):
     505  ...
     506  LookupError: name 'form.code'
     507
     508Let's set a new title and save the form:
     509
     510  >>> browser.getControl(name='form.title').value = "My test cert"
     511  >>> browser.getControl(name='form.actions.save').click()
     512
     513Clicking 'Save' we will stay on the settings form. So we can change
     514the cert again. This time we will return to the overview page
     515afterwards:
     516
     517  >>> browser.getControl(name='form.title').value = "My renamed cert"
     518  >>> ctrl = browser.getControl("Save and return")
     519  >>> ctrl.click()
     520
     521If we go to the settings page and click ``Cancel`` nothing will be
     522changed:
     523
     524  >>> browser.getLink('Edit settings').click()
     525  >>> browser.getControl(name='form.title').value = "Blah"
     526  >>> browser.getControl('Cancel').click()
     527
     528Our certificate was indeed renamed to ``My renamed cert`` and not to
     529``Blah``:
     530
     531  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     532  >>> print browser.contents
     533  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     534  ...<div name="thecerttable">
     535  ...<td>My renamed cert</td>...
     536  ...
     537
     538
     539Deleting certificates
     540---------------------
     541
     542We can delete certificates by browsing the containing department and
     543clicking on the appropriate 'Delete' button. As we have only one
     544certificate, there is only one 'Delete' button yet:
     545
     546  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     547  >>> 'My renamed cert' in browser.contents
     548  True
     549
     550  >>> browser.getControl('Delete').click()
     551
     552  >>> browser.open('http://localhost/myuniversity/faculties/TF/TD')
     553  >>> 'My renamed cert' in browser.contents
     554  False
     555
     556
     557CertificateCourses
     558==================
     559
     560Once we have a certificate, we can add also certificate courses. These
     561are references to courses with some extra data.
     562
     563Before we can work with certificate courses, we need some certificates
     564and courses to be available.
     565
     566  >>> browser.open(dept_url + '/addcourse')
     567  >>> ctrl = browser.getControl(name='form.title')
     568  >>> ctrl.value = 'Another course with same code'
     569  >>> browser.getControl(name='form.code').value = 'COURSE1'
     570  >>> browser.getControl(name='form.title').value = 'Course 1' 
     571  >>> browser.getControl('Add course').click()
     572
     573  >>> browser.open(dept_url + '/addcourse')
     574  >>> ctrl = browser.getControl(name='form.title')
     575  >>> ctrl.value = 'Another course with same code'
     576  >>> browser.getControl(name='form.code').value = 'COURSE2'
     577  >>> browser.getControl(name='form.title').value = 'Course 2'
     578  >>> browser.getControl('Add course').click()
     579
     580  >>> browser.open(dept_url + '/addcertificate')
     581  >>> ctrl = browser.getControl(name='form.title')
     582  >>> ctrl.value = 'Another cert with same code'
     583  >>> browser.getControl(name='form.code').value = 'CERT1'
     584  >>> browser.getControl(name='form.title').value = 'Certificate 1'
     585  >>> browser.getControl(name='form.category').value = 'UME'
     586  >>> browser.getControl(name='form.study_mode').value = 'combined'
     587  >>> browser.getControl(name='form.start_level').value = '100'
     588  >>> browser.getControl(name='form.end_level').value = '400'
     589  >>> browser.getControl(name='form.application_category').value = 'UME'
     590  >>> browser.getControl(name='form.m_prefix').value = 'something'
     591  >>> browser.getControl(name='form.max_pass').value = '400'
     592  >>> browser.getControl('Add certificate').click()
     593
     594  >>> browser.open(dept_url + '/addcertificate')
     595  >>> ctrl = browser.getControl(name='form.title')
     596  >>> ctrl.value = 'Another cert with same code'
     597  >>> browser.getControl(name='form.code').value = 'CERT2'
     598  >>> browser.getControl(name='form.title').value = 'Certificate 2'
     599  >>> browser.getControl(name='form.category').value = 'UME'
     600  >>> browser.getControl(name='form.study_mode').value = 'combined'
     601  >>> browser.getControl(name='form.start_level').value = '100'
     602  >>> browser.getControl(name='form.end_level').value = '400'
     603  >>> browser.getControl(name='form.application_category').value = 'UME'
     604  >>> browser.getControl(name='form.m_prefix').value = 'something'
     605  >>> browser.getControl(name='form.max_pass').value = '400'
     606  >>> browser.getControl('Add certificate').click()
     607 
     608
     609Adding certificatecourses
     610-------------------------
     611
     612Certcourses are stored in :class:`ICertificate` instances
     613with their code as key.
     614
     615Each certificate provides a ``Add course`` action button near top.
     616
     617Certificates provide an add-form to add new certcourses:
     618
     619  >>> cert_url = dept_url + '/certificates/CERT1'
     620  >>> browser.open(cert_url + '/addcertificatecourse')
     621  >>> print browser.contents
     622  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     623  ...<span class="required">*</span><span>Level of this course</span>
     624  ...
     625
     626In the add-form we will get a list of available courses to select
     627from. This list will contain all courses stored in the site, not only
     628the ones from local department:
     629
     630  >>> ctrl = browser.getControl(name='form.course')
     631  >>> ctrl.displayOptions
     632  ['COURSE1 Course 1', 'COURSE2 Course 2']
     633
     634If we do not select anything else, the first item will be
     635selected. That's okay for us. Finally, we create the
     636certificatecourse:
     637
     638  >>> browser.getControl('Add course').click()
     639
     640Our certificatecourse will be linked on the parent certificate page:
     641
     642  >>> browser.open(cert_url)
     643  >>> browser.getLink('COURSE1').click()
     644  >>> browser.url
     645  'http://localhost/my...sity/faculties/TF/TD/certificates/CERT1/COURSE1_100'
     646
     647When we started to add a new certificatecourse, we can also cancel the
     648process before submitting. This will bring us back to the certificate
     649page:
     650
     651  >>> browser.open(cert_url + '/addcertificatecourse')
     652  >>> browser.getControl('Cancel').click()
     653  >>> browser.url
     654  'http://localhost/myuniversity/faculties/TF/TD/certificates/CERT1'
     655
     656
     657Modifying certificatecourses
     658----------------------------
     659
     660We can change the settings for a certcourse by clicking on the
     661provided 'Edit settings' link:
     662
     663  >>> browser.open(cert_url + '/COURSE1_100')
     664  >>> browser.getLink('Edit settings').click()
     665
     666Let's set a new level (it was 100 before) and save the form. This will
     667bring us to the certificate index page afterwards:
     668
     669  >>> browser.getControl(name='form.level').value = "200"
     670  >>> ctrl = browser.getControl("Save and return")
     671  >>> ctrl.click()
     672
     673As we changed the level, also the URL will change:
     674
     675  >>> browser.getLink('COURSE1').click()
     676  >>> browser.url
     677  'http://localhost/myun.../TF/TD/certificates/CERT1/COURSE1_200'
     678
     679If we go to the settings page and click ``Cancel`` nothing will be
     680changed:
     681
     682  >>> browser.getLink('Edit settings').click()
     683  >>> browser.getControl(name='form.level').value = "666"
     684  >>> browser.getControl('Cancel').click()
     685
     686Our certcourse provides a new level of 200 and not 666:
     687
     688  >>> browser.open(cert_url + '/COURSE1_200')
     689  >>> print browser.contents
     690  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     691  ...Level of this course
     692  ...200...
     693  ...
     694
     695
     696Deleting certificatecourses
     697---------------------------
     698
     699We can delete certcourses by browsing the containing certificate and
     700clicking on the appropriate 'Delete' button. As we have only one
     701certcourse, there is only one 'Delete' button yet:
     702
     703  >>> browser.open(cert_url)
     704  >>> 'COURSE1_200' in browser.contents
     705  True
     706
     707  >>> browser.getControl('delete').click()
     708
     709  >>> browser.open(cert_url)
     710  >>> 'COURSE1_200' in browser.contents
     711  False
     712
     713
     714Data Center
     715===========
     716
     717The data center helps us uploading files for later import or similar.
     718
     719  >>> browser.open('http://localhost/myuniversity')
     720  >>> browser.getLink('Data Center').click()
     721
     722Setting the file path
     723---------------------
     724
     725A datacenter stores files in a path in filesystem. By default this is
     726a directory in the sources:
     727
     728  >>> print browser.contents
     729  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     730  ...
     731  <b>Storage path:</b> <span>/.../waeup/files</span>
     732  ...
     733
     734Going to 'Settings` we can change the path:
     735
     736  >>> browser.getLink('Edit settings').click()
     737  >>> pathsetting = browser.getControl(name='newpath')
     738
     739We create a directory and set it as our upload path:
     740
     741  >>> import os
     742  >>> cwd = os.getcwd()
     743  >>> uploadpath = os.path.join(cwd, 'testfiles')
     744  >>> os.mkdir(uploadpath)
     745  >>> pathsetting.value = uploadpath
     746
     747And submit the form:
     748
     749  >>> browser.getControl(name='save').click()
     750
     751We clean up the set directory path, as there might be some files be
     752copied some files from installation:
     753
     754  >>> files = os.listdir(uploadpath)
     755  >>> for filename in files:
     756  ...   os.unlink(os.path.join(uploadpath, filename))
     757
     758The new upload directory is now empty:
     759
     760  >>> os.listdir(uploadpath)
     761  []
     762
     763
     764Uploading files
     765---------------
     766
     767Now we can upload files. Most interesting files might be CSV files,
     768that can be imported lateron. We create a CSV file containing faculty
     769descriptions:
     770
     771  >>> open('faculties.csv', 'wb').write(
     772  ... """code,title,title_prefix
     773  ... FA,Arts,Faculty
     774  ... FS,Sciences,Faculty
     775  ... """)
     776
     777Now we can upload this file. To do this, we first go to the upload
     778page:
     779
     780  >>> browser.getLink('Upload data').click()
     781
     782and enter the appropriate data in the form:
     783
     784  >>> filewidget = browser.getControl(name='uploadfile:file')
     785  >>> filewidget
     786  <Control name='uploadfile:file' type='file'>
     787 
     788A sidenote for developers: by marking the filewidget with the
     789``:file`` extension, we tell Zope to handle this field as a file
     790widget.
     791
     792  >>> import cStringIO
     793  >>> filecontents = cStringIO.StringIO(
     794  ...   open('faculties.csv', 'rb').read())
     795  >>> filewidget.add_file(filecontents, 'text/plain', 'myfaculties.csv')
     796 
     797  >>> browser.getControl(name='SUBMIT').click()
     798
     799The file was indeed uploaded:
     800
     801  >>> os.listdir(uploadpath)
     802  ['myfaculties.csv']
     803
     804We create and upload also a CSV file containing departments:
     805
     806  >>> open('departments.csv', 'wb').write(
     807  ... """code,title,title_prefix,faculty_code
     808  ... LIT,Literature,Department,FA
     809  ... SOC,Sociology,Department,FA
     810  ... PHY,Physics,Department,FS
     811  ... INF,Informatics,Department,FS
     812  ... MAT,Math,Department,FS
     813  ... """)
     814
     815  >>> browser.open('http://localhost/myuniversity/datacenter/upload')
     816  >>> browser.getControl(name='uploadfile:file').add_file(
     817  ...   cStringIO.StringIO(open('departments.csv', 'rb').read()),
     818  ...   'text/plain', 'mydepartments.csv')
     819  >>> browser.getControl(name='SUBMIT').click()
     820
     821We create and upload also a CSV file containing courses:
     822
     823  >>> open('courses.csv', 'wb').write(
     824  ... """code,level,title,passmark,credits,semester,faculty,department
     825  ... LI1,,Introduction to Literature I,40,2,1,FA,LIT
     826  ... LI2,,Introduction to Literature II,40,2,2,FA,LIT
     827  ... AN1,000,Analysis I,40,2,1,FS,MAT
     828  ... AN2,000,Analysis II,40,2,2,FS,MAT
     829  ... """)
     830
     831  >>> browser.open('http://localhost/myuniversity/datacenter/upload')
     832  >>> browser.getControl(name='uploadfile:file').add_file(
     833  ...   cStringIO.StringIO(open('courses.csv', 'rb').read()),
     834  ...   'text/plain', 'mycourses.csv')
     835  >>> browser.getControl(name='SUBMIT').click()
     836
     837We create and upload also a CSV file containing certificates:
     838
     839  >>> open('certificates.csv', 'wb').write(
     840  ... """code,title,faculty_code,department_code,category,study_mode,end_level,m_prefix,max_pass,start_level,application_category,review_state
     841  ... LBA,BACHELOR OF LITERATURE,FA,LIT,UG,ug_ft,500,LIT,30,100,basic,checked
     842  ... LMA,MASTER OF LITERATURE,FA,LIT,UG,ug_pt,500,LIT,30,100,cest,checked
     843  ... DME,DIPLOMA OF MATH,FS,MAT,DP,dp_ft,200,DME,30,100,cest,unchecked
     844  ... """)
     845
     846  >>> browser.open('http://localhost/myuniversity/datacenter/upload')
     847  >>> browser.getControl(name='uploadfile:file').add_file(
     848  ...   cStringIO.StringIO(open('certificates.csv', 'rb').read()),
     849  ...   'text/plain', 'mycertificates.csv')
     850  >>> browser.getControl(name='SUBMIT').click()
     851
     852We create and upload also a CSV file containing certificate courses:
     853
     854  >>> open('certcourses.csv', 'wb').write(
     855  ... """code,faculty_code,department_code,certificate_code,level,core_or_elective
     856  ... LI1,FA,LIT,LBA,100,True
     857  ... LI2,FA,LIT,LBA,200,True
     858  ... """)
     859
     860  >>> browser.open('http://localhost/myuniversity/datacenter/upload')
     861  >>> browser.getControl(name='uploadfile:file').add_file(
     862  ...   cStringIO.StringIO(open('certcourses.csv', 'rb').read()),
     863  ...   'text/plain', 'mycertcourses.csv')
     864  >>> browser.getControl(name='SUBMIT').click()
     865
     866
     867Importing a CSV file
     868--------------------
     869
     870As the uploaded file conforms with what we expect from a faculty-CSV
     871file, we can import it. To do this we click the ``Import CSV data``
     872link:
     873
     874  >>> browser.open('http://localhost/myuniversity/datacenter')
     875  >>> browser.getLink('Import CSV data').click()
     876
     877Here we get all importable files listed:
     878
     879  >>> print browser.contents
     880  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     881  ...The following files are available for import:...
     882  ...mycertcourses.csv...Certificate Course Importer...
     883  ...mycertificates.csv...Certificate Importer...
     884  ...mycourses.csv...Course Importer...
     885  ...mydepartments.csv...Department Importer...
     886  ...myfaculties.csv...Faculty Importer...
     887  ...
     888
     889As we can see, the files are analyzed on-the-fly so the system can
     890tell us what kind of importer is available for each.
     891
     892We do import the faculty file by clicking on ``Import!`` next to the
     893appropriate file:
     894
     895  >>> facimport = browser.getControl('Import!', index=4)
     896  >>> facimport.click()
     897
     898  >>> print browser.contents
     899  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     900  ...Successfully imported: myfaculties.csv...
     901  ...
     902
     903We just created two new faculties:
     904
     905  >>> browser.open('http://localhost/myuniversity/faculties/')
     906  >>> print browser.contents
     907  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     908  ...<h2>Registered Faculties</h2>...
     909  ...Arts...
     910  ...Sciences...
     911  ...
     912
     913We can also import the department data:
     914
     915  >>> browser.open('http://localhost/myuniversity/datacenter')
     916  >>> browser.getLink('Import CSV data').click()
     917  >>> browser.getControl('Import!', index=3).click()
     918
     919  >>> print browser.contents
     920  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     921  ...Successfully imported: mydepartments.csv...
     922  ...
     923
     924We can also import the course data:
     925
     926  >>> browser.open('http://localhost/myuniversity/datacenter')
     927  >>> browser.getLink('Import CSV data').click()
     928  >>> browser.getControl('Import!', index=2).click()
     929
     930  >>> print browser.contents
     931  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     932  ...Successfully imported: mycourses.csv...
     933  ...
     934
     935We can also import the certificate data:
     936
     937  >>> browser.open('http://localhost/myuniversity/datacenter')
     938  >>> browser.getLink('Import CSV data').click()
     939  >>> browser.getControl('Import!', index=1).click()
     940
     941  >>> print browser.contents
     942  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     943  ...Successfully imported: mycertificates.csv...
     944  ...
     945
     946We can also import certificate courses:
     947
     948  >>> browser.open('http://localhost/myuniversity/datacenter')
     949  >>> browser.getLink('Import CSV data').click()
     950  >>> browser.getControl('Import!', index=0).click()
     951
     952  >>> print browser.contents
     953  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
     954  ...Successfully imported: mycertcourses.csv...
     955  ...
     956
     957Clean up:
     958
     959  >>> import os
     960  >>> import shutil
     961  >>> shutil.rmtree(uploadpath)
  • waeup/trunk/src/waeup/catalog.py

    r3527 r4789  
    1 import sys
    2 import csv
    3 import random
    4 from datetime import datetime
    51import os
    62import grok
     
    84from hurry.query.query import Query, Text
    95from hurry.query import Eq
    10 from app import University
    11 from students.interfaces import IStudent
    12 from students.student import Student
     6from waeup.app import University
     7from waeup.interfaces import ICourse, ICertificateCourse
     8from waeup.student.interfaces import IStudent
     9from zope.app.catalog.interfaces import ICatalog
     10from zope.app.intid.interfaces import IIntIds
     11from zope.component import getUtility
     12from zope.component.interfaces import ComponentLookupError
     13
    1314
    1415class StudentIndexes(grok.Indexes):
     
    2021
    2122
    22 class StudentSearch(grok.View):
    23     grok.context(University)
     23class CourseIndexes(grok.Indexes):
     24     grok.site(University)
     25     grok.name('courses_catalog')
     26     grok.context(ICourse)
    2427
    25     def update(self, query=None):
    26         self.search_result = []
    27         if query is not None:
    28             self.search_result = self.search_context(query)
    29             print "RESULT: ", list(self.search_result)
     28     code = index.Field(attribute='code')
     29     title = index.Text(attribute='title')
    3030
    31     def search_context(self, query):
    32         result = Query().searchResults(
    33             Eq(('students_catalog', 'name'), query)
    34             )
    35         return result
    36    
     31class CourseCertificatesIndexes(grok.Indexes):
     32     grok.site(University)
     33     grok.name('certcourses_catalog')
     34     grok.context(ICertificateCourse)
     35
     36     course_code = index.Field(attribute='getCourseCode')
     37
     38@grok.subscribe(ICourse, grok.IObjectAddedEvent)
     39def handleCourseAdd(obj, event):
     40     """Index an added course with the local catalog.
     41
     42     Courses are not indexed automatically, as they are not a
     43     dictionary subitem of the accompanied site object
     44     (`IUniversity`). I.e. one cannot get them by asking for
     45     ``app['FACCODE']['DEPTCODE']['COURSECODE']`` but one has to ask for
     46     ``app.faculties['FACCODE']['DEPTCODE'].courses['COURSECODE']``.
     47
     48     Once, a course is indexed we can leave the further handling to
     49     the default component architechture. At least removals will
     50     be handled correctly then (and the course unindexed).
     51     """
     52     try:
     53          cat = getUtility(ICatalog, name='courses_catalog')
     54     except ComponentLookupError:
     55          # catalog not available. This might happen during tests.
     56          return
     57     intids = getUtility(IIntIds)
     58     index = cat['code']
     59     index.index_doc(intids.getId(obj), obj)
     60
     61from zope.interface import Interface
     62from zope import schema
     63
     64class IQueryResultItem(Interface):
     65     url = schema.TextLine(
     66          title = u'URL that links to the found item')
     67     title = schema.TextLine(
     68          title = u'Title displayed in search results.')
     69     description = schema.Text(
     70          title = u'Longer description of the item found.')
     71     
     72     
     73class QueryResultItem(object):
     74     grok.implements(IQueryResultItem)
     75     url = None
     76     title = None
     77     description = None
     78     
     79     def __init__(self, context, view):
     80          self.context = context
     81          self.url = view.url(context)
     82          self.title = context.title
     83          self.description = ''
     84     
     85class CourseQueryResultItem(QueryResultItem):
     86     def __init__(self, context, view):
     87          self.context = context
     88          self.url = view.url(context)
     89          self.title = "COURSE: " + context.title
     90          self.description = 'code: %s' % context.code
     91
     92def search(query=None, view=None):
     93     if not query:
     94          return []
     95     cat = getUtility(ICatalog, name='courses_catalog')
     96     results = list(cat.searchResults(code=(query, query)))
     97         
     98     hitlist = []
     99     results = Query().searchResults(
     100          Eq(('courses_catalog', 'code'), query))
     101     for result in results:
     102          hitlist.append(CourseQueryResultItem(result, view=view))
     103
     104     results = Query().searchResults(
     105          Text(('courses_catalog', 'title'), query))
     106         
     107     for result in results:
     108          hitlist.append(CourseQueryResultItem(result, view=view))
     109
     110     return hitlist
     111
     112def search_context(query):
     113     result = Query().searchResults(
     114          Eq(('students_catalog', 'name'), query)
     115          )
     116     return result
  • waeup/trunk/src/waeup/ftesting.zcml

    r3523 r4789  
    1010  <!-- Typical functional testing security setup -->
    1111  <securityPolicy
    12       component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy"
     12      component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
    1313      />
    1414
     
    1717      title="Unauthenticated User"
    1818      />
     19  <unauthenticatedGroup id="zope.Anybody"
     20      title="Unauthenticated Users" />
     21  <authenticatedGroup id="zope.Authenticated"
     22      title="Authenticated Users" />
     23  <everybodyGroup id="zope.Everybody"
     24      title="All Users" />
     25
    1926  <grant
    2027      permission="zope.View"
    21       principal="zope.anybody"
     28      principal="zope.Anybody"
     29      />
     30  <grant
     31      permission="zope.app.dublincore.view"
     32      principal="zope.Anybody" />
     33  <grant
     34      permission="zope.View"
     35      principal="zope.Authenticated"
     36      />
     37  <grant
     38      permission="zope.app.dublincore.view"
     39      principal="zope.Authenticated"
     40      />
     41  <grant
     42      permission="waeup.Public"
     43      principal="zope.Everybody"
    2244      />
    2345
  • waeup/trunk/src/waeup/hostel/hostel.py

    r3531 r4789  
    11import grok
    2 from waeup.baseitem import BaseItem
    3 from interfaces import IHostel
     2from waeup.interfaces import IHostel
    43
    5 
    6 class Hostel(BaseItem):
     4class Hostel(grok.Container):
    75    """This is a hostel record.
    86    """   
     
    1210        super(Hostel, self).__init__(**kw)
    1311        self.name = name
     12
     13grok.global_utility(Hostel, provides=IHostel)
  • waeup/trunk/src/waeup/hostel/hostelcontainer.py

    r3527 r4789  
    1 from waeup.basecontainer import BaseContainer
    2 from hostel import Hostel
     1import grok
     2from zope.component.interfaces import IFactory
     3from zope.interface import implementedBy
     4from waeup.interfaces import IHostelContainer, IHostel
    35
    4 class HostelContainer(BaseContainer):
    5     name = u"Hostels"
    6     childClass = Hostel
     6class HostelContainer(grok.Container):
     7
     8    grok.implements(IHostelContainer)
     9    grok.require('waeup.manageUniversity')
     10
     11    # A simple counter for ids.
     12    next_id = 0L
     13
     14    def addHostel(self, hostel):
     15        if not IHostel.providedBy(hostel):
     16            raise TypeError('HostelContainers contain only IHostel instances')
     17        id = str(self.next_id)
     18        self[id] = hostel
     19        while self.next_id in self.keys():
     20            # Look for next unused int...
     21            self.next_id += 1
     22        return id
     23
     24class HostelContainerFactory(grok.GlobalUtility):
     25    """A factory for faculty containers.
     26    """
     27    grok.implements(IFactory)
     28    grok.name(u'waeup.HostelContainer')
     29    title = u"Create a new hostel container.",
     30    description = u"This factory instantiates new hostel containers."
     31
     32    def __call__(self):
     33        return HostelContainer()
     34
     35    def getInterfaces(self):
     36        return implementedBy(HostelContainer)
  • waeup/trunk/src/waeup/interfaces.py

    r3526 r4789  
    11##
    22## interfaces.py
    3 from zope.interface import Interface
     3from zc.sourcefactory.basic import BasicSourceFactory
     4from zope.component import getUtility
     5from zope.app.catalog.interfaces import ICatalog
     6from zope.interface import Interface, Attribute
    47from zope import schema
    5 
    6 class IUniversity(Interface):
     8from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
     9from waeup.permissions import RoleSource
     10
     11def SimpleWAeUPVocabulary(*terms):
     12    """A well-buildt vocabulary provides terms with a value, token and
     13       title for each term
     14    """
     15    return SimpleVocabulary([
     16            SimpleTerm(value, value, title) for title, value in terms])
     17
     18
     19class CourseSource(BasicSourceFactory):
     20    """A course source delivers all courses inside the portal by looking
     21       up a catalog.
     22    """
     23    catalog = None
     24    def getValues(self):
     25        if self.catalog is None:
     26            self.catalog = getUtility(ICatalog, name='courses_catalog')
     27        return list(self.catalog.searchResults(code=('', 'z*')))
     28       
     29    def getTitle(self, value):
     30        return "%s %s" % (value.code, value.title[:32])
     31
     32
     33class IWAeUPObject(Interface):
     34    """A WAeUP object.
     35    """
     36
     37class IUniversity(IWAeUPObject):
    738    """Representation of a university.
    839    """
     
    1243        required = True,
    1344        )
     45
     46    faculties = Attribute("A container for faculties.")
     47    students = Attribute("A container for students.")
     48    hostels = Attribute("A container for hostels.")
     49   
     50class IWAeUPContainer(IWAeUPObject):
     51    """A container for WAeUP objects.
     52    """
     53
     54class IWAeUPContained(IWAeUPObject):
     55    """An item contained in an IWAeUPContainer.
     56    """
     57   
     58class IStudentContainer(IWAeUPContainer):
     59    """A container for StudentObjects.
     60    """
     61
     62class IFaculty(IWAeUPContainer):
     63    """Representation of a university faculty.
     64    """
     65    title = schema.TextLine(
     66        title = u'Name of Faculty',
     67        default = u'Unnamed',
     68        required = True,
     69        )
     70
     71    title_prefix = schema.TextLine(
     72        title = u'Title prefix',
     73        default = u'faculty',
     74        required = True,
     75        )
     76   
     77    code = schema.TextLine(
     78        title = u'Code',
     79        description = u'Abbreviated code of the faculty',
     80        default = u'NA',
     81        required = True,
     82        )
     83
     84class IFacultyContainer(IWAeUPContainer):
     85    """A container for faculties.
     86    """
     87    def addFaculty(faculty):
     88        """Add an IFactulty object.
     89
     90        Returns the key, under which the object was stored.
     91        """
     92
     93class IHostelContainer(IWAeUPContainer):
     94    """A container for hostels.
     95    """
     96    def addHostel(hostel):
     97        """Add an IHostel object.
     98
     99        Returns the key, under which the object was stored.
     100        """
     101
     102class IHostel(IWAeUPObject):
     103    """Representation of a hostel.
     104    """
     105    name = schema.TextLine(
     106        title = u'Name of Hostel',
     107        default = u'Nobody',
     108        required = True,
     109        )
     110
     111class IDepartment(IWAeUPObject):
     112    """Representation of a department.
     113    """
     114    title = schema.TextLine(
     115        title = u'Name of Department',
     116        default = u'Unnamed',
     117        required = True,
     118        )
     119
     120    title_prefix = schema.TextLine(
     121        title = u'Title prefix',
     122        default = u'department',
     123        required = True,
     124        )
     125   
     126    code = schema.TextLine(
     127        title = u'Code',
     128        default = u'NA',
     129        description = u'Abbreviated code of the department',
     130        required = True,
     131        )
     132
     133    courses = Attribute("A container for courses.")
     134    certificates = Attribute("A container for certificates.")
     135
     136
     137class ICourseContainer(IWAeUPContainer):
     138    """A container for faculties.
     139    """
     140    def addCourse(faculty):
     141        """Add an ICourse object.
     142
     143        Returns the key, under which the object was stored.
     144        """
     145
     146class ICourse(IWAeUPObject):
     147    """Representation of a course.
     148    """
     149    code = schema.TextLine(
     150        title = u'Code',
     151        default = u'NA',
     152        description = u'Abbreviated code of the course',
     153        required = True,
     154        readonly = True,
     155        )
     156
     157    title = schema.TextLine(
     158        title = u'Title of course',
     159        default = u'Unnamed',
     160        required = True,
     161        )
     162
     163    level = schema.TextLine(
     164        title = u'Level',
     165        default = u'100',
     166        required = False,
     167        )
     168
     169    credits = schema.Int(
     170        title = u'Credits',
     171        default = 0,
     172        required = False,
     173        )
     174   
     175    passmark = schema.Int(
     176        title = u'Passmark',
     177        default = 40,
     178        required = False,
     179        )
     180
     181    semester = schema.Choice(
     182        title = u'Semester/Term',
     183        default = 0,
     184        vocabulary = SimpleWAeUPVocabulary(
     185            ('N/A', 0), ('First Semester', 1),
     186            ('Second Semester', 2), ('Combined', 3)),
     187        required = True,
     188        )
     189
     190
     191class ICertificate(IWAeUPObject):
     192    """Representation of a certificate.
     193    """
     194    code = schema.TextLine(
     195        title = u'Code',
     196        default = u'NA',
     197        description = u'Abbreviated code of the certificate.',
     198        required = True,
     199        )
     200
     201    review_state = schema.Choice(
     202        title = u'review state',
     203        values = ['unchecked', 'checked']
     204        )
     205
     206    title = schema.TextLine(
     207        title = u'title',
     208        required = True,
     209        )
     210
     211    category = schema.TextLine(
     212        title = u'category',
     213        required = True,
     214        )
     215
     216    study_mode = schema.TextLine(
     217        title = u'study mode',
     218        required = True,
     219        )
     220
     221    start_level = schema.TextLine(
     222        title = u'start level',
     223        required = True,
     224        )
     225   
     226    end_level = schema.TextLine(
     227        title = u'end level',
     228        required = True,
     229        )
     230   
     231    application_category = schema.TextLine(
     232        title = u'application category',
     233        required = True,
     234        )
     235   
     236    m_prefix = schema.TextLine(
     237        title = u'prefix',
     238        required = True,
     239        )
     240       
     241    max_pass = schema.TextLine(
     242        title = u'maximum pass',
     243        required = True,
     244        )
     245   
     246
     247   
     248class ICertificateContainer(IWAeUPContainer):
     249    """A container for certificates.
     250    """
     251    def addCertificate(faculty):
     252        """Add an ICertificate object.
     253
     254        Returns the key, under which the object was stored.
     255        """
     256
     257class ICertificateCourse(IWAeUPObject):
     258    """A certificatecourse is a course referenced by a certificate, which
     259       provides some own attributes.
     260    """
     261    course = schema.Choice(
     262        title = u'Course',
     263        source = CourseSource(),
     264        )
     265   
     266    level = schema.Int(
     267        title = u'Level of this course',
     268        required = True,
     269        default = 100
     270        )
     271
     272    core_or_elective = schema.Bool(
     273        title = u'Is mandatory course (not elective)',
     274        required = True,
     275        default = True
     276        )
     277
     278    def getCourseCode():
     279        """Return the code of the referenced course.
     280
     281        This is needed for cataloging.
     282        """
     283       
     284class IWAeUPExporter(Interface):
     285    """An exporter for objects.
     286    """
     287    def export(obj, filepath=None):
     288        """Export by pickling.
     289
     290        Returns a file-like object containing a representation of `obj`.
     291
     292        This is done using `pickle`. If `filepath` is ``None``, a
     293        `cStringIO` object is returned, that contains the saved data.
     294        """
     295
     296class IWAeUPXMLExporter(Interface):
     297    """An XML exporter for objects.
     298    """
     299    def export(obj, filepath=None):
     300        """Export as XML.
     301
     302        Returns an XML representation of `obj`.
     303
     304        If `filepath` is ``None``, a StringIO` object is returned,
     305        that contains the transformed data.
     306        """
     307
     308class IWAeUPXMLImporter(Interface):
     309    """An XML import for objects.
     310    """
     311    def doImport(filepath):
     312        """Create Python object from XML.
     313
     314        Returns a Python object.
     315        """
     316
     317class IWAeUPCSVExporter(Interface):
     318    """A CSV exporter for objects.
     319    """
     320
     321    def export(obj, filepath=None):
     322        """Export as CSV.
     323
     324        Returns a CSV representation of `obj`.
     325
     326        If `filepath` is ``None``, a StringIO` object is returned,
     327        that contains the transformed data.
     328        """
     329
     330class IWAeUPCSVImporter(Interface):
     331    """A CSV importer for objects.
     332    """
     333    datatype = schema.TextLine(
     334        title = u'Data type',
     335        description = u'Type of data supported by this filter.',
     336        required = True,)
     337
     338    def doImport(clear_old_data=True, overwrite=True):
     339        """Read data from `filepath` and apply data.
     340
     341        It depends on the context, what 'applying data' means here.
     342
     343        If `clear_old_data` is False, old data will be
     344        preserved. Otherwise all old data will get lost.
     345
     346        If `overwrite` is False, any existing entries with similar
     347        keys might be overwritten. This option is ignored, when
     348        `clear_old_data` is set to True.
     349        """
     350       
     351class IUserAccount(IWAeUPObject):
     352    """A user account.
     353    """
     354    name = schema.TextLine(
     355        title = u'User ID',
     356        description = u'Loginname',
     357        required = True,)
     358    title = schema.TextLine(
     359        title = u'Username',
     360        description = u'Real name',
     361        required = False,)
     362    description = schema.TextLine(
     363        title = u'User description',
     364        required = False,)
     365    password = schema.Password(
     366        title = u'Password',
     367        required = True,)
     368    roles = schema.List(
     369        title = u'Roles',
     370        value_type = schema.Choice(source=RoleSource()))
     371   
     372   
     373class IUserContainer(IWAeUPObject):
     374    """A container for users (principals).
     375
     376    These users are used for authentication purposes.
     377    """
     378
     379    def addUser(name, password, title=None, description=None):
     380        """Add a user.
     381        """
     382
     383    def delUser(name):
     384        """Delete a user if it exists.
     385        """
     386
     387class IDataCenter(IWAeUPObject):
     388    """A data center.
     389
     390    TODO : declare methods, at least those needed by pages.
     391    """
     392    pass
     393
     394class ICSVDataReceivers(Interface):
     395    """An object containing things ready for CSV imports.
     396
     397    This is pure marker interface. Objects that implement it, indicate
     398    that they might provide attributes, which are able to receive CSV
     399    data, i.e. for which an IWAeUPCSVDataImporter exists.
     400    """
     401
     402class IDataCenterFile(Interface):
     403    """A data center file.
     404    """
     405    def getDate():
     406        """Get creation timestamp from file in human readable form.
     407        """
     408
     409    def getSize():
     410        """Get human readable size of file.
     411        """
  • waeup/trunk/src/waeup/permissions.py

    r3521 r4789  
    11import grok
     2from zc.sourcefactory.basic import BasicSourceFactory
    23
     4class Public(grok.Permission):
     5    """Everyone-can-do-this-permission.
     6
     7    This permission is meant to be applied to objects/views/pages
     8    etc., that should be usable/readable by everyone.
     9
     10    We need this to be able to tune default permissions more
     11    restrictive and open up some dedicated objects like the front
     12    page.
     13    """
     14    grok.name('waeup.Public')
     15
     16class ViewPermission(grok.Permission):
     17    grok.name('waeup.View')
     18
     19class ManageUniversity(grok.Permission):
     20    grok.name('waeup.manageUniversity')
     21
     22class ManageUsers(grok.Permission):
     23    grok.name('waeup.manageUsers')
     24   
    325class FacultyRead(grok.Permission):
    426    grok.name('waeup.facultyread')
     27   
     28class PortalUser(grok.Role):
     29    grok.name('waeup.PortalUser')
     30    grok.permissions('waeup.facultyread', 'waeup.View', 'waeup.Public')
    531
     32class PortalManager(grok.Role):
     33    grok.name('waeup.PortalManager')
     34    grok.permissions('waeup.manageUniversity', 'waeup.manageUsers',
     35                     'waeup.View', 'waeup.Public')
     36
     37def getRoles():
     38    app = grok.getSite()
     39    app = None
     40    manager = None
     41    if app is not None:
     42        from zope.securitypolicy.interfaces import IRolePermissionManager
     43        manager = IRolePermissionManager(app, None)
     44    else:
     45        from zope.securitypolicy.rolepermission import (
     46            rolePermissionManager as manager)
     47    role_permission_map =  manager.getRolesAndPermissions()
     48    result = dict()
     49    for item in role_permission_map:
     50        if not item[1].startswith('waeup.'):
     51            # Ignore non-WAeUP roles...
     52            continue
     53        result[item[1]] = True
     54    return sorted(result.keys())
     55
     56class RoleSource(BasicSourceFactory):
     57    def getValues(self):
     58        return getRoles()
     59    def getTitle(self, value):
     60        if isinstance(value, basestring):
     61            return value.split('.', 2)[1]
  • waeup/trunk/src/waeup/static/app.css

    r3530 r4789  
    88}
    99
     10#uni-title {
     11        font-size: 24px;
     12        font-weight: bold;
     13        padding-top: 20px;
     14        position: absolute;
     15}
     16
     17#yui-gen1 {
     18        background-color: #FFEEBB;
     19        border: 3px;
     20}
     21
     22
     23div.yui-layout-bd-nohd {
     24        background-color: #0f0;
     25        border: 0px;
     26}
     27
     28.actionbarlink {
     29        background-color: #feb;
     30        padding: 3px;
     31        text-align: center;
     32}
     33
     34.actionbarlink a{
     35        text-decoration: underline;
     36}
     37
     38.actionbarlink img {
     39        position: absolute;
     40}
     41
     42.actionbarlink span {
     43        padding-left: 20px;
     44}
     45
     46.yui-link-button img {
     47        position: absolute;
     48        padding-top: 5px;
     49}
     50
     51.yui-link-button a span {
     52        padding-left: 20px;
     53}
     54
    1055h1,h2,h3 {
    1156        color: #005500;
    12         font-weight: normal;
     57        font-weight: bold;
     58        font-size: 120%;
     59        border-bottom: 1px solid;
    1360}
    1461
     
    1764}
    1865
    19 a:hover {
     66a:hovers {
    2067        text-decoration: underline;
    2168}
     
    62109        */
    63110        font-size: x-small;
     111        text-align: center;
    64112}
    65113
     
    120168        font-size: small;
    121169}
     170
     171.message {
     172  background-color: #fe0;
     173  color: #000;
     174  text-align: center;
     175  font-weight: bold;
     176  padding: 2px;
     177  padding-right: 10px;
     178  border: 1px dotted #050;
     179}
     180
     181.yui-skin-sam .yui-layout .yui-layout-clip {
     182/* collapsed unit */
     183  background-color: #edc;
     184}
     185
     186.yui-layout, .yui-layout-doc {
     187  background-color: #feb;
     188}
     189
     190.yui-layout-wrap, .yui-layout-bd, .yui-layout-clip {
     191  backgr/ound-color: #feb;
     192}
     193
     194div.yui-layout-bd-nohd {
     195  background-color: #ffc;
     196}
     197
     198.yui-layout-unit {
     199  background-color: #feb;
     200}
     201
     202#alayout-doc {
     203        background-color: #FFEEBB;
     204}
     205
     206#div, .yui-skin-sam .yui-layout .yui-layout-unit div.yui-layout-bd {
     207
     208}
     209
     210.yui-skin-sam .yui-layout .yui-layout-unit div.yui-layout-bd {
     211/* asd */
     212  background-color: #ffc;
     213}
     214
     215#innermain {
     216  padding: 5px;
     217}
     218
     219.yui-skin-sam .yui-resize .yui-resize-handle {
     220  background-color: #feb;
     221}
     222
     223#hd {
     224  padding: 5px;
     225}
     226
     227th, td {
     228  font-family: arial, sans-serif;
     229}
  • waeup/trunk/src/waeup/testing.py

    r3521 r4789  
    55ftesting_zcml = os.path.join(
    66    os.path.dirname(waeup.__file__), 'ftesting.zcml')
    7 FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer')
     7FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
     8                            allow_teardown=True)
  • waeup/trunk/src/waeup/tests/test_waeup.py

    r3521 r4789  
    55"""
    66import grok
     7import zope.component.eventtesting
     8from waeup.testing import FunctionalLayer
     9
     10def setUpZope(test):
     11    zope.component.eventtesting.setUp(test)
     12
    713# Register all tests in the waeup_ng package
    814test_suite = grok.testing.register_all_tests(
    9     'waeup')
     15    'waeup', usetup=setUpZope,
     16    layer=FunctionalLayer)
  • waeup/trunk/src/waeup/university/__init__.py

    r3521 r4789  
     1# Make this a package.
  • waeup/trunk/src/waeup/university/certificate.py

    r3526 r4789  
     1"""WAeUP portal certificates
     2"""
     3import grok
     4from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
     5from zope.app.catalog.interfaces import ICatalog
     6from zope.component import getUtility
     7from zope.component.interfaces import IFactory, ComponentLookupError
     8from zope.interface import implementedBy
     9from waeup.interfaces import ICertificate, ICertificateCourse, ICourse
     10
     11class Certificate(grok.Container):
     12    """A certificate.
     13    """
     14    grok.implements(ICertificate)
     15
     16    def __init__(self, code=u'NA', title=u'Unnamed Certificate',
     17                 category=None, study_mode=None, start_level=None,
     18                 end_level=None, application_category=None, m_prefix=u'',
     19                 max_pass = u''):
     20        super(Certificate, self).__init__()
     21        self.code = code
     22        self.title = title
     23        self.category = category
     24        self.study_mode = study_mode
     25        self.start_level = start_level
     26        self.end_level = end_level
     27        self.application_category = application_category
     28        self.m_prefix = m_prefix
     29        self.max_pass = max_pass
     30        # Initialize workflow state...
     31        self.setReviewState('unchecked')
     32
     33    def check(self):
     34        """Set Review State to ``checked``.
     35        """
     36        self.setReviewState(state='checked')
     37
     38    def getReviewState(self):
     39        return IWorkflowState(self).getState()
     40
     41    def setReviewState(self, state=None):
     42        """Try to set new state.
     43
     44        Instead of simply setting a value, we fire a transition of a
     45        workflow. This might fail if, for instance, the required state
     46        is not part of the workflow or unreachable from current state.
     47        """
     48        if state == self.getReviewState():
     49            # Accept staying in the same state, even if workflow does
     50            # not allow this.
     51            return
     52        info = IWorkflowInfo(self)
     53        info.fireTransitionToward(state)
     54        return
     55
     56    review_state = property(getReviewState, setReviewState)
     57   
     58    def addCourseRef(self, course, level=100, core_or_elective=True):
     59        """Add a course reference.
     60        """
     61        code = "%s_%s" % (course.code, level)
     62        self[code] = CertificateCourse(course, level, core_or_elective)
     63        self[code].__parent__ = self
     64        self[code].__name__ = code
     65        self._p_changed = True
     66
     67    def delCourseRef(self, code, level=None):
     68        """Delete a course denoted by its code.
     69        """
     70        for key in self.keys():
     71            if self[key].getCourseCode() != code:
     72                continue
     73            if level is not None and str(self[key].level) != str(level):
     74                # found a course with correct key but wrong level...
     75                continue
     76            del self[key]
     77            self._p_changed = True
     78        return
     79
     80
     81
     82
     83class CertificateFactory(grok.GlobalUtility):
     84    """A factory for certificates.
     85    """
     86    grok.implements(IFactory)
     87    grok.name(u'waeup.Certificate')
     88    title = u"Create a new certificate.",
     89    description = u"This factory instantiates new certificate instances."
     90
     91    def __call__(self, *args, **kw):
     92        return Certificate(*args, **kw)
     93
     94    def getInterfaces(self):
     95        return implementedBy(Certificate)
     96
     97class CertificateCourse(grok.Model):
     98    grok.implements(ICertificateCourse)
     99
     100    def __init__(self, course=None, level=100, core_or_elective=True):
     101        self.course = course
     102        self.level = level
     103        self.core_or_elective = core_or_elective
     104
     105    def getCourseCode(self):
     106        """Get code of a course.
     107        """
     108        return self.course.code
     109   
     110class CertificateCourseFactory(grok.GlobalUtility):
     111    """A factory for certificate courses.
     112    """
     113    grok.implements(IFactory)
     114    grok.name(u'waeup.CertificateCourse')
     115    title = u"Create a new certificate course.",
     116    description = u"This factory instantiates new certificate courses."
     117
     118    def __call__(self, *args, **kw):
     119        return CertificateCourse(*args, **kw)
     120
     121    def getInterfaces(self):
     122        return implementedBy(CertificateCourse)
     123
     124@grok.subscribe(ICourse, grok.IObjectRemovedEvent)
     125def removedCourseHandler(course, event):
     126    """If a course is deleted, we make sure that also references in a
     127       certificatecontainer are removed.
     128    """
     129    code = course.code
     130
     131    # Find all certcourses that reference the given course...
     132    try:
     133        cat = getUtility(ICatalog, name='certcourses_catalog')
     134    except ComponentLookupError:
     135        # catalog not available. This might happen during tests.
     136        return
     137       
     138    results = cat.searchResults(course_code=(code, code))
     139    for certcourse in results:
     140        # Remove that reference...
     141        cert = certcourse.__parent__
     142        cert.delCourseRef(code)
     143        cert._p_changed = True
     144    return
  • waeup/trunk/src/waeup/university/course.py

    r3526 r4789  
     1"""Courses.
     2"""
     3import grok
     4from zope.interface import implementedBy
     5from zope.component.interfaces import IFactory
     6from waeup.interfaces import ICourse
     7
     8class Course(grok.Model):
     9    """A university course.
     10    """
     11    grok.implements(ICourse)
     12
     13    def __init__(self,
     14                 title=u'Unnamed Course',
     15                 code=u'NA',
     16                 level=None,
     17                 credits=0,
     18                 passmark=40,
     19                 semester=1, **kw):
     20        super(Course, self).__init__(**kw)
     21        self.title = title
     22        self.code = code
     23        self.level = level
     24        self.credits = credits
     25        self.passmark = passmark
     26        self.semester = semester
     27       
     28class CourseFactory(grok.GlobalUtility):
     29    """A factory for courses.
     30    """
     31    grok.implements(IFactory)
     32    grok.name(u'waeup.Course')
     33    title = u"Create a new course.",
     34    description = u"This factory instantiates new course instances."
     35
     36    def __call__(self, *args, **kw):
     37        return Course(*args, **kw)
     38
     39    def getInterfaces(self):
     40        return implementedBy(Course)
  • waeup/trunk/src/waeup/university/department.py

    r3526 r4789  
     1"""University departments.
     2"""
     3import grok
     4from zope.component import createObject
     5from zope.component.interfaces import IFactory
     6from zope.interface import implementedBy
     7from waeup.interfaces import IDepartment
     8
     9class Department(grok.Container):
     10    """A university department.
     11    """
     12    grok.implements(IDepartment)
     13
     14    # A simple counter for ids.
     15    next_id = 0L
     16   
     17    def __init__(self,
     18                 title=u'Unnamed Department',
     19                 title_prefix=u'department',
     20                 code=u"NA", **kw):
     21        super(Department, self).__init__(**kw)
     22        self.title = title
     23        self.title_prefix = title_prefix
     24        self.code = code
     25        self.courses = createObject(u'waeup.CourseContainer')
     26        self.courses.__parent__ = self
     27        self.courses.__name__ = 'courses'
     28        self.certificates = createObject(u'waeup.CertificateContainer')
     29        self.certificates.__parent__ = self
     30        self.certificates.__name__ = 'certificates'
     31
     32    def traverse(self, name):
     33        """Deliver appropriate containers, if someone wants to go to courses
     34        or departments.
     35        """
     36        if name == 'courses':
     37            return self.courses
     38        elif name == 'certificates':
     39            return self.certificates
     40        return None
     41       
     42
     43class DepartmentFactory(grok.GlobalUtility):
     44    """A factory for department containers.
     45    """
     46    grok.implements(IFactory)
     47    grok.name(u'waeup.Department')
     48    title = u"Create a new department.",
     49    description = u"This factory instantiates new department instances."
     50
     51    def __call__(self, *args, **kw):
     52        return Department(*args, **kw)
     53
     54    def getInterfaces(self):
     55        """Get interfaces of objects provided by this factory.
     56        """
     57        return implementedBy(Department)
  • waeup/trunk/src/waeup/university/faculty.py

    r3529 r4789  
     1"""Faculty components.
     2"""
     3
    14import grok
    2 from waeup.basecontainer import BaseContainer
    3 from interfaces import IFaculty
     5from zope.component.interfaces import IFactory
     6from zope.interface import implementedBy
     7from waeup.interfaces import IFaculty, IDepartment
    48
    5 class Faculty(BaseContainer):
     9class Faculty(grok.Container):
     10    """A university faculty.
     11    """
    612    grok.implements(IFaculty)
    713
    8     def __init__(self, name=u'Unnamed Faculty', **kw):
     14    def __init__(self,
     15                 title=u'Unnamed Faculty',
     16                 title_prefix=u'faculty',
     17                 code=u'NA', **kw):
    918        super(Faculty, self).__init__(**kw)
    10         self.name = name
     19        self.title = title
     20        self.title_prefix = title_prefix
     21        self.code = code
    1122
    12 class Index(grok.DisplayForm):
    13     form_fields = grok.AutoFields(IFaculty)
     23    def addDepartment(self, department):
     24        """Add a department to the faculty.
     25        """
     26        if not IDepartment.providedBy(department):
     27            raise TypeError('Faculties contain only IDepartment instances')
     28        self[department.code] = department
    1429
     30    def clear(self):
     31        """Remove all departments from faculty.
     32        """
     33        keys = self.keys()
     34        for key in keys:
     35            del self[key]
    1536
     37    def getName(self, key):
     38        """Get complete faculty name.
     39        """
     40        if key in self.keys():
     41            dept = self[key]
     42            prefix = dept.title_prefix
     43            prefix = prefix[0].upper() + prefix[1:]
     44            return '%s of %s' % (prefix, dept.title)
     45
     46       
     47class FacultyFactory(grok.GlobalUtility):
     48    """A factory for faculty containers.
     49    """
     50    grok.implements(IFactory)
     51    grok.name(u'waeup.Faculty')
     52    title = u"Create a new faculty.",
     53    description = u"This factory instantiates new faculty instances."
     54
     55    def __call__(self, *args, **kw):
     56        return Faculty()
     57
     58    def getInterfaces(self):
     59        return implementedBy(Faculty)
  • waeup/trunk/src/waeup/university/facultycontainer.py

    r3527 r4789  
    11import grok
    2 from waeup.basecontainer import BaseContainer
    3 from interfaces import IFaculty
    4 from faculty import Faculty
     2from zope.component.interfaces import IFactory
     3from zope.interface import implementedBy
     4from waeup.interfaces import IFacultyContainer, IFaculty
    55
    6 class FacultyContainer(BaseContainer):
    7    
    8     name = u'Faculties'
    9     childClass = Faculty
     6class FacultyContainer(grok.Container):
     7    """See interfaces for description.
     8    """
     9    grok.implements(IFacultyContainer)
     10    grok.require('waeup.manageUniversity')
     11
     12    def addFaculty(self, faculty):
     13        if not IFaculty.providedBy(faculty):
     14            raise TypeError('FacultyContainers contain only IFaculty instances')
     15        self[faculty.code] = faculty
     16        return
     17
     18    def clear(self):
     19        keys = self.keys()
     20        for key in keys:
     21            del self[key]
     22
     23    def getName(self, key):
     24        if key in self.keys():
     25            fac = self[key]
     26            prefix = fac.title_prefix
     27            prefix = prefix[0].upper() + prefix[1:]
     28            return '%s of %s' % (prefix, fac.title)
     29           
     30class FacultyContainerFactory(grok.GlobalUtility):
     31    """A factory for faculty containers.
     32    """
     33    grok.implements(IFactory)
     34    grok.name(u'waeup.FacultyContainer')
     35    title = u"Create a new faculty container.",
     36    description = u"This factory instantiates new faculty containers."
     37
     38    def __call__(self):
     39        return FacultyContainer()
     40
     41    def getInterfaces(self):
     42        return implementedBy(FacultyContainer)
  • waeup/trunk/src/waeup/utils/importexport.py

    r3529 r4789  
     1import re
    12import csv
     3import grok
     4import cPickle
     5import zope.xmlpickle
     6from cStringIO import StringIO
     7from xml.dom.minidom import Document, getDOMImplementation
     8from zope.interface import Interface
     9from waeup.csvfile.interfaces import ICSVFile
     10from waeup.interfaces import (IWAeUPObject, IWAeUPExporter, IWAeUPXMLExporter,
     11                              IWAeUPXMLImporter, IWAeUPCSVImporter)
    212
    313def readFile(f):
     
    1424    writer = csv.writer(open("export.csv", "wb"))
    1525    writer.writerows(data)
     26
     27class Exporter(grok.Adapter):
     28    """Export a WAeUP object as pickle.
     29
     30    This all-purpose exporter exports attributes defined in schemata
     31    and contained objects (if the exported thing is a container).
     32    """
     33    grok.context(IWAeUPObject)
     34    grok.provides(IWAeUPExporter)
     35
     36    def __init__(self, context):
     37        self.context = context
     38
     39    def export(self, filepath=None):
     40        if filepath is None:
     41            filelike_obj = StringIO()
     42        else:
     43            filelike_obj = open(filepath, 'wb')
     44        exported_obj = cPickle.dump(self.context, filelike_obj)
     45        filelike_obj.close()
     46        return filelike_obj
     47
     48class XMLExporter(grok.Adapter):
     49    """Export a WAeUP object as XML.
     50
     51    This all-purpose exporter exports XML representations of pickable
     52    objects.
     53    """
     54    grok.context(Interface)
     55    grok.provides(IWAeUPXMLExporter)
     56
     57    def __init__(self, context):
     58        self.context = context
     59   
     60    def export(self, filepath=None):
     61        pickled_obj = cPickle.dumps(self.context)
     62        result = zope.xmlpickle.toxml(pickled_obj)
     63        if filepath is None:
     64            filelike_obj = StringIO()
     65        else:
     66            filelike_obj = open(filepath, 'wb')
     67        filelike_obj.write(result)
     68        filelike_obj.seek(0)
     69        return filelike_obj
     70
     71class XMLImporter(grok.Adapter):
     72    """Import a WAeUP object from XML.
     73    """
     74    grok.context(Interface)
     75    grok.provides(IWAeUPXMLImporter)
     76
     77    def __init__(self, context):
     78        self.context = context
     79   
     80    def doImport(self, filepath):
     81        xml = None
     82        if isinstance(filepath, basestring):
     83            xml = open(filepath, 'rb').read()
     84        else:
     85            xml = filepath.read()
     86        obj = zope.xmlpickle.loads(xml)
     87        return obj
     88
     89
     90class CSVImporter(grok.MultiAdapter):
     91    grok.adapts(ICSVFile, IWAeUPObject)
     92    grok.provides(IWAeUPCSVImporter)
     93    grok.baseclass()  # We don't want this base to be registered really.
     94
     95    def __init__(self, csvfile, receiver):
     96        self.csvfile = csvfile
     97        self.receiver = receiver
     98
     99    def doImport(self):
     100        pass
Note: See TracChangeset for help on using the changeset viewer.