]> git.decadent.org.uk Git - dak.git/blob - tools/queue_rss.py
Merge commit 'godog/master' into merge
[dak.git] / tools / queue_rss.py
1 #!/usr/bin/python
2 # Generate two rss feeds for a directory with .changes file
3
4 # License: GPL v2 or later
5 # Author: Filippo Giunchedi <filippo@debian.org>
6 # Version: 0.4
7
8 import cgi
9 import os
10 import os.path
11 import cPickle
12 import sys
13 import encodings.ascii
14 from optparse import OptionParser
15 from datetime import datetime
16
17 import PyRSS2Gen
18
19 from debian_bundle.deb822 import Changes
20
21 inrss_filename = "NEW_in.rss"
22 outrss_filename = "NEW_out.rss"
23 db_filename = "status.db"
24
25 parser = OptionParser()
26 parser.set_defaults(queuedir="queue", outdir="out", datadir="status", max_entries="30")
27
28 parser.add_option("-q", "--queuedir", dest="queuedir",
29         help="The queue dir (%default)")
30 parser.add_option("-o", "--outdir", dest="outdir",
31         help="The output directory (%default)")
32 parser.add_option("-d", "--datadir", dest="datadir",
33         help="The data dir (%default)")
34 parser.add_option("-m", "--max-entries", dest="max_entries", type="int",
35         help="Max number of entries to keep (%default)")
36
37 class Status:
38     def __init__(self):
39         self.feed_in = PyRSS2Gen.RSS2(
40                        title = "Packages entering NEW",
41                        link = "http://ftp-master.debian.org/new.html",
42                        description = "Debian packages entering the NEW queue" )
43
44         self.feed_out = PyRSS2Gen.RSS2(
45                        title = "Packages leaving NEW",
46                        link = "http://ftp-master.debian.org/new.html",
47                        description = "Debian packages leaving the NEW queue" )
48
49         self.queue = {}
50
51 def utf2ascii(src):
52     """ Return an ASCII encoded copy of the input UTF-8 string """
53     try:
54         res = unicode(src, 'utf-8').encode('ascii', 'replace')
55     except UnicodeDecodeError:
56         res = None
57     return res
58
59 def purge_old_items(feed, max):
60     """ Purge RSSItem from feed, no more than max. """
61     if feed.items is None or len(feed.items) == 0:
62         return False
63
64     feed.items = feed.items[:max]
65     return True
66
67 def parse_changes(fname):
68     """ Parse a .changes file named fname.
69
70     Return {fname: parsed} """
71
72     m = Changes(open(fname))
73
74     wanted_fields = set(['Source', 'Version', 'Architecture', 'Distribution',
75                          'Date', 'Maintainer', 'Description', 'Changes'])
76
77     if not set(m.keys()).issuperset(wanted_fields):
78         return None
79
80     return {os.path.basename(fname): m}
81
82 def parse_queuedir(dir):
83     """ Parse dir for .changes files.
84
85     Return a dictionary {filename: parsed_file}"""
86
87     if not os.path.exists(dir):
88         return None
89
90     res = {}
91     for fname in os.listdir(dir):
92         if not fname.endswith(".changes"):
93             continue
94
95         parsed = parse_changes(os.path.join(dir, fname))
96         if parsed:
97             res.update(parsed)
98
99     return res
100
101 def add_rss_item(status, msg, direction):
102     if direction == "in":
103         feed = status.feed_in
104         title = "%s %s entered NEW" % (msg['Source'], msg['Version'])
105         pubdate = msg['Date']
106     elif direction == "out":
107         feed = status.feed_out
108         title = "%s %s left NEW" % (msg['Source'], msg['Version'])
109         pubdate = datetime.utcnow()
110     else:
111         return False
112
113     description = "<pre>Description: %s\nChanges: %s\n</pre>" % \
114             (utf2ascii(cgi.escape(msg['Description'])), utf2ascii(cgi.escape(msg['Changes'])))
115
116     link = "http://ftp-master.debian.org/new/%s_%s.html" % \
117             (msg['Source'], msg['Version'])
118
119     feed.items.insert(0,
120         PyRSS2Gen.RSSItem(
121             title,
122             pubDate = pubdate,
123             description = description,
124             author = utf2ascii(cgi.escape(msg['Maintainer'])),
125             link = link,
126             guid = link
127         )
128     )
129
130 def update_feeds(curqueue, status):
131     # inrss -> append all items in curqueue not in status.queue
132     # outrss -> append all items in status.queue not in curqueue
133
134     for (name, parsed) in curqueue.items():
135         if not status.queue.has_key(name):
136             # new package
137             add_rss_item(status, parsed, "in")
138
139     for (name, parsed) in status.queue.items():
140         if not curqueue.has_key(name):
141             # removed package
142             add_rss_item(status, parsed, "out")
143
144
145
146 if __name__ == "__main__":
147
148     (settings, args) = parser.parse_args()
149
150     if not os.path.exists(settings.outdir):
151         sys.stderr.write("Outdir '%s' does not exists\n" % settings.outdir)
152         parser.print_help()
153         sys.exit(1)
154
155     if not os.path.exists(settings.datadir):
156         sys.stderr.write("Datadir '%s' does not exists\n" % settings.datadir)
157         parser.print_help()
158         sys.exit(1)
159
160     status_db = os.path.join(settings.datadir, db_filename)
161
162     try:
163         status = cPickle.load(open(status_db))
164     except IOError:
165         status = Status()
166
167     current_queue = parse_queuedir(settings.queuedir)
168     if not current_queue:
169         sys.stderr.write("Unable to scan queuedir '%s'\n" % settings.queuedir)
170         parser.print_help()
171         sys.exit(1)
172
173     update_feeds(current_queue, status)
174
175     purge_old_items(status.feed_in, settings.max_entries)
176     purge_old_items(status.feed_out, settings.max_entries)
177
178     feed_in_file = os.path.join(settings.outdir, inrss_filename)
179     feed_out_file = os.path.join(settings.outdir, outrss_filename)
180
181     try:
182         status.feed_in.write_xml(file(feed_in_file, "w+"), "utf-8")
183         status.feed_out.write_xml(file(feed_out_file, "w+"), "utf-8")
184     except IOError, why:
185         sys.stderr.write("Unable to write feeds: %s\n", why)
186         sys.exit(1)
187
188     status.queue = current_queue
189
190     try:
191         cPickle.dump(status, open(status_db, "w+"))
192     except IOError, why:
193         sys.stderr.write("Unable to save status: %s\n", why)
194         sys.exit(1)
195
196 # vim:et:ts=4