source: main/waeup.kofa/trunk/src/waeup/kofa/accesscodes/browser.txt @ 8685

Last change on this file since 8685 was 8321, checked in by Henrik Bettermann, 13 years ago

We have to store the cost in AccessCode? not only in AccessCodeBatches?. We need this for access code slips in students.

File size: 14.2 KB
Line 
1:mod:`waeup.kofa.accesscodes.browser` -- UI components for access-codes
2***********************************************************************
3
4.. module:: waeup.kofa.accesscodes.browser
5
6Here we visit the access-code related parts of a Kofa site using
7a virtual browser.
8
9:NOTTest-Layer: functional
10
11Preliminaries
12=============
13
14Before we can do anything, we have to create a university site:
15
16    >>> from zope.testbrowser.testing import Browser
17    >>> browser = Browser()
18    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
19    >>> browser.handleErrors = False
20    >>> root = getRootFolder()
21
22    >>> from waeup.kofa.app import University
23    >>> u = University()
24    >>> root['myuniversity'] = u
25
26We set a new datacenter storage path:
27
28  >>> import os
29  >>> browser.open('http://localhost/myuniversity')
30  >>> browser.getLink('Data Center').click()
31  >>> browser.getLink('Edit settings').click()
32  >>> pathsetting = browser.getControl(name='newpath')
33
34  >>> cwd = os.getcwd()
35  >>> uploadpath = os.path.join(cwd, 'ac_testfiles')
36  >>> os.mkdir(uploadpath)
37  >>> pathsetting.value = uploadpath
38  >>> browser.getControl(name='save').click()
39
40We remove any existing 'accesscodes' dir from datacenter dir:
41
42  >>> import shutil
43  >>> if os.path.exists(os.path.join(uploadpath, 'accesscodes')):
44  ...   shutil.rmtree(os.path.join(uploadpath, 'accesscodes'))
45
46
47Access-code management screen
48=============================
49
50For users that have the right to manage access-code related stuff, the
51home page of a `University` instance provides a link to the
52access-code management screen. In the beginning, there are already
53three empty batches available which will be filled by online payments:
54
55    >>> browser.open('http://localhost/myuniversity')
56    >>> browser.getLink('Access Codes').click()
57    >>> print browser.contents
58    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
59    ...
60    ...<h1>Access Code Batches</h1>
61    ...
62    ... The following batches are available:
63    ...CLR...
64    ...HOS...
65    ...SFE...
66    ...
67
68Adding batches
69==============
70
71We can add a batch of access-codes using a button displayed in the
72action bar:
73
74    >>> browser.getLink('Add Access Code Batch').click()
75
76The add screen shows a form where we have to enter a prefix, the
77number of access codes to be generated and the costs for each card.
78
79    >>> browser.getControl(name='form.prefix').value = 'APP'
80    >>> browser.getControl(name='form.entry_num').value = '5'
81    >>> browser.getControl(name='form.cost').value = '12.12'
82
83If we click 'cancel' afterwards, the whole process will be cancelled
84and we'll be redirected to the management screen:
85
86    >>> browser.getControl('Cancel').click()
87    >>> browser.url
88    'http://localhost/myuniversity/accesscodes'
89
90    >>> 'Batch creation cancelled' in browser.contents
91    True
92
93Now let's try again and this time we finish the procedure by clicking
94'Create batch' in the form:
95
96    >>> browser.getLink('Add Access Code Batch').click()
97    >>> browser.getControl(name='form.prefix').value = 'APP'
98    >>> browser.getControl(name='form.entry_num').value = '5'
99    >>> browser.getControl(name='form.cost').value = '12.12'
100    >>> browser.getControl('Create batch').click()
101
102We're also redirected to the management screen, with a notice about
103the freshly created batch:
104
105    >>> print browser.contents
106    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
107    ...
108    ...<h1>Access Code Batches</h1>
109    ...
110    ... The following batches are available:
111    ...APP
112    ...-
113    ...1
114    ...
115    ...5
116    .../
117    ...0
118    ...
119    ...12.12...
120    ...zope.mgr...
121    ...
122
123which means: there exists a batch named ``APP-1`` with 5 entries of
124which 0 have been devalidated, each one costs ``12.12`` and the batch
125was created by ``zope.mgr``.
126
127We create a second batch to see whether searching and related stuff
128works:
129
130    >>> browser.getLink('Add Access Code Batch').click()
131    >>> browser.getControl(name='form.prefix').value = 'APP'
132    >>> browser.getControl(name='form.entry_num').value = '5'
133    >>> browser.getControl(name='form.cost').value = '10.12'
134    >>> browser.getControl('Create batch').click()
135
136And a third one that can be deleted afterwards:
137
138    >>> browser.getLink('Add Access Code Batch').click()
139    >>> browser.getControl(name='form.prefix').value = 'BLA'
140    >>> browser.getControl(name='form.entry_num').value = '3'
141    >>> browser.getControl(name='form.cost').value = '19.12'
142    >>> browser.getControl('Create batch').click()
143
144
145Creating Archive Files
146======================
147
148Once a batch is created, we can archive it. To do so we have to tick
149the respective checkbox and click on 'Archive':
150
151    >>> ctrl = browser.getControl(name='batches')
152    >>> ctrl.getControl(value='APP-2').selected = True
153    >>> browser.getControl(name='archive').click()
154    >>> print browser.contents
155    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
156    ...
157    <div ...>Archived APP-2 (APP-2_archive-...-zope.mgr.csv)</div>
158    ...
159
160If we do not select a batch and try to archive or delete, the system
161will complain:
162
163    >>> browser.getControl(name='archive').click()
164    >>> print browser.contents
165    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
166    ...
167    <div ...>No batch selected.</div>
168    ...
169
170Deleting Batches
171================
172
173We can delete batches. They are automatically archived when doing so:
174
175    >>> ctrl = browser.getControl(name='batches')
176    >>> ctrl.getControl(value='BLA-1').selected = True
177    >>> browser.getControl('Archive and delete').click()
178    >>> print browser.contents
179    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
180    ...Archived BLA-1 (BLA-1_archive-...-zope.mgr.csv)...
181    ...Deleted batch BLA-1...
182    ...
183
184
185Reimporting Batches
186===================
187
188We can reimport batches using the log files written when a batch was
189created before. So one can reimport the freshly deleted BLA-1
190batch.
191
192To do so we must copy the logfile into the ``imports`` dir of
193accesscodes inside the university's datacenter storage. Otherwisae the
194list of importable files is empty:
195
196    >>> browser.getLink('Reimport Access Code Batch').click()
197    >>> print browser.contents
198    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
199    ...
200    ...No import batches available...
201    ...
202
203We can cancel that operation:
204
205    >>> browser.getControl('Cancel').click()
206
207We copy the ``BLA-1`` archive batch file over to the ``imports`` directory:
208
209    >>> ac_storage = os.path.join(uploadpath, 'accesscodes')
210    >>> logfile2 = os.path.join(ac_storage,
211    ...                         sorted(os.listdir(ac_storage))[-2])
212    >>> filename = os.path.basename(logfile2)
213    >>> filename
214    'BLA-1_archive...-zope.mgr.csv'
215
216    >>> import shutil
217    >>> import_path = os.path.join(ac_storage, 'imports')
218    >>> shutil.copy(logfile2, import_path)
219
220Now the file will be presented as import source:
221
222    >>> browser.getLink('Reimport Access Code Batch').click()
223    >>> filename in browser.contents
224    True
225
226If we do not tick a filename to import and click 'Reimport', we will
227be warned:
228
229    >>> browser.getControl('Reimport').click()
230    >>> print browser.contents
231    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
232    ...No file chosen. Action cancelled...
233
234Now let's really reimport the batch:
235
236    >>> browser.getLink('Reimport Access Code Batch').click()
237    >>> ctrl = browser.getControl(name='filenames')
238    >>> ctrl.getControl(value=filename).selected = True
239    >>> browser.getControl('Reimport').click()
240
241The batch does exist now again:
242
243    >>> print browser.contents
244    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
245    ...Successfully reimported: BLA-1_archive...-zope.mgr.csv...
246    ...
247
248If we try to reimport an existing batch, that won't work:
249
250    >>> browser.getLink('Reimport Access Code Batch').click()
251    >>> ctrl = browser.getControl(name='filenames')
252    >>> ctrl.getControl(value=filename).selected = True
253    >>> browser.getControl('Reimport').click()
254    >>> print browser.contents
255    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
256    ...This batch already exists: BLA-1_archive...-zope.mgr.csv...
257    ...
258
259Searching Access Codes
260======================
261
262We can search for access codes already created.
263
264First we pick some really existing access codes to look for:
265
266    >>> codes = getRootFolder()['myuniversity']['accesscodes']
267    >>> app_1_codes = codes[u'APP-1']
268    >>> app_2_codes = codes[u'APP-2']
269    >>> bla_1_codes = codes[u'BLA-1']
270
271    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
272    >>> ctrl = browser.getControl(name='searchtype')
273    >>> ctrl.getControl(value='code').selected = True
274    >>> browser.getControl(name='searchterm').value = app_1_codes.keys()[0]
275    >>> browser.getControl('Search').click()
276
277The first access code in the ``APP-1`` batch is displayed:
278
279    >>> print browser.contents
280    <!DOCTYPE html ...
281    ...
282      value="APP-1..." /></td>
283      <td>...</td>
284      <td>APP-1-...</td>
285      <td>initialized</td>
286      <td>... - initialized by Manager</td>
287    ...
288
289We can also search for batch serials (the number of an access code
290inside its batch). Looking for number ``1`` will display first access
291code of each batch ('APP-1', 'APP-2', and 'BLA-1')
292
293    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
294    >>> ctrl = browser.getControl(name='searchtype')
295    >>> ctrl.getControl(value='batch_serial').selected = True
296    >>> browser.getControl(name='searchterm').value = '1'
297    >>> browser.getControl('Search').click()
298    >>> print browser.contents
299    <!DOCTYPE html ...
300    ...
301      value="APP-1..." /></td>
302      <td>1</td>
303      <td>APP-1-...</td>
304      <td>initialized</td>
305      <td>... - initialized by Manager</td>
306    ...
307
308Searching for non-integer values does not result in an exception:
309
310    >>> ctrl = browser.getControl(name='searchtype')
311    >>> ctrl.getControl(value='batch_serial').selected = True
312    >>> browser.getControl(name='searchterm').value = 'xyz'
313    >>> browser.getControl('Search').click()
314    >>> print browser.contents
315    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
316    ...
317
318And we can search for text in history messages of access
319codes. Looking for the string ``initialized`` we get nearly all
320entries:
321
322    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
323    >>> ctrl = browser.getControl(name='searchtype')
324    >>> ctrl.getControl(value='history').selected = True
325    >>> browser.getControl(name='searchterm').value = 'initialized'
326    >>> browser.getControl('Search').click()
327    >>> print browser.contents
328    <!DOCTYPE html ...
329    ...
330      value="APP-1-<10-DIGITS>" /></td>
331      <td>1</td>
332      <td>APP-1-<10-DIGITS></td>
333      <td>initialized</td>
334      <td>... - initialized by Manager</td>
335    ...
336
337Enabling and Disabling Found Access Codes
338-----------------------------------------
339
340If a search is successfull, we can enable or disable the found access
341codes:
342
343    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
344    >>> ctrl = browser.getControl(name='searchtype')
345    >>> ctrl.getControl(value='history').selected = True
346    >>> browser.getControl(name='searchterm').value = 'initialized'
347    >>> browser.getControl('Search').click()
348
349This lists all access codes. We now tick one to disable it and click
350on the appropriate button:
351
352    >>> entries = browser.getControl(name='entries')
353    >>> entries.controls[0].selected = True
354    >>> browser.getControl('Disable ACs').click()
355    >>> print browser.contents
356    <!DOCTYPE html ...
357    ...APP-1-<10-DIGITS> disabled.</div>
358    ...
359
360We cannot disable the same entry again:
361
362    >>> entries = browser.getControl(name='entries')
363    >>> entries.controls[0].selected = True
364    >>> browser.getControl('Disable ACs').click()
365    >>> print browser.contents
366    <!DOCTYPE html ...
367    <div ...>APP-1-<10-DIGITS>: Disable transition not allowed.</div>
368    ...
369
370But we can reenable the same entry:
371
372    >>> entries = browser.getControl(name='entries')
373    >>> entries.controls[0].selected = True
374    >>> browser.getControl('Enable ACs').click()
375    >>> print browser.contents
376    <!DOCTYPE html ...
377    ...APP-1-<10-DIGITS> (re-)enabled.</div>
378    ...
379
380Enabling already enabled items gives a warning:
381
382    >>> entries = browser.getControl(name='entries')
383    >>> entries.controls[0].selected = True
384    >>> browser.getControl('Enable ACs').click()
385    >>> print browser.contents
386    <!DOCTYPE html ...
387    <div ...>APP-1-<10-DIGITS>: Re-enable transition not allowed.</div>
388    ...
389
390Log- and Archive Files
391======================
392
393We store log- and archive-files inside the storage managed by the
394local datacenter. Access-code related files are placed inside an
395``accesscode`` subdir:
396
397    >>> ac_storage = os.path.join(uploadpath, 'accesscodes')
398
399Log files for access-code batches
400---------------------------------
401
402Whenever a batch is created, there is also a log file with all entries
403created:
404
405    >>> sorted(os.listdir(ac_storage))
406    ['APP-1-...-zope.mgr.csv', 'APP-2-...-zope.mgr.csv', ...]
407
408Each logfile name contains the prefix, batch number, date of creation
409and userid of creator.
410
411    >>> logfile1 = os.path.join(ac_storage,
412    ...                         sorted(os.listdir(ac_storage))[0])
413    >>> print open(logfile1, 'rb').read()
414    "serial","ac","cost"
415    "APP","1","12.12"
416    "0","APP-1-<10-DIGITS>"
417    "1","APP-1-<10-DIGITS>"
418    "2","APP-1-<10-DIGITS>"
419    "3","APP-1-<10-DIGITS>"
420    "4","APP-1-<10-DIGITS>"
421
422
423
424Archive files
425-------------
426
427We created an archive file above. An archive file name consists of the
428batch prefix, batch number, the string ``_archive``, creation datetime of
429the archive file and userid of batch creator:
430
431    >>> sorted(os.listdir(ac_storage))
432    [..., 'BLA-1_archive-...-zope.mgr.csv', 'imports']
433
434Archive files eventually also contain infos about invalidation dates
435and have a slightly different format therefore.
436
437    >>> archive_file = os.path.join(ac_storage,
438    ...                             sorted(os.listdir(ac_storage))[-2])
439    >>> print open(archive_file, 'rb').read()
440    "prefix","serial","ac","state","history","cost"
441    "BLA","19.12","1","3"
442    "BLA","0","BLA-1-<10-DIGITS>","initialized","...","19.12"
443    "BLA","1","BLA-1-<10-DIGITS>","initialized","...","19.12"
444    "BLA","2","BLA-1-<10-DIGITS>","initialized","...","19.12"
445
446Clean up:
447
448    >>> shutil.rmtree(uploadpath)
Note: See TracBrowser for help on using the repository browser.