2 from enum
import Enum
, auto
3 from io
import StringIO
4 from typing
import Any
, Callable
, Dict
, List
, Optional
5 from budget_sync
.budget_graph
import BudgetGraph
, Node
, PayeeState
, PaymentSummary
6 from pathlib
import Path
7 from budget_sync
.config
import Milestone
8 from budget_sync
.money
import Money
9 from budget_sync
.write_budget_markdown
import markdown_escape
12 def _budget_csv_row(budget_graph
: BudgetGraph
, milestone
: Milestone
, node
: Optional
[Node
]) -> Dict
[str, str]:
13 row_fns
: Dict
[str, Callable
[[Node
], Any
]] = {
14 'bug_id': lambda node
: node
.bug
.id,
15 'excl_subtasks': lambda node
: node
.budget_excluding_subtasks
,
16 'inc_subtasks': lambda node
: node
.budget_including_subtasks
,
17 'fixed_excl_subtasks': lambda node
: node
.fixed_budget_excluding_subtasks
,
18 'fixed_inc_subtasks': lambda node
: node
.fixed_budget_including_subtasks
,
19 'req_excl_subtasks': lambda node
: node
.submitted_excluding_subtasks
,
20 'paid_excl_subtasks': lambda node
: node
.paid_excluding_subtasks
,
22 milestone_people
= budget_graph
.milestone_people
[milestone
]
24 def handle_person(person
):
25 # need a nested function in order to create a new person variable
26 # for this iteration that can be bound to the lambdas
27 id = person
.identifier
29 id + " (planned amt)": lambda node
: node
.payment_summaries
[person
].total
,
30 id + " (req amt)": lambda node
: node
.payment_summaries
[person
].total_submitted
,
31 id + " (req date)": lambda node
: node
.payment_summaries
[person
].submitted_date
,
32 id + " (paid amt)": lambda node
: node
.payment_summaries
[person
].total_paid
,
33 id + " (paid date)": lambda node
: node
.payment_summaries
[person
].paid_date
,
35 for person
in milestone_people
:
37 row
= {k
: "" for k
in row_fns
.keys()}
40 for k
, fn
in row_fns
.items():
50 def _budget_csv_for_milestone(budget_graph
: BudgetGraph
, milestone
: Milestone
) -> str:
51 with
StringIO() as string_io
:
52 writer
= csv
.DictWriter(
54 _budget_csv_row(budget_graph
, milestone
, None).keys(),
57 for node
in budget_graph
.assigned_nodes_for_milestones
[milestone
]:
58 # skip uninteresting nodes
59 if len(node
.payments
) == 0 \
60 and node
.budget_excluding_subtasks
== 0 \
61 and node
.budget_including_subtasks
== 0:
63 row
= _budget_csv_row(budget_graph
, milestone
, node
)
65 return string_io
.getvalue()
68 def write_budget_csv(budget_graph
: BudgetGraph
,
70 output_dir
.mkdir(parents
=True, exist_ok
=True)
71 milestones
= budget_graph
.config
.milestones
72 csv_paths
: Dict
[Milestone
, Path
] = {}
73 for milestone
in milestones
.values():
74 csv_text
= _budget_csv_for_milestone(budget_graph
, milestone
)
75 csv_paths
[milestone
] = output_dir
.joinpath(
76 f
"{milestone.identifier}.csv")
77 csv_paths
[milestone
].write_text(csv_text
, encoding
="utf-8")
79 markdown_text
= "\n".join(f
"# {markdown_escape(milestone.identifier)}\n"
81 f
"[[!table format=csv file=\"{path!s}\"]]"
82 for milestone
, path
in csv_paths
.items())
83 output_dir
.joinpath("csvs.mdwn").write_text(
84 markdown_text
, encoding
="utf-8")