1 from typing
import Dict
, List
2 from budget_sync
.write_budget_csv
import write_budget_csv
3 from bugzilla
import Bugzilla
6 from pathlib
import Path
7 from budget_sync
.money
import Money
8 from budget_sync
.util
import all_bugs
9 from budget_sync
.config
import Config
, ConfigParseError
, Milestone
10 from budget_sync
.budget_graph
import BudgetGraph
, BudgetGraphBaseError
, PaymentSummary
11 from budget_sync
.write_budget_markdown
import write_budget_markdown
15 parser
= argparse
.ArgumentParser(
16 description
="Check for errors in "
17 "Libre-SOC's style of budget tracking in Bugzilla.")
19 "-c", "--config", type=argparse
.FileType('r'),
20 required
=True, help="The path to the configuration TOML file",
21 dest
="config", metavar
="<path/to/budget-sync-config.toml>")
23 "-o", "--output-dir", type=Path
, default
=None,
24 help="The path to the output directory, will be created if it "
26 dest
="output_dir", metavar
="<path/to/output/dir>")
27 parser
.add_argument('--username', help="Log in with this username")
28 parser
.add_argument('--password', help="Log in with this password")
29 args
= parser
.parse_args()
31 with args
.config
as config_file
:
32 config
= Config
.from_file(config_file
)
33 except (IOError, ConfigParseError
) as e
:
34 logging
.error("Failed to parse config file: %s", e
)
36 logging
.info("Using Bugzilla instance at %s", config
.bugzilla_url
)
37 bz
= Bugzilla(config
.bugzilla_url
)
39 logging
.debug("logging in...")
40 bz
.interactive_login(args
.username
, args
.password
)
41 logging
.debug("Connected to Bugzilla")
42 budget_graph
= BudgetGraph(all_bugs(bz
), config
)
43 for error
in budget_graph
.get_errors():
44 logging
.error("%s", error
)
45 if args
.output_dir
is not None:
46 write_budget_markdown(budget_graph
, args
.output_dir
)
47 write_budget_csv(budget_graph
, args
.output_dir
)
48 summarize_milestones(budget_graph
)
51 def print_budget_then_children(indent
, nodes
, bug_id
):
52 """recursive indented printout of budgets
56 print("bug #%5d %s budget %7s excltasks %7s" % \
57 (bug
.bug
.id, ' ' * (indent
*4),
58 str(bug
.budget_including_subtasks
),
59 str(bug
.budget_excluding_subtasks
)))
61 for child
in bug
.immediate_children
:
62 if (str(child
.budget_including_subtasks
) == "0" and
63 str(child
.budget_excluding_subtasks
) == "0"):
65 print_budget_then_children(indent
+1, nodes
, child
.bug
.id)
68 def summarize_milestones(budget_graph
: BudgetGraph
):
69 for milestone
, payments
in budget_graph
.milestone_payments
.items():
70 summary
= PaymentSummary(payments
)
71 print(f
"{milestone.identifier}")
72 print(f
"\t{summary.total} submitted: "
73 f
"{summary.total_submitted} paid: {summary.total_paid}")
75 # and one to display people
76 for person
in budget_graph
.milestone_people
[milestone
]:
77 print(f
"\t{person.identifier}")
81 for milestone
, payments
in budget_graph
.milestone_payments
.items():
82 print("%s %d" % (milestone
.identifier
, milestone
.canonical_bug_id
))
83 print_budget_then_children(0, budget_graph
.nodes
,
84 milestone
.canonical_bug_id
)
87 if __name__
== "__main__":