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