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