source: main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser.txt @ 11031

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

Catch ValueError? exception.

File size: 14.1 KB
Line 
1:mod:`waeup.sirp.accesscodes.browser` -- UI components for access-codes
2***********************************************************************
3
4.. module:: waeup.sirp.accesscodes.browser
5
6Here we visit the access-code related parts of a SIRP 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.sirp.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"
441    "BLA","19.12","1","3"
442    "BLA","0","BLA-1-<10-DIGITS>","initialized","..."
443    "BLA","1","BLA-1-<10-DIGITS>","initialized","..."
444    "BLA","2","BLA-1-<10-DIGITS>","initialized","..."
445
446Clean up:
447
448    >>> shutil.rmtree(uploadpath)
Note: See TracBrowser for help on using the repository browser.