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