]> git.donarmstrong.com Git - neurodebian.git/blob - neurodebian/dde.py
Merge remote branch 'alioth/master'
[neurodebian.git] / neurodebian / dde.py
1 #!/usr/bin/env python
2 """Tell me who you are!
3 """
4
5 import pysvn
6 import json
7 from debian_bundle import deb822
8
9 # Lets first assure no guarding (but annoying) warnings
10 import warnings
11 warnings.simplefilter('ignore', FutureWarning)
12 warnings.filterwarnings('ignore', 'Module debian_bundle was already imported.*', UserWarning)
13
14 import apt
15 from ConfigParser import SafeConfigParser
16 from optparse import OptionParser, Option, OptionGroup, OptionConflictError
17 import sys
18 import os
19 import copy
20 import shutil
21 import urllib2
22 import urllib
23 import codecs
24 import subprocess
25 import time
26 import re
27 # templating
28 from jinja2 import Environment, PackageLoader
29
30 from pprint import PrettyPrinter
31
32
33 class AptListsCache(object):
34     def __init__(self, cachedir='build/cache',
35                  ro_cachedirs=None,
36                  init_db=None):
37         self.cachedir = cachedir
38
39         if not ro_cachedirs is None:
40             self.ro_cachedirs = ro_cachedirs
41         else:
42             self.ro_cachedirs = []
43
44         # create cachedir
45         create_dir(self.cachedir)
46
47     def get(self, url, update=False):
48         """Looks in the cache if the file is there and takes the cached one.
49         Otherwise it is downloaded first.
50
51         Knows how to deal with http:// and svn:// URLs.
52
53         :Return:
54           file handler
55         """
56         # look whether it is compressed
57         cext = url.split('.')[-1]
58         if cext in ['gz', 'bz2']:
59             target_url = url[:-1 * len(cext) -1]
60         else:
61             # assume not compressed
62             target_url = url
63             cext = None
64
65         # turn url into a filename -- mimik what APT does for
66         # /var/lib/apt/lists/
67         tfilename = '_'.join(target_url.split('/')[2:])
68
69         # if we need to download anyway do not search
70         if update:
71             cfilename = os.path.join(self.cachedir, tfilename)
72         else:
73             # look for the uncompressed file anywhere in the cache
74             cfilename = None
75             for cp in [self.cachedir] + self.ro_cachedirs:
76                 if os.path.exists(os.path.join(cp, tfilename)):
77                     cfilename = os.path.join(cp, tfilename)
78
79         # nothing found?
80         if cfilename is None:
81             # add cache item
82             cfilename = os.path.join(self.cachedir, tfilename)
83             update = True
84
85         # if updated needed -- download
86         if update:
87             #print 'Caching file from %s' % url
88
89             if url.startswith('svn://'):
90                 # export from SVN
91                 pysvn.Client().export(url, cfilename)
92             if url.startswith('http://'):
93                 # download
94                 tempfile, ignored = urllib.urlretrieve(url)
95
96                 # decompress
97                 decompressor = None
98                 if cext == 'gz':
99                     decompressor = 'gzip'
100                 elif cext == 'bz2':
101                     decompressor = 'bzip2'
102                 elif cext == None:
103                     decompressor = None
104                 else:
105                     raise ValueError, \
106                           "Don't know how to decompress %s files" \
107                           % cext
108
109                 if not decompressor is None:
110                     if subprocess.call([decompressor, '-d', '-q', '-f',
111                                        tempfile]) == 1:
112                         raise RuntimeError, \
113                               "Something went wrong while decompressing '%s'" \
114                               % tempfile
115
116                 # move decompressed file into cache
117                 shutil.move(os.path.splitext(tempfile)[0], cfilename)
118
119                 # XXX do we need that if explicit filename is provided?
120                 urllib.urlcleanup()
121
122         # open cached file
123         fh = codecs.open(cfilename, 'r', 'utf-8')
124
125         return fh
126
127
128 def add_pkgfromtaskfile(db, urls):
129     cache = AptListsCache()
130     pkgs = []
131
132     for task in urls:
133         fh = cache.get(task)
134
135         # loop over all stanzas
136         for stanza in deb822.Packages.iter_paragraphs(fh):
137             if stanza.has_key('Depends'):
138                 pkg = stanza['Depends']
139             elif stanza.has_key('Recommends'):
140                 pkg = stanza['Recommends']
141             elif stanza.has_key('Suggests'):
142                 pkg = stanza['Suggests']
143             else:
144                 continue
145
146             # account for multiple packages per line
147             if pkg.count(','):
148                 pkgs += [p.strip() for p in pkg.split(',')]
149             else:
150                 pkgs.append(pkg.strip())
151
152     for p in pkgs:
153         if not db.has_key(p):
154             db[p] = get_emptydbentry()
155
156     return db
157
158 def get_emptydbentry():
159     return {'main': {}}
160
161 def import_blendstask(cfg, db, url):
162     cache = AptListsCache()
163     fh = cache.get(url)
164     task_name = None
165
166     # figure out blend's task page URL, since they differ from blend to blend
167     urlsec = url.split('/')
168     blendname = urlsec[-3]
169     if blendname == 'debian-med':
170         taskpage_url = 'http://debian-med.alioth.debian.org/tasks/'
171     elif blendname == 'debian-science':
172         taskpage_url = 'http://blends.alioth.debian.org/science/tasks/' 
173     else:
174         raise ValueError('Unknown blend "%s"' % blendname)
175     taskpage_url += urlsec[-1]
176
177     for st in deb822.Packages.iter_paragraphs(fh):
178         if st.has_key('Task'):
179             task_name = st['Task']
180             task = (blendname, task_name, taskpage_url)
181
182         if st.has_key('Depends'):
183             pkg = st['Depends']
184         elif st.has_key('Recommends'):
185             pkg = st['Recommends']
186         elif st.has_key('Suggests'):
187             pkg = st['Suggests']
188         else:
189 #            print 'Warning: Cannot determine name of prospective package ' \
190 #                    '... ignoring. Dump follows:'
191 #            print st
192             continue
193
194         # take care of pkg lists
195         for p in pkg.split(', '):
196             if not db.has_key(p):
197                 print 'Ignoring blend package "%s"' % p
198                 continue
199
200             info = {}
201
202             # blends info
203             info['tasks'] = [task]
204             if st.has_key('License'):
205                 info['license'] = st['License']
206             if st.has_key('Responsible'):
207                 info['responsible'] = st['Responsible']
208
209             # pkg description
210             if st.has_key('Pkg-Description'):
211                 descr = st['Pkg-Description'].split('\n')
212                 info['description'] = descr[0].strip()
213                 info['long_description'] = \
214                         u'\n'.join(descr[1:])
215
216                 # charge the basic property set
217                 db[p]['main']['description'] = info['description']
218                 db[p]['main']['long_description'] = info['long_description']
219             if st.has_key('WNPP'):
220                 db[p]['main']['debian_itp'] = st['WNPP']
221             if st.has_key('Pkg-URL'):
222                 db[p]['main']['other_pkg'] = st['Pkg-URL']
223             if st.has_key('Homepage'):
224                 db[p]['main']['homepage'] = st['Homepage']
225
226             # Publications
227             if st.has_key('Published-Title'):
228                 title = st['Published-Title']
229                 if title[-1] == '.':
230                     # trip trailing dot -- added later
231                     pub = {'title': title[:-1]}
232                 else:
233                     pub = {'title': title}
234                 if st.has_key('Published-Authors'):
235                     pub['authors'] = st['Published-Authors']
236                 if st.has_key('Published-Year'):
237                     pub['year'] = st['Published-Year']
238                 if st.has_key('Published-In'):
239                     pub['in'] = st['Published-In']
240                 if st.has_key('Published-URL'):
241                     pub['url'] = st['Published-URL']
242                 if st.has_key('Published-DOI'):
243                     pub['doi'] = st['Published-DOI']
244                     # need at least one URL
245                     if not pub.has_key('url'):
246                         pub['url'] = "http://dx.doi.org/%s" % st['Published-DOI']
247
248                 db[p]['main']['publication'] = pub
249
250             # Registration
251             if st.has_key('Registration'):
252                 db[p]['main']['registration'] = st['Registration']
253
254             # Remarks
255             if st.has_key('Remark'):
256                 # prepend a single space to make it look like a long description
257                 info['remark'] = convert_longdescr(' ' + st['Remark'])
258
259             # only store if there isn't something already
260             if not db[p].has_key('blends'):
261                 db[p]['blends'] = info
262             else:
263                 # just add this tasks name and id
264                 db[p]['blends']['tasks'].append(task)
265
266             # handle pkg name aliases
267             if p in cfg.options('blend package aliases'):
268                 src_entry = db[p].copy()
269                 # remove original entry
270                 del db[p]
271                 # copy the entry into all aliases
272                 for alias in cfg.get('blend package aliases', p).split():
273                     print "Aliasing %s to %s" % (p, alias)
274                     db[alias] = copy.deepcopy(src_entry)
275
276     return db
277
278
279 def get_releaseinfo(rurl):
280     cache = AptListsCache()
281     # root URL of the repository
282     baseurl = '/'.join(rurl.split('/')[:-1])
283     # get the release file from the cache
284     release_file = cache.get(rurl)
285
286     # create parser instance
287     rp = deb822.Release(release_file)
288
289     # architectures on this dist
290     archs = rp['Architectures'].split()
291     components = rp['Components'].split()
292     # compile a new codename that also considers the repository label
293     # to distinguish between official and unofficial repos.
294     label = rp['Label']
295     origin = rp['Origin']
296     codename = rp['Codename']
297     labelcode = '_'.join([rp['Label'], rp['Codename']])
298
299     # cleanup
300     release_file.close()
301
302     return {'baseurl': baseurl, 'archs': archs, 'components': components,
303             'codename': codename, 'label': label, 'labelcode': labelcode,
304             'origin': origin}
305
306
307 def build_pkgsurl(baseurl, component, arch):
308     return '/'.join([baseurl, component, 'binary-' + arch, 'Packages.bz2'])
309
310
311 def import_release(cfg, db, rurl):
312     cache = AptListsCache()
313
314     ri = get_releaseinfo(rurl)
315
316     # compile the list of Packages files to parse and parse them
317     for c in ri['components']:
318         for a in ri['archs']:
319             # compile packages URL
320             pkgsurl = build_pkgsurl(ri['baseurl'], c, a)
321
322             # retrieve from cache
323             packages_file = cache.get(pkgsurl)
324
325             # parse
326             for stanza in deb822.Packages.iter_paragraphs(packages_file):
327                 db = _store_pkg(cfg, db, stanza, ri['origin'], ri['codename'], c, ri['baseurl'])
328
329             # cleanup
330             packages_file.close()
331
332     return db
333
334 def _store_pkg(cfg, db, st, origin, codename, component, baseurl):
335     """
336     :Parameter:
337       st: Package section
338     """
339     pkg = st['Package']
340
341     # only care for known packages
342     if not db.has_key(pkg):
343 #        print 'Ignoring NeuroDebian package "%s"' % pkg
344         return db
345
346     distkey = (trans_codename(codename, cfg), 'neurodebian-' + codename)
347
348     if db[pkg].has_key(distkey):
349         info = db[pkg][distkey]
350     else:
351         info = {'architecture': []}
352
353     # fill in data
354     if not st['Architecture'] in info['architecture']:
355         info['architecture'].append(st['Architecture'])
356     info['maintainer'] = st['Maintainer']
357     if st.has_key('Homepage'):
358         info['homepage'] = st['Homepage']
359     info['version'] = st['Version']
360
361     # origin
362     info['distribution'] = origin
363     info['release'] = codename
364     info['component'] = component
365
366     # pool url
367     info['poolurl'] = '/'.join([os.path.dirname(st['Filename'])])
368
369     # pkg description
370     descr = st['Description'].replace('%', '%%').split('\n')
371     info['description'] = descr[0].strip()
372     info['long_description'] = u'\n'.join(descr[1:])
373
374     db[pkg][distkey] = info
375
376     # charge the basic property set
377     db[pkg]['main']['description'] = info['description']
378     db[pkg]['main']['long_description'] = info['long_description']
379     if st.has_key('Source'):
380         db[pkg]['main']['sv'] = "%s %s" % (st['Source'], st['Version'])
381     else:
382         db[pkg]['main']['sv'] = "%s %s" % (st['Package'], st['Version'])
383     if st.has_key('Homepage'):
384         db[pkg]['main']['homepage'] = st['Homepage']
385     if st.has_key('Recommends'):
386         db[pkg]['main']['recommends'] = st['Recommends']
387
388     return db
389
390
391 def trans_codename(codename, cfg):
392     """Translate a known codename into a release description.
393
394     Unknown codenames will simply be returned as is.
395     """
396     # if we know something, tell
397     if codename in cfg.options('release codenames'):
398         return cfg.get('release codenames', codename)
399     else:
400         return codename
401
402
403 def create_dir(path):
404     if os.path.exists(path):
405         return
406
407     ps = path.split(os.path.sep)
408
409     for i in range(1,len(ps) + 1):
410         p = os.path.sep.join(ps[:i])
411
412         if not os.path.exists(p):
413             os.mkdir(p)
414
415
416 def dde_get(url, fail=False):
417     # enforce delay to be friendly to DDE
418     time.sleep(3)
419     try:
420         data = json.read(urllib2.urlopen(url+"?t=json").read())['r']
421         print "SUCCESS:", url
422         return data
423     except urllib2.HTTPError, e:
424         print "NOINFO:", url, type(e)
425         return False
426     except urllib2.URLError, e:
427         print "URLERROR:", url, type(e)
428         if fail:
429             print "Permanant failure"
430             return False
431         print "Try again after 30 seconds..."
432         time.sleep(30)
433         return dde_get(url, fail=True)
434     except (StopIteration):
435         print "NOINFO:", url
436         return False
437     except json.ReadException, e:
438         print "UDD-DOWN?:", url, type(e)
439         return False
440
441
442 def nitrc_get(spec, fail=False):
443     nitrc_url = 'http://www.nitrc.org/export/site/projects.json.php'
444     try:
445         # change into this from python 2.6 on
446         #data = json.loads(urllib2.urlopen(nitrc_url + '?spec=%s' % spec).read())
447         data = json.read(urllib2.urlopen(nitrc_url + '?spec=%s' % spec).read())
448         print "NITRC-SUCCESS:", spec
449     except urllib2.HTTPError, e:
450         print "NITRC-NOINFO:", spec, type(e)
451         return False
452     except urllib2.URLError, e:
453         print "NITRC-URLERROR:", spec, type(e)
454         if fail:
455             print "Permanant failure"
456             return False
457         print "Try again after 30 seconds..."
458         time.sleep(30)
459         return nitrc_get(spec, fail=True)
460     return data
461
462
463 def parse_nitrc(data):
464     if data is False:
465         return None
466     # simplify -- there is only one project in the data
467     project = data['projects'][0]
468     nitrc_filtered = {'downloads': 0,
469                       'id': project['id']}
470     for pkg in project['packages']:
471         for release in pkg['releases']:
472             for file in release['files']:
473                 nitrc_filtered['downloads'] += file['download_count']
474     return nitrc_filtered
475
476
477 def import_nitrc(cfg, db):
478     for p in db.keys():
479         if not cfg.has_option("nitrc ids", p):
480             continue
481         nitrc_spec = cfg.get("nitrc ids", p)
482         nitrc_data = nitrc_get(nitrc_spec)
483         nitrc_excerpt = parse_nitrc(nitrc_data)
484         if not nitrc_excerpt is None:
485             db[p]['nitrc'] = nitrc_excerpt
486     return db
487
488
489 def import_dde(cfg, db):
490     query_url = cfg.get('dde', 'pkgquery_url')
491     for p in db.keys():
492         # get freshest
493         q = dde_get(query_url + "/packages/all/%s" % p)
494         if q:
495             # copy all stuff, while preserving non-overlapping information
496             for k, v in q.iteritems():
497                 db[p]['main'][k] = v
498             # get latest popcon info for debian and ubuntu
499             # cannot use origin field itself, since it is none for few packages
500             # i.e. python-nifti
501             origin = q['drc'].split()[0]
502             if origin == 'ubuntu':
503                 if q.has_key('popcon'):
504                     db[p]['main']['ubuntu_popcon'] = q['popcon']
505                 # if we have ubuntu, need to get debian
506                 q = dde_get(query_url + "/packages/prio-debian-sid/%s" % p)
507                 if q and q.has_key('popcon'):
508                     db[p]['main']['debian_popcon'] = q['popcon']
509             elif origin == 'debian':
510                 if q.has_key('popcon'):
511                     db[p]['main']['debian_popcon'] = q['popcon']
512                 # if we have debian, need to get ubuntu
513                 q = dde_get(query_url + "/packages/prio-ubuntu-natty/%s" % p)
514                 if q and q.has_key('popcon'):
515                     db[p]['main']['ubuntu_popcon'] = q['popcon']
516             else:
517                 print("Ignoring unkown origin '%s' for package '%s'." \
518                         % (origin, p))
519
520         # now get info for package from all releases in UDD
521         q = dde_get(query_url + "/dist/p:%s" % p)
522         if not q:
523             continue
524         # hold all info about this package per distribution release
525         info = {}
526         for cp in q:
527             distkey = (trans_codename(cp['release'], cfg),
528                        "%s-%s" % (cp['distribution'], cp['release']))
529             if not info.has_key(distkey):
530                 info[distkey] = cp
531                 # turn into a list to append others later
532                 info[distkey]['architecture'] = [info[distkey]['architecture']]
533             # accumulate data for multiple over archs
534             else:
535                 comp = apt.VersionCompare(cp['version'],
536                                           info[distkey]['version'])
537                 # found another arch for the same version
538                 if comp == 0:
539                     info[distkey]['architecture'].append(cp['architecture'])
540                 # found newer version, dump the old ones
541                 elif comp > 0:
542                     info[distkey] = cp
543                     # turn into a list to append others later
544                     info[distkey]['architecture'] = [info[distkey]['architecture']]
545                 # simply ignore older versions
546                 else:
547                     pass
548
549         # finally assign the new package data
550         for k, v in info.iteritems():
551             db[p][k] = v
552
553     return db
554
555 def assure_unicode(s):
556     """Assure that argument is unicode
557
558     Necessary if strings are not carrying out Pythonish 'u' prefix to
559     signal UTF8 strings, but are in fact UTF8
560     """
561     if type(s) is unicode:
562         return s
563     elif type(s) is str:
564         # attempt regular unicode call and if fails -- just decode it
565         # into utf8
566         try:
567             return unicode(s)
568         except UnicodeDecodeError, e:
569             return s.decode('utf8')
570     else:
571         return assure_unicode(str(s))
572
573
574 def convert_longdescr(ld):
575     ld = ld.replace('% ', '%% ')
576     ld = ld.split('\n')
577     for i, l in enumerate(ld):
578         if l == ' .':
579             ld[i] = ' #NEWLINEMARKER#'
580         # look for embedded lists
581         elif len(l) >=3 and l[:2] == '  ' and l[2] in '-*':
582             ld[i] = ' #NEWLINEMARKER# ' + l[2:]
583
584     ld = u' '.join([l[1:] for l in ld])
585     ld = ld.replace('#NEWLINEMARKER# ', '\n\n')
586     # cleanup any leftover (e.g. trailing markers)
587     ld = ld.replace('#NEWLINEMARKER#', '')
588     # safe-guard ReST active symbols
589     ld = re.sub(r'([\'`*])', r'\\\1', ld)
590     return ld
591
592
593 def generate_pkgpage(pkg, cfg, db, template, addenum_dir, extracts_dir):
594     print pkg
595     # local binding for ease of use
596     pkgdb = db[pkg]
597     # do nothing if there is not at least the very basic stuff
598     if not pkgdb['main'].has_key('description'):
599         return
600     title = '**%s** -- %s' % (pkg, pkgdb['main']['description'])
601     underline = '*' * (len(title) + 2)
602     title = '%s\n %s\n%s' % (underline, title, underline)
603
604     ex_dir = None
605     if 'sv' in pkgdb['main']:
606         ex_dir = os.path.join(extracts_dir, pkgdb['main']['sv'].split()[0])
607         if not os.path.exists(ex_dir):
608             ex_dir = None
609     page = template.render(
610             pkg=pkg,
611             title=title,
612             long_description=convert_longdescr(
613                 assure_unicode(pkgdb['main']['long_description'])),
614             cfg=cfg,
615             db=pkgdb,
616             fulldb=db,
617             extracts_dir=ex_dir,
618             op=os.path)
619     # the following can be replaced by something like
620     # {% include "sidebar.html" ignore missing %}
621     # in the template whenever jinja 2.2 becomes available
622     addenum = os.path.join(os.path.abspath(addenum_dir), '%s.rst' % pkg)
623     if os.path.exists(addenum):
624         page += '\n\n.. include:: %s\n' % addenum
625     return page
626
627
628 def store_db(db, filename):
629     pp = PrettyPrinter(indent=2)
630     f = codecs.open(filename, 'w', 'utf-8')
631     f.write(pp.pformat(db))
632     f.close()
633
634
635 def read_db(filename):
636     f = codecs.open(filename, 'r', 'utf-8')
637     db = eval(f.read())
638     return db
639
640 def write_sourceslist(jinja_env, cfg, outdir):
641     create_dir(outdir)
642     create_dir(os.path.join(outdir, 'lists'))
643
644     repos = {}
645     for release in cfg.options('release codenames'):
646         if release == 'data':
647             # no seperate list for the data archive
648             continue
649         transrel = trans_codename(release, cfg)
650         repos[transrel] = []
651         for mirror in cfg.options('mirrors'):
652             listname = '%s.%s' % (release, mirror)
653             repos[transrel].append((mirror, listname))
654             lf = open(os.path.join(outdir, 'lists', listname), 'w')
655             for rel in ('data', release):
656                 aptcfg = '%s %s main contrib non-free\n' % (cfg.get('mirrors', mirror),
657                                                           rel)
658                 lf.write('deb %s' % aptcfg)
659                 lf.write('#deb-src %s' % aptcfg)
660             lf.close()
661
662     id2codename = dict([(cfg.get('release backport ids', r), r)
663                             for r in cfg.options('release codenames')])
664     id2relname = dict([(cfg.get('release backport ids', r), trans_codename(r, cfg))
665                             for r in cfg.options('release codenames')])
666     mirror2name = dict([(m, cfg.get('mirror names', m))
667                             for m in cfg.options('mirrors')])
668     mirror2url = dict([(m, cfg.get('mirrors', m))
669                             for m in cfg.options('mirrors')])
670     srclist_template = jinja_env.get_template('sources_lists.rst')
671     sl = open(os.path.join(outdir, 'sources_lists'), 'w')
672     sl.write(srclist_template.render(id2codename=id2codename,
673                                      id2relname=id2relname,
674                                      mirror2name=mirror2name,
675                                      mirror2url=mirror2url))
676     sl.close()
677
678
679 def write_pkgpages(jinja_env, cfg, db, outdir, addenum_dir, extracts_dir):
680     create_dir(outdir)
681     create_dir(os.path.join(outdir, 'pkgs'))
682
683     # generate the TOC with all packages
684     toc_template = jinja_env.get_template('pkgs_toc.rst')
685     toc = codecs.open(os.path.join(outdir, 'pkgs.rst'), 'w', 'utf-8')
686     # this is a fragile test
687     toc.write(toc_template.render(
688         pkgs=[k for k in db.keys()
689                 if not ('Datasets (data)', 'neurodebian-data') in db[k]]))
690     toc.close()
691     # and now only for dataset packages
692     toc_template = jinja_env.get_template('datasets_toc.rst')
693     toc = codecs.open(os.path.join(outdir, 'datasets.rst'), 'w', 'utf-8')
694     # this is a fragile test
695     toc.write(toc_template.render(
696         pkgs=[k for k in db.keys()
697                 if ('Datasets (data)', 'neurodebian-data') in db[k]]))
698     toc.close()
699
700
701     # and now each individual package page
702     pkg_template = jinja_env.get_template('pkg.rst')
703     for p in db.keys():
704         page = generate_pkgpage(p, cfg, db, pkg_template, addenum_dir, extracts_dir)
705         # when no page is available skip this package
706         if page is None:
707             continue
708         pf = codecs.open(os.path.join(outdir, 'pkgs', p + '.rst'), 'w', 'utf-8')
709         pf.write(page)
710         pf.close()
711
712
713 def prepOptParser(op):
714     # use module docstring for help output
715     op.usage = "%s [OPTIONS]\n\n" % sys.argv[0] + __doc__
716
717     op.add_option("--db",
718                   action="store", type="string", dest="db",
719                   default=None,
720                   help="Database file to read. Default: None")
721
722     op.add_option("--cfg",
723                   action="store", type="string", dest="cfg",
724                   default=None,
725                   help="Repository config file.")
726
727     op.add_option("-o", "--outdir",
728                   action="store", type="string", dest="outdir",
729                   default=None,
730                   help="Target directory for ReST output. Default: None")
731
732     op.add_option("-r", "--release-url",
733                   action="append", dest="release_urls",
734                   help="None")
735
736     op.add_option("--pkgaddenum", action="store", dest="addenum_dir",
737                   type="string", default=None, help="None")
738
739     op.add_option("--extracts", action="store", dest="extracts_dir",
740                   type="string", default=None, help="None")
741
742
743 def main():
744     op = OptionParser(version="%prog 0.0.2")
745     prepOptParser(op)
746
747     (opts, args) = op.parse_args()
748
749     if len(args) != 1:
750         print('There needs to be exactly one command')
751         sys.exit(1)
752
753     cmd = args[0]
754
755     if opts.cfg is None:
756         print("'--cfg' option is mandatory.")
757         sys.exit(1)
758     if opts.db is None:
759         print("'--db' option is mandatory.")
760         sys.exit(1)
761
762
763     cfg = SafeConfigParser()
764     cfg.read(opts.cfg)
765
766     # load existing db, unless renew is requested
767     if cmd == 'updatedb':
768         db = {}
769         if cfg.has_option('packages', 'select taskfiles'):
770             db = add_pkgfromtaskfile(db, cfg.get('packages',
771                                                  'select taskfiles').split())
772
773         # add additional package names from config file
774         if cfg.has_option('packages', 'select names'):
775             for p in cfg.get('packages', 'select names').split():
776                 if not db.has_key(p):
777                     db[p] = get_emptydbentry()
778
779         # get info from task files
780         if cfg.has_option('packages', 'prospective'):
781             for url in cfg.get('packages', 'prospective').split():
782                 db = import_blendstask(cfg, db, url)
783
784         # parse NeuroDebian repository
785         if cfg.has_option('neurodebian', 'releases'):
786             for rurl in cfg.get('neurodebian', 'releases').split():
787                 db = import_release(cfg, db, rurl)
788
789         # collect package information from DDE
790         db = import_dde(cfg, db)
791         # get info from NITRC
792         db = import_nitrc(cfg, db)
793         # store the new DB
794         store_db(db, opts.db)
795         # and be done
796         return
797
798     # load the db from file
799     db = read_db(opts.db)
800
801     # fire up jinja
802     jinja_env = Environment(loader=PackageLoader('neurodebian', 'templates'))
803
804     # generate package pages and TOC and write them to files
805     write_pkgpages(jinja_env, cfg, db, opts.outdir, opts.addenum_dir, opts.extracts_dir)
806
807     write_sourceslist(jinja_env, cfg, opts.outdir)
808
809 if __name__ == "__main__":
810     main()