source: main/waeup.kofa/trunk/src/waeup/kofa/doctests/batchprocessing_browser.txt @ 13235

Last change on this file since 13235 was 12981, checked in by Henrik Bettermann, 9 years ago

Remove quite old bug in doImport: Replace empty strings *and* lists with
ignore-markers in update *and* create mode.

File size: 20.0 KB
RevLine 
[12920]1Batch Processing via Browser
[4857]2****************************
3
[4869]4Preliminaries:
5
6We define a function that looks up a form with several submit buttons
7for the one with a given value (this functionality is missing in
8zope.testbrowser):
9
10    >>> def lookup_submit_value(name, value, browser):
11    ...   """Find a button with a certain value."""
12    ...   for num in range(0, 100):
13    ...     try:
14    ...       button = browser.getControl(name=name, index=num)
[4899]15    ...       if button.value.endswith(value):
[4869]16    ...         return button
17    ...     except IndexError:
18    ...       break
19    ...   return None
20
[4857]21Create a site:
22
[7811]23    >>> from waeup.kofa.app import University
[4857]24    >>> getRootFolder()['app'] = University()
[9312]25    >>> from zope.component.hooks import setSite
26    >>> setSite(getRootFolder()['app'])
[4857]27
28Create a datacenter storage path:
29
30    >>> import os
31    >>> import tempfile
32    >>> dc_path = tempfile.mkdtemp()
33
34Log in:
35
36    >>> from zope.testbrowser.testing import Browser
37    >>> browser = Browser()
38    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
39    >>> browser.handleErrors = False
40
41Set datacenter path and deselect moving old data:
42
43    >>> browser.open('http://localhost/app')
44    >>> browser.getLink('Data Center').click()
45    >>> browser.getLink('Edit settings').click()
46    >>> browser.getControl(name='newpath').value = dc_path
47    >>> browser.getControl(name='move').value = False
48    >>> browser.getControl(name='save').click()
49
[6611]50Set non-usable datacenter path:
51
52    >>> browser.getLink('Edit settings').click()
53    >>> browser.getControl(name='newpath').value = '/'
54    >>> browser.getControl(name='save').click()
55    >>> 'Given storage path cannot be used.' in browser.contents
56    True
[11254]57    >>> browser.getControl('Back to Data Center').click()
[6611]58
59
[12920]60Batch Processing Faculties
[4857]61==========================
62
63Go to datacenter page:
64
65    >>> browser.open('http://localhost/app/datacenter')
66
[9919]67Prepare a CSV file for faculties (extended ascii values are accepted):
[4857]68
69    >>> open('faculties.csv', 'wb').write(
[6823]70    ... """code,title,title_prefix
71    ... FAC1,Faculty 1,faculty
72    ... FAC2,Faculty 2,institute
[9919]73    ... FAC3,Fäcülty 3,school
[4857]74    ... """)
75
76Upload the file:
77
78    >>> import cStringIO
[9024]79    >>> browser.getLink('Upload data').click()
[4857]80    >>> filecontents = cStringIO.StringIO(
81    ...   open('faculties.csv', 'rb').read())
82    >>> filewidget = browser.getControl(name='uploadfile:file')
83    >>> filewidget.add_file(filecontents, 'text/plain', 'faculties.csv')
84    >>> browser.getControl(name='SUBMIT').click()
85
86Step 1: start batch processing:
87
[9024]88    >>> browser.getLink('Process data').click()
[4869]89    >>> button = lookup_submit_value(
90    ...   'select', 'faculties_zope.mgr.csv', browser)
91    >>> button.click()
[4857]92
93Step 2: select a processor and mode:
94
95    >>> importerselect = browser.getControl(name='importer')
96    >>> importerselect.displayOptions
[12439]97    ['AccessCodeBatch Processor',
98    'AccessCode Processor',
99    'Applicant Processor',
100    'ApplicantsContainer Processor',
101    'CertificateCourse Processor',
102    'Certificate Processor',
[9420]103    'Course Processor',
[9418]104    'CourseTicket Processor',
[12439]105    'Department Processor',
106    'Faculty Processor',
[9203]107    'Hostel Processor',
[12439]108    'Public HTML Document Processor',
109    'StudentOnlinePayment Processor',
110    'Public PDF Document Processor',
111    'Public REST Document Processor',
112    'Student Processor',
[7954]113    'StudentStudyCourse Processor (update only)',
[8973]114    'StudentStudyLevel Processor',
115    'User Processor',
[9418]116    'Verdict Processor (special processor, update only)']
[4857]117
[7933]118    >>> importerselect.getControl('Faculty Processor').selected = True
[4857]119
120    >>> modeselect = browser.getControl(name='mode')
121    >>> modeselect.options
122    ['create', 'update', 'remove']
123
124    >>> modeselect.getControl(value='create').selected = True
[7705]125    >>> browser.getControl('Proceed to step 3').click()
[4857]126
127Step 3: Fix headerlines
128
129We get informed that there are no problems with the current header:
130
131    >>> print browser.contents
132    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
133    ...
134    Header fields OK
135    ...
136
137The submit button is enabled:
138
[7705]139    >>> browser.getControl('Perform import').disabled
[4857]140    False
141
[7705]142    >>> browser.getControl('Perform import').click()
[4857]143
144Step 4: See import results
145
146The import was successful:
147
148    >>> print browser.contents
149    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
150    ...Successfully processed 3 rows...
151    ...Batch processing finished...
152    ...File:...faculties_zope.mgr.csv...
153
[7749]154We can grep the entries generated in logfile:
[4857]155
[10099]156    >>> browser.open('http://localhost/app/datacenter/logs')
157    >>> browser.getControl('Show', index=0).click()
[4857]158    >>> print browser.contents
159    <!DOCTYPE ...
[10207]160    ...<h1 class="kofa-content-label">Logfile datacenter.log</h1>...
[4857]161
[7749]162    >>> browser.getControl(name='query').value = "zope.mgr"
163    >>> browser.getControl('Search').click()
[9739]164    >>> 'zope.mgr - processed' in browser.contents
[7749]165    True
[4857]166
[7749]167
[12920]168Batch Processing Departments
[4857]169============================
170
171    >>> browser.open('http://localhost/app/datacenter')
172
173Prepare a CSV file for departments:
174
175    >>> open('departments.csv', 'wb').write(
[6823]176    ... """code,faculty_code,title,title_prefix
177    ... DEP1,FAC1,Department 1,department
178    ... DEP2,FAC2,Department 2,centre
[4857]179    ... """)
180
181Upload the file:
182
183    >>> import cStringIO
[9024]184    >>> browser.getLink('Upload data').click()
[4857]185    >>> filecontents = cStringIO.StringIO(
186    ...   open('departments.csv', 'rb').read())
187    >>> filewidget = browser.getControl(name='uploadfile:file')
188    >>> filewidget.add_file(filecontents, 'text/plain', 'departments.csv')
189    >>> browser.getControl(name='SUBMIT').click()
190
191Step 1: start batch processing:
192
[9024]193    >>> browser.getLink('Process data').click()
[4869]194    >>> button = lookup_submit_value(
195    ...   'select', 'departments_zope.mgr.csv', browser)
196    >>> button.click()
[4857]197
198Step 2: select a processor and mode:
199
200    >>> importerselect = browser.getControl(name='importer')
[7933]201    >>> importerselect.getControl('Department Processor').selected = True
[4857]202    >>> modeselect = browser.getControl(name='mode')
203    >>> modeselect.getControl(value='create').selected = True
[7705]204    >>> browser.getControl('Proceed to step 3').click()
[4857]205
206Step 3: Fix headerlines
207
208We get informed that there are no problems with the current header:
209
210    >>> print browser.contents
211    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
212    ...
213    Header fields OK
214    ...
215
216The submit button is enabled:
217
[7705]218    >>> browser.getControl('Perform import').disabled
[4857]219    False
220
[7705]221    >>> browser.getControl('Perform import').click()
[4857]222
223Step 4: See import results
224
225The import was successful:
226
227    >>> print browser.contents
228    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
229    ...Successfully processed 2 rows...
230    ...Batch processing finished...
231    ...File:...departments_zope.mgr.csv...
232
[12920]233Batch Processing Courses
[4857]234========================
235
236    >>> browser.open('http://localhost/app/datacenter')
237
238Prepare a CSV file for courses:
239
240    >>> open('courses.csv', 'wb').write(
241    ... """code,faculty_code,department_code,title,level,passmark,credits,semester
242    ... CRS1,FAC1,DEP1,Course 1,100,40,2,1
243    ... CRS2,FAC1,DEP1,Course 2,100,40,2,2
244    ... """)
245
246Upload the file:
247
248    >>> import cStringIO
[9024]249    >>> browser.getLink('Upload data').click()
[4857]250    >>> filecontents = cStringIO.StringIO(
251    ...   open('courses.csv', 'rb').read())
252    >>> filewidget = browser.getControl(name='uploadfile:file')
253    >>> filewidget.add_file(filecontents, 'text/plain', 'courses.csv')
254    >>> browser.getControl(name='SUBMIT').click()
255
256Step 1: start batch processing:
257
[9024]258    >>> browser.getLink('Process data').click()
[4869]259    >>> button = lookup_submit_value(
260    ...   'select', 'courses_zope.mgr.csv', browser)
261    >>> button.click()
[4857]262
263Step 2: select a processor and mode:
264
265    >>> importerselect = browser.getControl(name='importer')
[7954]266    >>> importerselect.getControl('Course Processor', index=1).selected = True
[4857]267    >>> modeselect = browser.getControl(name='mode')
268    >>> modeselect.getControl(value='create').selected = True
[7705]269    >>> browser.getControl('Proceed to step 3').click()
[4857]270
271Step 3: Fix headerlines
272
273We get informed that there are no problems with the current header:
274
275    >>> print browser.contents
276    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
277    ...
278    Header fields OK
279    ...
280
281The submit button is enabled:
282
[7705]283    >>> browser.getControl('Perform import').disabled
[4857]284    False
285
[7705]286    >>> browser.getControl('Perform import').click()
[4857]287
288Step 4: See import results
289
290The import was successful:
291
292    >>> print browser.contents
293    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
294    ...Successfully processed 2 rows...
295    ...Batch processing finished...
296    ...File:...courses_zope.mgr.csv...
297
[12920]298Batch Processing Certificates
[4857]299=============================
300
301    >>> browser.open('http://localhost/app/datacenter')
302
303Prepare a CSV file for certificates:
304
305    >>> open('certificates.csv', 'wb').write(
[5947]306    ... """code,faculty_code,department_code,title,study_mode,start_level,end_level,application_category
[8472]307    ... CERT1,FAC1,DEP1,Certificate 1,pg_ft,999,999,basic
[5986]308    ... CERT2,FAC1,DEP1,Certificate 2,ug_ft,200,300,cest
[4857]309    ... """)
310
311Upload the file:
312
313    >>> import cStringIO
[9024]314    >>> browser.getLink('Upload data').click()
[4857]315    >>> filecontents = cStringIO.StringIO(
316    ...   open('certificates.csv', 'rb').read())
317    >>> filewidget = browser.getControl(name='uploadfile:file')
318    >>> filewidget.add_file(filecontents, 'text/plain', 'certificates.csv')
319    >>> browser.getControl(name='SUBMIT').click()
320
321Step 1: start batch processing:
322
[9024]323    >>> browser.getLink('Process data').click()
[4869]324    >>> button = lookup_submit_value(
325    ...   'select', 'certificates_zope.mgr.csv', browser)
326    >>> button.click()
[4857]327
[4869]328
[4857]329Step 2: select a processor and mode:
330
331    >>> importerselect = browser.getControl(name='importer')
[7933]332    >>> importerselect.getControl('Certificate Processor').selected = True
[4857]333    >>> modeselect = browser.getControl(name='mode')
334    >>> modeselect.getControl(value='create').selected = True
[7705]335    >>> browser.getControl('Proceed to step 3').click()
[4857]336
337Step 3: Fix headerlines
338
339We get informed that there are no problems with the current header:
340
341    >>> print browser.contents
342    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
343    ...
344    Header fields OK
345    ...
346
347The submit button is enabled:
348
[7705]349    >>> browser.getControl('Perform import').disabled
[4857]350    False
351
[7705]352    >>> browser.getControl('Perform import').click()
[4857]353
354Step 4: See import results
355
356The import was successful:
357
358    >>> print browser.contents
359    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
360    ...Successfully processed 2 rows...
361    ...Batch processing finished...
362    ...File:...certificates_zope.mgr.csv...
363
[12920]364Batch Processing Certificate Courses
[4857]365====================================
366
367    >>> browser.open('http://localhost/app/datacenter')
368
369Prepare a CSV file for certificate courses:
370
371    >>> open('mycertcourses.csv', 'wb').write(
[7665]372    ... """course,faculty_code,department_code,certificate_code,level,mandatory
[4857]373    ... CRS1,FAC1,DEP1,CERT1,100,True
374    ... CRS2,FAC1,DEP1,CERT1,100,True
375    ... """)
376
377Upload the file:
378
379    >>> import cStringIO
[9024]380    >>> browser.getLink('Upload data').click()
[4857]381    >>> filecontents = cStringIO.StringIO(
382    ...   open('mycertcourses.csv', 'rb').read())
383    >>> filewidget = browser.getControl(name='uploadfile:file')
384    >>> filewidget.add_file(filecontents, 'text/plain', 'mycertcourses.csv')
385    >>> browser.getControl(name='SUBMIT').click()
386
387Step 1: start batch processing:
388
[9024]389    >>> browser.getLink('Process data').click()
[4869]390    >>> button = lookup_submit_value(
391    ...   'select', 'mycertcourses_zope.mgr.csv', browser)
392    >>> button.click()
[4857]393
394Step 2: select a processor and mode:
395
396    >>> importerselect = browser.getControl(name='importer')
[7933]397    >>> importerselect.getControl('CertificateCourse Processor').selected = True
[4857]398    >>> modeselect = browser.getControl(name='mode')
399    >>> modeselect.getControl(value='create').selected = True
[7705]400    >>> browser.getControl('Proceed to step 3').click()
[4857]401
402Step 3: Fix headerlines
403
404We get informed that there are no problems with the current header:
405
406    >>> print browser.contents
407    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
408    ...
409    Header fields OK
410    ...
411
412The submit button is enabled:
413
[7705]414    >>> browser.getControl('Perform import').disabled
[4857]415    False
416
[7705]417    >>> browser.getControl('Perform import').click()
[4857]418
419Step 4: See import results
420
421The import was successful:
422
423    >>> print browser.contents
424    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
425    ...Successfully processed 2 rows...
426    ...Batch processing finished...
427    ...File:...mycertcourses_zope.mgr.csv...
428
[12920]429Batch Processing Users
[8973]430======================
[4857]431
[8973]432    >>> browser.open('http://localhost/app/datacenter')
433
[9203]434Prepare a CSV file for users:
[8973]435
436    >>> open('users.csv', 'wb').write(
[8976]437    ... """name,title,public_name,email,phone,roles
[9310]438    ... uli,Uli Fouquet,Chief Developer,uli@abc.de,+49-234-567,[]
439    ... henrik,Henrik Bettermann,Admin,henrik@abc.de,+49-234-567,"['waeup.PortalManager', 'waeup.ImportManager']"
[12190]440    ... anne,Anne Palina,,anne@abc.de,+49-234-567,"['waeup.Nonsense']"
[8973]441    ... """)
442
443Upload the file:
444
445    >>> import cStringIO
[9024]446    >>> browser.getLink('Upload data').click()
[8973]447    >>> filecontents = cStringIO.StringIO(
448    ...   open('users.csv', 'rb').read())
449    >>> filewidget = browser.getControl(name='uploadfile:file')
450    >>> filewidget.add_file(filecontents, 'text/plain', 'users.csv')
451    >>> browser.getControl(name='SUBMIT').click()
452
453Step 1: start batch processing:
454
[9024]455    >>> browser.getLink('Process data').click()
[8973]456    >>> button = lookup_submit_value(
457    ...   'select', 'users_zope.mgr.csv', browser)
458    >>> button.click()
459
460Step 2: select a processor and mode:
461
462    >>> importerselect = browser.getControl(name='importer')
463    >>> importerselect.getControl('User Processor').selected = True
464    >>> modeselect = browser.getControl(name='mode')
465    >>> modeselect.getControl(value='create').selected = True
466    >>> browser.getControl('Proceed to step 3').click()
467
468Step 3: Fix headerlines
469
470We get informed that there are no problems with the current header:
471
472    >>> print browser.contents
473    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
474    ...
475    Header fields OK
476    ...
477
478The submit button is enabled:
479
480    >>> browser.getControl('Perform import').disabled
481    False
482
483    >>> browser.getControl('Perform import').click()
484
485Step 4: See import results
486
487The import was successful:
488
489    >>> print browser.contents
490    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
491    ...Successfully processed 2 rows...
492    ...Batch processing finished...
493    ...File:...users_zope.mgr.csv...
494
[9310]495User henrik has got the global roles:
496
497   >>> henrik = getRootFolder()['app']['users']['henrik']
498   >>> henrik.roles
[9312]499   ['waeup.PortalManager', 'waeup.AcademicsOfficer', 'waeup.ImportManager']
[9310]500
[12920]501Pending Files
[4899]502=============
503
504When an error occurs during an import, two files are generated: a CSV
505file with finished files and a CSV file with pending data. Both are
506stored in the appropriate subdirectories in datacenter. We try to
507create faculties, from which one already exists.
508
509Go to datacenter page:
510
511    >>> browser.open('http://localhost/app/datacenter')
512
513Prepare a CSV file for faculties:
514
515    >>> open('newfaculties.csv', 'wb').write(
[6823]516    ... """code,title,title_prefix
517    ... FAC1,Faculty 1,faculty
518    ... FAC4,Faculty 4,school
[12191]519    ... FAC 5,Faculty 5,faculty
[4899]520    ... """)
521
522Upload the file:
523
524    >>> import cStringIO
[9024]525    >>> browser.getLink('Upload data').click()
[4899]526    >>> filecontents = cStringIO.StringIO(
527    ...   open('newfaculties.csv', 'rb').read())
528    >>> filewidget = browser.getControl(name='uploadfile:file')
529    >>> filewidget.add_file(filecontents, 'text/plain', 'newfaculties.csv')
530    >>> browser.getControl(name='SUBMIT').click()
531
[9310]532Since we now have a user with waeup.ImportManager role, an email has been sent:
533    >>> print browser.contents
534    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
535    ...
536    ...All import managers have been notified by email...
537    ...
538
[4899]539Step 1: start batch processing:
540
[9024]541    >>> browser.getLink('Process data').click()
[4899]542    >>> button = lookup_submit_value(
543    ...   'select', 'newfaculties_zope.mgr.csv', browser)
544    >>> button.click()
545
546Step 2: select a processor and mode:
547
548    >>> importerselect = browser.getControl(name='importer')
[7933]549    >>> importerselect.getControl('Faculty Processor').selected = True
[4899]550    >>> modeselect = browser.getControl(name='mode')
551    >>> modeselect.getControl(value='create').selected = True
[7705]552    >>> browser.getControl('Proceed to step 3').click()
[4899]553
554Step 3: Fix headerlines
555
556As there should be no problem with the headers, we can immediately
557perfom the import:
558
[7705]559    >>> browser.getControl('Perform import').click()
[4899]560
[12191]561Two lines could not be imported:
[4899]562
563    >>> print browser.contents
564    <!DOCTYPE html PUBLIC...
565    ...
[12191]566    ...Processing of 2 rows failed...
[4899]567    ...Successfully processed 1 rows...
568    ...
569
570Now there are two files as a result in datacenter storage's root and
571``finished`` dirs:
572
[4998]573    >>> pending_file = dc_path + '/newfaculties_zope.mgr.create.pending.csv'
[4899]574    >>> print open(pending_file).read()
[6824]575    title_prefix,code,title,--ERRORS--
[12868]576    faculty,FAC1,Faculty 1,This object already exists.
[12415]577    faculty,FAC 5,Faculty 5,code: Invalid input
[4899]578
[4998]579    >>> finished_file = dc_path + '/finished/newfaculties_zope.mgr.create.finished.csv'
[4899]580    >>> print open(finished_file).read()
[6824]581    title_prefix,code,title
[6823]582    school,FAC4,Faculty 4
[4899]583
584The finished-file contains the dataset we could import, while the
585pending file contains the dataset that failed, appended by an error
586message.
587
588
[12920]589Fixing the Pending File
[4981]590-----------------------
591
592We 'edit' the pending file (setting code to ``FAC5`` and title
593appropriately, and removing the --ERROR-- column) and finish the
594import this way:
595
[4998]596    >>> open(dc_path + '/newfaculties_zope.mgr.create.pending.csv', 'wb').write(
[4981]597    ... """title_prefix,--IGNORE--,code,title
[12191]598    ... faculty,,FAC5,Faculty 5
[4981]599    ... """)
600
601Step 1: start batch processing:
602
603    >>> browser.open('http://localhost/app/datacenter')
[9024]604    >>> browser.getLink('Process data').click()
[4981]605    >>> button = lookup_submit_value(
[4998]606    ...   'select', 'newfaculties_zope.mgr.create.pending.csv', browser)
[4981]607    >>> button.click()
608
609Step 2: select a processor and mode:
610
611    >>> importerselect = browser.getControl(name='importer')
[7933]612    >>> importerselect.getControl('Faculty Processor').selected = True
[4981]613    >>> modeselect = browser.getControl(name='mode')
614    >>> modeselect.getControl(value='create').selected = True
[7705]615    >>> browser.getControl('Proceed to step 3').click()
[4981]616
617Step 3/4: Fix headerlines and import:
618
619As there should be no problem with the headers, we can immediately
620perfom the import:
621
[7705]622    >>> browser.getControl('Perform import').click()
[4981]623
624This time everything should work:
625
626    >>> print browser.contents
627    <!DOCTYPE html PUBLIC...
628    ...
629    ...Successfully processed 1 rows...
630    ...
631
[12190]632Oh no, we forgot Anne Palina. Her user record was not imported because
633she has a non-existent role:
634
[4981]635    >>> sorted(os.listdir(dc_path))
[12190]636    ['deleted', 'finished', 'logs', 'unfinished', 'users_zope.mgr.create.pending.csv']
[4981]637
638    >>> os.listdir(dc_path + '/unfinished')
[12190]639    ['users_zope.mgr.csv']
[4981]640
[12190]641    >>> pending_file = dc_path + '/users_zope.mgr.create.pending.csv'
642    >>> print open(pending_file).read()
643    name,roles,title,public_name,phone,email,--ERRORS--
[12981]644    anne,['waeup.Nonsense'],Anne Palina,<IGNORE>,+49-234-567,anne@abc.de,roles: invalid role
[12190]645
646There are many finished-files:
647
[4981]648    >>> sorted(os.listdir(dc_path + '/finished'))
[8372]649    ['certificates_zope.mgr.create.finished.csv', ...,
[12190]650    'users_zope.mgr.create.finished.csv']
[4981]651
[9023]652Processed (finished) Files
653==========================
[4981]654
[9023]655    >>> browser.open('http://localhost/app/datacenter/processed')
[11460]656    >>> 'download?filename=finished/certificates_zope.mgr.create.finished.csv' in browser.contents
[9023]657    True
658
[6608]659Log Files
660=========
661
662    >>> browser.open('http://localhost/app/datacenter/logs')
663    >>> 'datacenter.log' in browser.contents
664    True
[6754]665    >>> browser.getControl('Show', index=0).click()
[6611]666    >>> browser.getControl('Back', index=0).click()
667    >>> browser.getControl('Back to Data Center').click()
668    >>> 'Storage path:' in browser.contents
669    True
[6608]670
671
[4857]672Clean up:
673
674    >>> import shutil
[6734]675    >>> shutil.rmtree(dc_path)
Note: See TracBrowser for help on using the repository browser.