{# Single unified scan form. Save (project / region / dbt-only default) and run a scan on the chosen lookback in one submit - the only button is "Run scan". The page also surfaces the latest stats strip, an ADC pill, scan history, and quick links to the dashboard / settings. #} {% extends "layout.html" %} {% block content %}

Configuration

{{ adc_state.label }}
{# Project / Region surfaced in the global header strip; subtitle removed to avoid repeating the same orientation context on every page. #}
Last scan: {{ cache_stats.last_scan_at | timeago }}
{% if has_scan_data %} View dashboard {% endif %} Settings {% if active_principal %} Signed in as {{ active_principal }} {% endif %}
{# Auto-refresh while a scan is in flight so the user sees the running → succeeded transition without having to reload manually. ``scan_in_progress`` is set by the route based on the most-recent ScanRun row's status; it drives both the meta refresh AND the form's disabled state below. #} {% if scan_in_progress %} {% endif %} {% if scan_in_progress %} {# Spec 151 polish - replace the static "Scan started - running in the background" banner with a 4-stage progress bar driven by ``scan_current_phase``. Phases are sequential; once we enter phase N, phases 1..N are marked complete (green). The active phase pulses. Failed scans land back here with status='failed' and current_phase set to whichever stage was last entered. #} {% set phases = [ ('fetching', 'Fetching from BigQuery', 'INFORMATION_SCHEMA jobs / columns / storage'), ('persisting', 'Persisting jobs', 'Writing rows to the local SQLite cache'), ('detection', 'Running detection', 'Cost / performance / SQL-rewrite rules'), ('recommendations', 'Building recommendations','Table-level partition + cluster + BigQuery second opinion'), ] %} {% set _phase_keys = phases | map(attribute=0) | list %} {% set _active_index = _phase_keys.index(scan_current_phase) if scan_current_phase in _phase_keys else -1 %} {# Linear progress bar - one filled track that grows left→right as phases complete, with 4 numbered step markers anchored on top. Active phase pulses; completed phases turn green. Labels live under each marker. #} {% set _completed = (_active_index + 1) if _active_index >= 0 else 0 %} {% set _total = phases | length %} {% set _pct = (_completed / _total * 100) | round(0, 'floor') %}

Scan in progress

Page auto-refreshes every 5 seconds.

{{ scan_current_phase | replace('_', ' ') | title if scan_current_phase else 'Starting' }} · {{ _completed }} / {{ _total }}
{# Track #}
{# Filled portion - proportional to completed stages. #}
{# Step markers anchored on the track. #}
    {% for key, title, sub in phases %} {% set i = loop.index0 %} {% set is_done = _active_index > i %} {% set is_active = _active_index == i %}
  1. {% if is_done %} {% elif is_active %} {{ loop.index }} {% else %} {{ loop.index }} {% endif %}

    {{ title }}

    {{ sub }}

  2. {% endfor %}
{% elif scan_banner %}

{{ scan_banner.headline }}

{% if scan_banner.detail %}

{{ scan_banner.detail }}

{% endif %}
{% if scan_banner.level == 'success' and has_scan_data %} View dashboard {% endif %}
{% endif %} {% if errors and errors.get('_general') %}
{{ errors['_general'] }}
{% endif %} {# ── Unified Run-scan form: saves config + kicks off a scan ── #}
{# Spec 150 - optional billing project. Shown FIRST to match the onboarding-wizard order (Step 2 Billing comes before Step 3 Audit Target). When set to a project different from gcp_project_id, BigQuery scan jobs run in (and are billed to) this project; SQL still reads JOBS_BY_PROJECT of the analysed project. Leave blank to bill the analysed project. #}

When set to a different project than the analysed project below, governor-audit's BigQuery scans run in (and are billed to) this project. Caller needs bigquery.jobs.create on this project and bigquery.resourceViewer (or equivalent JOBS_BY_PROJECT read) on the analysed project.

{% if errors and errors.get('billing_project_id') %}

{{ errors['billing_project_id'] }}

{% endif %}
{% if gcp_projects %} {% for project in gcp_projects %} {% endfor %} {% endif %} {% if errors and errors.get('gcp_project_id') %}

{{ errors['gcp_project_id'] }}

{% endif %}
{# Datalist behaves as a typeahead in every modern browser: the user sees the full list on focus, and typing narrows it. #} {# Multi-regions #} {# US #} {# North + South America #} {# Europe #} {# Middle East + Africa #} {# Asia #} {# Oceania #} {% if errors and errors.get('bigquery_region') %}

{{ errors['bigquery_region'] }}

{% endif %}
BigQuery query preview
{{ scan_query_preview.sql }}
{% if scan_in_progress %} {% else %} {% endif %}
{# ── Scan history ── #} {% from "components/_section_title.html" import section_title %}
{{ section_title( "Scan history", description="Every scan run against this configuration. Each row shows the lookback window queried and the number of BigQuery jobs ingested.", aside=((scans | length) ~ " most recent") ) }}
{% if scans %}
{% for scan in scans %} {% endfor %}
Started Status Project Region Lookback Jobs dbt-only
{{ scan.started_at.strftime("%Y-%m-%d %H:%M") }} UTC {% if scan.status == "succeeded" %} succeeded {% elif scan.status == "failed" %} failed {% elif scan.status == "running" %} running {% else %} {{ scan.status }} {% endif %} {{ scan.gcp_project_id or "-" }} {{ scan.bigquery_region or "-" }} {{ scan.requested_lookback_days }}d {% if scan.fetched_job_count is not none %} {{ "{:,}".format(scan.fetched_job_count) }} {% else %} - {% endif %} {% if scan.dbt_only_filter %} on {% else %} - {% endif %}
{% else %}

No scans yet. Use the form above to run your first scan.

{% endif %}
{% endblock %}