1#! /usr/bin/env python3
3"""Produces a report on NEW and BYHAND packages"""
4# Copyright (C) 2001, 2002, 2003, 2005, 2006 James Troup <james@nocrew.org>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20################################################################################
22# <o-o> XP runs GCC, XFREE86, SSH etc etc,.,, I feel almost like linux....
23# <o-o> I am very confident that I can replicate any Linux application on XP
24# <willy> o-o: *boggle*
25# <o-o> building from source.
26# <o-o> Viiru: I already run GIMP under XP
27# <willy> o-o: why do you capitalise the names of all pieces of software?
28# <o-o> willy: because I want the EMPHASIZE them....
29# <o-o> grr s/the/to/
30# <willy> o-o: it makes you look like ZIPPY the PINHEAD
31# <o-o> willy: no idea what you are talking about.
32# <willy> o-o: do some research
33# <o-o> willy: for what reason?
35################################################################################
37import datetime
38import functools
39import html
40import os
41import sys
42import time
44import apt_pkg
46from daklib import utils
47from daklib.dak_exceptions import ParseMaintError
48from daklib.dbconn import DBConn, PolicyQueue, get_uid_from_fingerprint, has_new_comment
49from daklib.policy import PolicyQueueUploadHandler
50from daklib.textutils import fix_maintainer
51from daklib.utils import get_logins_from_ldap
53Cnf = None
54direction = []
56################################################################################
59def usage(exit_code=0):
60 print(
61 """Usage: dak queue-report
62Prints a report of packages in queues (usually new and byhand).
64 -h, --help show this help and exit.
65 -8, --822 writes 822 formated output to the location set in dak.conf
66 -n, --new produce html-output
67 -s, --sort=key sort output according to key, see below.
68 -a, --age=key if using sort by age, how should time be treated?
69 If not given a default of hours will be used.
70 -r, --rrd=key Directory where rrd files to be updated are stored
71 -d, --directories=key A comma separated list of queues to be scanned
73 Sorting Keys: ao=age, oldest first. an=age, newest first.
74 na=name, ascending nd=name, descending
75 nf=notes, first nl=notes, last
77 Age Keys: m=minutes, h=hours, d=days, w=weeks, o=months, y=years
79"""
80 )
81 sys.exit(exit_code)
84################################################################################
87def plural(x):
88 if x > 1:
89 return "s"
90 else:
91 return ""
94################################################################################
97def time_pp(x):
98 if x < 60: 98 ↛ 100line 98 didn't jump to line 100, because the condition on line 98 was never false
99 unit = "second"
100 elif x < 3600:
101 x /= 60
102 unit = "minute"
103 elif x < 86400:
104 x /= 3600
105 unit = "hour"
106 elif x < 604800:
107 x /= 86400
108 unit = "day"
109 elif x < 2419200:
110 x /= 604800
111 unit = "week"
112 elif x < 29030400:
113 x /= 2419200
114 unit = "month"
115 else:
116 x /= 29030400
117 unit = "year"
118 x = int(x)
119 return "%s %s%s" % (x, unit, plural(x))
122################################################################################
125def sg_compare(a, b):
126 a = a[1]
127 b = b[1]
128 # Sort by have pending action, have note, time of oldest upload.
129 # Sort by have pending action
130 a_note_state = a["processed"]
131 b_note_state = b["processed"]
132 if a_note_state < b_note_state: 132 ↛ 133line 132 didn't jump to line 133, because the condition on line 132 was never true
133 return -1
134 elif a_note_state > b_note_state: 134 ↛ 135line 134 didn't jump to line 135, because the condition on line 134 was never true
135 return 1
137 # Sort by have note
138 a_note_state = a["note_state"]
139 b_note_state = b["note_state"]
140 if a_note_state < b_note_state: 140 ↛ 141line 140 didn't jump to line 141, because the condition on line 140 was never true
141 return -1
142 elif a_note_state > b_note_state: 142 ↛ 143line 142 didn't jump to line 143, because the condition on line 142 was never true
143 return 1
145 # Sort by time of oldest upload
146 return a["oldest"] - b["oldest"]
149############################################################
152def sortfunc(a, b):
153 for sorting in direction: 153 ↛ 154line 153 didn't jump to line 154, because the loop on line 153 never started
154 (sortkey, way, time) = sorting
155 ret = 0
156 if time == "m":
157 x = int(a[sortkey] / 60)
158 y = int(b[sortkey] / 60)
159 elif time == "h":
160 x = int(a[sortkey] / 3600)
161 y = int(b[sortkey] / 3600)
162 elif time == "d":
163 x = int(a[sortkey] / 86400)
164 y = int(b[sortkey] / 86400)
165 elif time == "w":
166 x = int(a[sortkey] / 604800)
167 y = int(b[sortkey] / 604800)
168 elif time == "o":
169 x = int(a[sortkey] / 2419200)
170 y = int(b[sortkey] / 2419200)
171 elif time == "y":
172 x = int(a[sortkey] / 29030400)
173 y = int(b[sortkey] / 29030400)
174 else:
175 x = a[sortkey]
176 y = b[sortkey]
177 if x < y:
178 ret = -1
179 elif x > y:
180 ret = 1
181 if ret != 0:
182 if way < 0:
183 ret = ret * -1
184 return ret
185 return 0
188############################################################
191def header():
192 print(
193 """<!DOCTYPE html>
194<html lang="en">
195 <head>
196 <meta charset="utf-8">
197 <link rel="stylesheet" href="style.css">
198 <link rel="shortcut icon" href="https://www.debian.org/favicon.ico">
199 <title>
200 Debian NEW and BYHAND Packages
201 </title>
202 <script>
203 function togglePkg() {
204 for (const el of document.getElementsByClassName('sourceNEW')) {
205 el.style.display = el.style.display == '' ? 'none' : '';
206 }
207 }
208 </script>
209 </head>
210 <body id="NEW">
211 <div id="logo">
212 <a href="https://www.debian.org/">
213 <img src="https://www.debian.org/logos/openlogo-nd-50.png"
214 alt=""></a>
215 <a href="https://www.debian.org/">
216 <img src="https://www.debian.org/Pics/debian.png"
217 alt="Debian Project"></a>
218 </div>
219 <div id="titleblock">
221 <img src="https://www.debian.org/Pics/red-upperleft.png"
222 id="red-upperleft" alt="">
223 <img src="https://www.debian.org/Pics/red-lowerleft.png"
224 id="red-lowerleft" alt="">
225 <img src="https://www.debian.org/Pics/red-upperright.png"
226 id="red-upperright" alt="">
227 <img src="https://www.debian.org/Pics/red-lowerright.png"
228 id="red-lowerright" alt="">
229 <span class="title">
230 Debian NEW and BYHAND Packages
231 </span>
232 </div>
233 """
234 )
237def footer():
238 print(
239 '<p class="timestamp">Timestamp: %s (UTC)</p>'
240 % (time.strftime("%d.%m.%Y / %H:%M:%S", time.gmtime()))
241 )
242 print(
243 """
244 <p>
245 There are <a href=\"/stat.html\">graphs about the queues</a> available.
246 You can also look at the <a href="/new.822">RFC822 version</a>.
247 </p>
248 """
249 )
251 print(
252 """
253 <div class="footer">
254 <p>Hint: Age is the youngest upload of the package, if there is more than
255 one version.<br>
256 You may want to look at <a href="https://ftp-master.debian.org/REJECT-FAQ.html">the REJECT-FAQ</a>
257 for possible reasons why one of the above packages may get rejected.</p>
258 </div> </body> </html>
259 """
260 )
263def table_header(type, source_count, total_count):
264 print("<h1 class='sourceNEW'>Summary for: %s</h1>" % (type))
265 print(
266 "<h1 class='sourceNEW' style='display: none'>Summary for: binary-%s only</h1>"
267 % (type)
268 )
269 print(
270 """
271 <p class="togglepkg" onclick="togglePkg()">Click to toggle all/binary-NEW packages</p>
272 <table class="NEW">
273 <caption class="sourceNEW">
274 """
275 )
276 print(
277 "Package count in <strong>%s</strong>: <em>%s</em> | Total Package count: <em>%s</em>"
278 % (type, source_count, total_count)
279 )
280 print(
281 """
282 </caption>
283 <thead>
284 <tr>
285 <th>Package</th>
286 <th>Version</th>
287 <th>Arch</th>
288 <th>Distribution</th>
289 <th>Age</th>
290 <th>Upload info</th>
291 <th>Closes</th>
292 </tr>
293 </thead>
294 <tbody>
295 """
296 )
299def table_footer(type):
300 print("</tbody></table>")
303def table_row(
304 source,
305 version,
306 arch,
307 last_mod,
308 maint,
309 distribution,
310 closes,
311 fingerprint,
312 sponsor,
313 changedby,
314):
315 trclass = "sid"
316 session = DBConn().session()
317 for dist in distribution:
318 if dist == "experimental":
319 trclass = "exp"
321 query = """SELECT source
322 FROM source_suite
323 WHERE source = :source
324 AND suite_name IN ('unstable', 'experimental')"""
325 if not session.execute(query, {"source": source}).rowcount:
326 trclass += " sourceNEW"
327 session.commit()
329 print('<tr class="%s">' % (trclass))
331 if "sourceNEW" in trclass:
332 print('<td class="package">%s</td>' % (source))
333 else:
334 print(
335 '<td class="package"><a href="https://tracker.debian.org/pkg/%(source)s">%(source)s</a></td>'
336 % {"source": source}
337 )
338 print('<td class="version">')
339 for vers in version.split():
340 print(
341 '<a href="new/%s_%s.html">%s</a><br>'
342 % (source, html.escape(vers), html.escape(vers, quote=False))
343 )
344 print("</td>")
345 print('<td class="arch">%s</td>' % (arch))
346 print('<td class="distribution">')
347 for dist in distribution:
348 print("%s<br>" % (dist))
349 print("</td>")
350 print(
351 '<td class="age"><abbr title="%s">%s</abbr></td>'
352 % (
353 datetime.datetime.utcfromtimestamp(int(time.time()) - last_mod).strftime(
354 "%a, %d %b %Y %T UTC"
355 ),
356 time_pp(last_mod),
357 )
358 )
359 (name, mail) = maint.split(":", 1)
361 print('<td class="upload-data">')
362 print(
363 '<span class="maintainer">Maintainer: <a href="https://qa.debian.org/developer.php?login=%s">%s</a></span><br>'
364 % (html.escape(mail), html.escape(name, quote=False))
365 )
366 (name, mail) = changedby.split(":", 1)
367 print(
368 '<span class="changed-by">Changed-By: <a href="https://qa.debian.org/developer.php?login=%s">%s</a></span><br>'
369 % (html.escape(mail), html.escape(name, quote=False))
370 )
372 if sponsor:
373 print(
374 '<span class="sponsor">Sponsor: <a href="https://qa.debian.org/developer.php?login=%s">%s</a>@debian.org</span><br>'
375 % (html.escape(sponsor), html.escape(sponsor, quote=False))
376 )
378 print('<span class="signature">Fingerprint: %s</span>' % (fingerprint))
379 print("</td>")
381 print('<td class="closes">')
382 for close in closes:
383 print(
384 '<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s">#%s</a><br>'
385 % (html.escape(close), html.escape(close, quote=False))
386 )
387 print("</td></tr>")
390############################################################
393def update_graph_database(rrd_dir, type, n_source, n_binary):
394 if not rrd_dir: 394 ↛ 397line 394 didn't jump to line 397, because the condition on line 394 was never false
395 return
397 import rrdtool
399 rrd_file = os.path.join(rrd_dir, type.lower() + ".rrd")
400 update = [rrd_file, "N:%s:%s" % (n_source, n_binary)]
402 try:
403 rrdtool.update(*update)
404 except rrdtool.error:
405 create = (
406 [rrd_file]
407 + """
408--step
409300
410--start
4110
412DS:ds0:GAUGE:7200:0:1000
413DS:ds1:GAUGE:7200:0:1000
414RRA:AVERAGE:0.5:1:599
415RRA:AVERAGE:0.5:6:700
416RRA:AVERAGE:0.5:24:775
417RRA:AVERAGE:0.5:288:795
418RRA:MAX:0.5:1:600
419RRA:MAX:0.5:6:700
420RRA:MAX:0.5:24:775
421RRA:MAX:0.5:288:795
422""".strip().split(
423 "\n"
424 )
425 )
426 try:
427 rrdtool.create(*create)
428 rrdtool.update(*update)
429 except rrdtool.error as e:
430 print(
431 (
432 "warning: queue_report: rrdtool error, skipping %s.rrd: %s"
433 % (type, e)
434 )
435 )
436 except NameError:
437 pass
440############################################################
443def process_queue(queue, log, rrd_dir):
444 msg = ""
445 type = queue.queue_name
446 session = DBConn().session()
448 # Divide the .changes into per-source groups
449 per_source = {}
450 total_pending = 0
451 for upload in queue.uploads:
452 source = upload.changes.source
453 if source not in per_source: 453 ↛ 461line 453 didn't jump to line 461, because the condition on line 453 was never false
454 per_source[source] = {}
455 per_source[source]["list"] = []
456 per_source[source]["processed"] = ""
457 handler = PolicyQueueUploadHandler(upload, session)
458 if handler.get_action(): 458 ↛ 459line 458 didn't jump to line 459, because the condition on line 458 was never true
459 per_source[source]["processed"] = "PENDING %s" % handler.get_action()
460 total_pending += 1
461 per_source[source]["list"].append(upload)
462 per_source[source]["list"].sort(key=lambda x: x.changes.created, reverse=True)
463 # Determine oldest time and have note status for each source group
464 for source in list(per_source.keys()):
465 source_list = per_source[source]["list"]
466 first = source_list[0]
467 oldest = time.mktime(first.changes.created.timetuple())
468 have_note = 0
469 for d in per_source[source]["list"]:
470 mtime = time.mktime(d.changes.created.timetuple())
471 if "Queue-Report::Options::New" in Cnf: 471 ↛ 472line 471 didn't jump to line 472, because the condition on line 471 was never true
472 if mtime > oldest:
473 oldest = mtime
474 else:
475 if mtime < oldest: 475 ↛ 476line 475 didn't jump to line 476, because the condition on line 475 was never true
476 oldest = mtime
477 have_note += has_new_comment(
478 d.policy_queue, d.changes.source, d.changes.version
479 )
480 per_source[source]["oldest"] = oldest
481 if not have_note: 481 ↛ 483line 481 didn't jump to line 483, because the condition on line 481 was never false
482 per_source[source]["note_state"] = 0 # none
483 elif have_note < len(source_list):
484 per_source[source]["note_state"] = 1 # some
485 else:
486 per_source[source]["note_state"] = 2 # all
487 per_source_items = list(per_source.items())
488 per_source_items.sort(key=functools.cmp_to_key(sg_compare))
490 update_graph_database(rrd_dir, type, len(per_source_items), len(queue.uploads))
492 entries = []
493 max_source_len = 0
494 max_version_len = 0
495 max_arch_len = 0
496 try:
497 logins = get_logins_from_ldap()
498 except:
499 logins = dict()
500 for i in per_source_items:
501 maintainer = {}
502 maint = ""
503 distribution = ""
504 closes = ""
505 fingerprint = ""
506 changeby = {}
507 changedby = ""
508 sponsor = ""
509 filename = i[1]["list"][0].changes.changesname
510 last_modified = time.time() - i[1]["oldest"]
511 source = i[1]["list"][0].changes.source
512 if len(source) > max_source_len: 512 ↛ 514line 512 didn't jump to line 514, because the condition on line 512 was never false
513 max_source_len = len(source)
514 binary_list = i[1]["list"][0].binaries
515 binary = ", ".join([b.package for b in binary_list])
516 arches = set()
517 versions = set()
518 for j in i[1]["list"]:
519 dbc = j.changes
521 if ( 521 ↛ 525line 521 didn't jump to line 525
522 "Queue-Report::Options::New" in Cnf
523 or "Queue-Report::Options::822" in Cnf
524 ):
525 try:
526 (
527 maintainer["maintainer822"],
528 maintainer["maintainer2047"],
529 maintainer["maintainername"],
530 maintainer["maintaineremail"],
531 ) = fix_maintainer(dbc.maintainer)
532 except ParseMaintError:
533 print("Problems while parsing maintainer address\n")
534 maintainer["maintainername"] = "Unknown"
535 maintainer["maintaineremail"] = "Unknown"
536 maint = "%s:%s" % (
537 maintainer["maintainername"],
538 maintainer["maintaineremail"],
539 )
540 # ...likewise for the Changed-By: field if it exists.
541 try:
542 (
543 changeby["changedby822"],
544 changeby["changedby2047"],
545 changeby["changedbyname"],
546 changeby["changedbyemail"],
547 ) = fix_maintainer(dbc.changedby)
548 except ParseMaintError:
549 (
550 changeby["changedby822"],
551 changeby["changedby2047"],
552 changeby["changedbyname"],
553 changeby["changedbyemail"],
554 ) = ("", "", "", "")
555 changedby = "%s:%s" % (
556 changeby["changedbyname"],
557 changeby["changedbyemail"],
558 )
560 distribution = dbc.distribution.split()
561 closes = dbc.closes
563 fingerprint = dbc.fingerprint
564 sponsor_uid = get_uid_from_fingerprint(fingerprint, session)
565 sponsor_name = sponsor_uid.name
566 sponsor_login = sponsor_uid.uid
567 if "@" in sponsor_login:
568 if fingerprint in logins:
569 sponsor_login = logins[fingerprint]
570 if (
571 sponsor_name != maintainer["maintainername"]
572 and sponsor_name != changeby["changedbyname"]
573 and sponsor_login + "@debian.org" != maintainer["maintaineremail"]
574 and sponsor_name != changeby["changedbyemail"]
575 ):
576 sponsor = sponsor_login
578 for arch in dbc.architecture.split():
579 arches.add(arch)
580 versions.add(dbc.version)
581 arches_list = sorted(arches, key=utils.ArchKey)
582 arch_list = " ".join(arches_list)
583 version_list = " ".join(sorted(versions, reverse=True))
584 if len(version_list) > max_version_len:
585 max_version_len = len(version_list)
586 if len(arch_list) > max_arch_len:
587 max_arch_len = len(arch_list)
588 if i[1]["note_state"]: 588 ↛ 589line 588 didn't jump to line 589, because the condition on line 588 was never true
589 note = " | [N]"
590 else:
591 note = ""
592 entries.append(
593 [
594 source,
595 binary,
596 version_list,
597 arch_list,
598 per_source[source]["processed"],
599 note,
600 last_modified,
601 maint,
602 distribution,
603 closes,
604 fingerprint,
605 sponsor,
606 changedby,
607 filename,
608 ]
609 )
611 # direction entry consists of "Which field, which direction, time-consider" where
612 # time-consider says how we should treat last_modified. Thats all.
614 # Look for the options for sort and then do the sort.
615 age = "h"
616 if "Queue-Report::Options::Age" in Cnf: 616 ↛ 617line 616 didn't jump to line 617, because the condition on line 616 was never true
617 age = Cnf["Queue-Report::Options::Age"]
618 if "Queue-Report::Options::New" in Cnf: 618 ↛ 620line 618 didn't jump to line 620, because the condition on line 618 was never true
619 # If we produce html we always have oldest first.
620 direction.append([6, -1, "ao"])
621 else:
622 if "Queue-Report::Options::Sort" in Cnf: 622 ↛ 623line 622 didn't jump to line 623, because the condition on line 622 was never true
623 for i in Cnf["Queue-Report::Options::Sort"].split(","):
624 if i == "ao":
625 # Age, oldest first.
626 direction.append([6, -1, age])
627 elif i == "an":
628 # Age, newest first.
629 direction.append([6, 1, age])
630 elif i == "na":
631 # Name, Ascending.
632 direction.append([0, 1, 0])
633 elif i == "nd":
634 # Name, Descending.
635 direction.append([0, -1, 0])
636 elif i == "nl":
637 # Notes last.
638 direction.append([5, 1, 0])
639 elif i == "nf":
640 # Notes first.
641 direction.append([5, -1, 0])
642 entries.sort(key=functools.cmp_to_key(sortfunc))
643 # Yes, in theory you can add several sort options at the commandline with. But my mind is to small
644 # at the moment to come up with a real good sorting function that considers all the sidesteps you
645 # have with it. (If you combine options it will simply take the last one at the moment).
646 # Will be enhanced in the future.
648 if "Queue-Report::Options::822" in Cnf: 648 ↛ 650line 648 didn't jump to line 650, because the condition on line 648 was never true
649 # print stuff out in 822 format
650 for entry in entries:
651 (
652 source,
653 binary,
654 version_list,
655 arch_list,
656 processed,
657 note,
658 last_modified,
659 maint,
660 distribution,
661 closes,
662 fingerprint,
663 sponsor,
664 changedby,
665 changes_file,
666 ) = entry
668 # We'll always have Source, Version, Arch, Mantainer, and Dist
669 # For the rest, check to see if we have them, then print them out
670 log.write("Source: " + source + "\n")
671 log.write("Binary: " + binary + "\n")
672 log.write("Version: " + version_list + "\n")
673 log.write("Architectures: ")
674 log.write((", ".join(arch_list.split(" "))) + "\n")
675 log.write("Age: " + time_pp(last_modified) + "\n")
676 log.write(
677 "Last-Modified: " + str(int(time.time()) - int(last_modified)) + "\n"
678 )
679 log.write("Queue: " + type + "\n")
681 (name, mail) = maint.split(":", 1)
682 log.write("Maintainer: " + name + " <" + mail + ">" + "\n")
683 if changedby:
684 (name, mail) = changedby.split(":", 1)
685 log.write("Changed-By: " + name + " <" + mail + ">" + "\n")
686 if sponsor:
687 log.write("Sponsored-By: %s@debian.org\n" % sponsor)
688 log.write("Distribution:")
689 for dist in distribution:
690 log.write(" " + dist)
691 log.write("\n")
692 log.write("Fingerprint: " + fingerprint + "\n")
693 if closes:
694 bug_string = ""
695 for bugs in closes:
696 bug_string += "#" + bugs + ", "
697 log.write("Closes: " + bug_string[:-2] + "\n")
698 log.write("Changes-File: " + os.path.basename(changes_file) + "\n")
699 log.write("\n")
701 total_count = len(queue.uploads)
702 source_count = len(per_source_items)
704 if "Queue-Report::Options::New" in Cnf: 704 ↛ 705line 704 didn't jump to line 705, because the condition on line 704 was never true
705 direction.append([6, 1, "ao"])
706 entries.sort(key=functools.cmp_to_key(sortfunc))
707 # Output for a html file. First table header. then table_footer.
708 # Any line between them is then a <tr> printed from subroutine table_row.
709 if len(entries) > 0:
710 table_header(type.upper(), source_count, total_count)
711 for entry in entries:
712 (
713 source,
714 binary,
715 version_list,
716 arch_list,
717 processed,
718 note,
719 last_modified,
720 maint,
721 distribution,
722 closes,
723 fingerprint,
724 sponsor,
725 changedby,
726 _,
727 ) = entry
728 table_row(
729 source,
730 version_list,
731 arch_list,
732 last_modified,
733 maint,
734 distribution,
735 closes,
736 fingerprint,
737 sponsor,
738 changedby,
739 )
740 table_footer(type.upper())
741 elif "Queue-Report::Options::822" not in Cnf: 741 ↛ exitline 741 didn't return from function 'process_queue', because the condition on line 741 was never false
742 # The "normal" output without any formatting.
743 msg = ""
744 for entry in entries:
745 (
746 source,
747 binary,
748 version_list,
749 arch_list,
750 processed,
751 note,
752 last_modified,
753 _,
754 _,
755 _,
756 _,
757 _,
758 _,
759 _,
760 ) = entry
761 if processed: 761 ↛ 762line 761 didn't jump to line 762, because the condition on line 761 was never true
762 format = "%%-%ds | %%-%ds | %%-%ds | %%s\n" % (
763 max_source_len,
764 max_version_len,
765 max_arch_len,
766 )
767 msg += format % (source, version_list, arch_list, processed)
768 else:
769 format = "%%-%ds | %%-%ds | %%-%ds%%s | %%s old\n" % (
770 max_source_len,
771 max_version_len,
772 max_arch_len,
773 )
774 msg += format % (
775 source,
776 version_list,
777 arch_list,
778 note,
779 time_pp(last_modified),
780 )
782 if msg:
783 print(type.upper())
784 print("-" * len(type))
785 print()
786 print(msg)
787 print(
788 (
789 "%s %s source package%s / %s %s package%s in total / %s %s package%s to be processed."
790 % (
791 source_count,
792 type,
793 plural(source_count),
794 total_count,
795 type,
796 plural(total_count),
797 total_pending,
798 type,
799 plural(total_pending),
800 )
801 )
802 )
803 print()
806################################################################################
809def main():
810 global Cnf
812 Cnf = utils.get_conf()
813 Arguments = [
814 ("h", "help", "Queue-Report::Options::Help"),
815 ("n", "new", "Queue-Report::Options::New"),
816 ("8", "822", "Queue-Report::Options::822"),
817 ("s", "sort", "Queue-Report::Options::Sort", "HasArg"),
818 ("a", "age", "Queue-Report::Options::Age", "HasArg"),
819 ("r", "rrd", "Queue-Report::Options::Rrd", "HasArg"),
820 ("d", "directories", "Queue-Report::Options::Directories", "HasArg"),
821 ]
822 for i in ["help"]:
823 key = "Queue-Report::Options::%s" % i
824 if key not in Cnf: 824 ↛ 822line 824 didn't jump to line 822, because the condition on line 824 was never false
825 Cnf[key] = ""
827 apt_pkg.parse_commandline(Cnf, Arguments, sys.argv)
829 Options = Cnf.subtree("Queue-Report::Options")
830 if Options["Help"]:
831 usage()
833 if "Queue-Report::Options::New" in Cnf: 833 ↛ 834line 833 didn't jump to line 834, because the condition on line 833 was never true
834 header()
836 queue_names = []
838 if "Queue-Report::Options::Directories" in Cnf: 838 ↛ 839line 838 didn't jump to line 839, because the condition on line 838 was never true
839 for i in Cnf["Queue-Report::Options::Directories"].split(","):
840 queue_names.append(i)
841 elif "Queue-Report::Directories" in Cnf: 841 ↛ 842line 841 didn't jump to line 842, because the condition on line 841 was never true
842 queue_names = Cnf.value_list("Queue-Report::Directories")
843 else:
844 queue_names = ["byhand", "new"]
846 if "Queue-Report::Options::Rrd" in Cnf: 846 ↛ 847line 846 didn't jump to line 847, because the condition on line 846 was never true
847 rrd_dir = Cnf["Queue-Report::Options::Rrd"]
848 elif "Dir::Rrd" in Cnf: 848 ↛ 849line 848 didn't jump to line 849, because the condition on line 848 was never true
849 rrd_dir = Cnf["Dir::Rrd"]
850 else:
851 rrd_dir = None
853 f = None
854 if "Queue-Report::Options::822" in Cnf: 854 ↛ 856line 854 didn't jump to line 856, because the condition on line 854 was never true
855 # Open the report file
856 f = sys.stdout
857 filename822 = Cnf.get("Queue-Report::ReportLocations::822Location")
858 if filename822:
859 f = open(filename822, "w")
861 session = DBConn().session()
863 for queue_name in queue_names:
864 queue = session.query(PolicyQueue).filter_by(queue_name=queue_name).first()
865 if queue is not None: 865 ↛ 868line 865 didn't jump to line 868, because the condition on line 865 was never false
866 process_queue(queue, f, rrd_dir)
867 else:
868 utils.warn("Cannot find queue %s" % queue_name)
870 if "Queue-Report::Options::822" in Cnf: 870 ↛ 871line 870 didn't jump to line 871, because the condition on line 870 was never true
871 f.close()
873 if "Queue-Report::Options::New" in Cnf: 873 ↛ 874line 873 didn't jump to line 874, because the condition on line 873 was never true
874 footer()
877################################################################################
880if __name__ == "__main__":
881 main()