hackagent.reporting.report
Campaign run summaries, reports, goal listings, the matrix and comparisons.
Every function takes rows (:mod:hackagent.reporting.rows, the stored
:class:~hackagent.reporting.tallies.GoalOutcome) and returns the JSON body
of a route, so both servers answer identically.
iso
def iso(value: Optional[datetime]) -> Optional[str]
How every timestamp is rendered: datetime.isoformat().
children
def children(run: CampaignRun,
attack_runs: Iterable[AttackRun]) -> list[AttackRun]
The attack runs of run, in report order.
campaign_run_summary
def campaign_run_summary(run: CampaignRun, attack_runs: Iterable[AttackRun],
totals: Iterable[GoalTotal]) -> CampaignRunSummary
GET /run/{id}/summary: one campaign run's CampaignRunSummary.
attack_runs and totals may hold other campaign runs' rows too.
campaign_run_summaries
def campaign_run_summaries(
runs: Sequence[CampaignRun], attack_runs: Iterable[AttackRun],
totals: Iterable[GoalTotal]) -> list[CampaignRunSummary]
The items of a GET /run/summary page, in the order of runs.
is_previous
def is_previous(current: CampaignRun, candidate: CampaignRun) -> bool
Whether candidate may be the previous run of current: a
COMPLETED run of the same saved campaign (or, for a run of no saved
campaign, of no saved campaign with the same agent and name), created
before it.
pick_previous
def pick_previous(current: CampaignRun,
candidates: Iterable[CampaignRun]) -> Optional[CampaignRun]
The most recent run of candidates that :func:is_previous.
A backend may pass any superset of the matching runs, e.g. the
COMPLETED runs of the same campaign (or agent) created before
current, newest first, LIMIT 1 after filtering on the same keys.
previous_summary
def previous_summary(run: CampaignRun, attack_runs: Iterable[AttackRun],
totals: Iterable[GoalTotal]) -> dict[str, Any]
The previous object of a report.
scoped_runs
def scoped_runs(
run: CampaignRun, attack_runs: Iterable[AttackRun],
scope_run_id: Optional[str]
) -> tuple[list[AttackRun], list[AttackRun]]
(every attack run, the attack runs in scope); raises
:class:~hackagent.reporting.http.NotFound for a scope that is not one of
run's attack runs.
campaign_run_report
def campaign_run_report(
run: CampaignRun,
attack_runs: Iterable[AttackRun],
outcomes: Iterable[GoalOutcome],
*,
scope_run_id: Optional[str] = None,
previous: Optional[dict[str, Any]] = None) -> dict[str, Any]
GET /run/{id}/report.
outcomes are the stored goal outcomes of run (rows of other runs
are ignored); previous is :func:previous_summary of
:func:pick_previous, or None.
goal_listing
def goal_listing(run: CampaignRun,
attack_runs: Iterable[AttackRun],
outcomes: Iterable[GoalOutcome],
filters: GoalFilters,
*,
scope_run_id: Optional[str] = None) -> list[GoalSummary]
Every goal of GET /run/{id}/goals that passes filters, with its
per-attack outcomes; the server pages the list.
goal_matrix
def goal_matrix(run: CampaignRun, attack_runs: Iterable[AttackRun],
outcomes: Iterable[GoalOutcome]) -> dict[str, Any]
GET /run/{id}/matrix: every goal's verdict per attack run, columnar.
compare_runs
def compare_runs(current: CampaignRun,
current_attack_runs: Iterable[AttackRun],
current_outcomes: Iterable[GoalOutcome],
baseline: CampaignRun,
baseline_attack_runs: Iterable[AttackRun],
baseline_outcomes: Iterable[GoalOutcome]) -> dict[str, Any]
GET /run/{id}/compare: goals matched by exact text, each campaign
run's verdicts taken over its whole scope.