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