Skip to content

report.export

Report export utilities with sensitive-field guards.

Overview

Exports a DailyReport to JSON, CSV, or Parquet. All three formats apply the same privacy guard: a recursive check rejects any report containing sensitive keys before writing to disk.

Sensitive-field redaction

Every export function calls _check_no_sensitive_fields before writing. The following keys are forbidden at any nesting depth:

Key Reason
raw_keystrokes Raw keystroke data violates privacy policy
window_title_raw Unhashed window titles may contain private information
clipboard_content Clipboard contents may contain passwords or secrets
clipboard Alias for clipboard data

If any forbidden key is found (including in nested dicts), a ValueError is raised and no file is written.

Output formats

JSON (export_report_json)

export_report_json(report: DailyReport, path: Path) -> Path

Serializes the report via model_dump(exclude_none=True) and writes formatted JSON (2-space indent). None-valued optional fields (mapped_breakdown, context_switch_stats, flap_rate_raw, flap_rate_smoothed) are omitted from the output.

Creates parent directories if they do not exist. Returns the written path.

CSV (export_report_csv)

export_report_csv(report: DailyReport, path: Path) -> Path

Flattens the report's core_breakdown and mapped_breakdown into tabular rows with one row per label:

Column Description
date Calendar date from the report
label_type core or mapped
label Label name
minutes Minutes rounded to 2 decimal places

Core rows are sorted alphabetically by label name, followed by mapped rows (also sorted). Creates parent directories if needed.

Parquet (export_report_parquet)

export_report_parquet(report: DailyReport, path: Path) -> Path

Same schema as CSV (date, label_type, label, minutes) written as a Parquet file via pandas. Creates parent directories if needed.

Usage

from pathlib import Path
from taskclf.report.daily import build_daily_report
from taskclf.report.export import (
    export_report_json,
    export_report_csv,
    export_report_parquet,
)

report = build_daily_report(segments)

export_report_json(report, Path("artifacts/report.json"))
export_report_csv(report, Path("artifacts/report.csv"))
export_report_parquet(report, Path("artifacts/report.parquet"))

All three functions raise ValueError if the serialized report contains any key from the sensitive-fields blocklist.

See report.daily for DailyReport construction.

taskclf.report.export

Report export utilities: JSON, CSV, and Parquet output.

export_report_json(report, path)

Write report to a JSON file, rejecting sensitive keys.

Parameters:

Name Type Description Default
report DailyReport

A DailyReport instance to serialize.

required
path Path

Destination JSON file path.

required

Returns:

Type Description
Path

The path that was written.

Raises:

Type Description
ValueError

If the serialized output contains any key from the sensitive-fields blocklist.

Source code in src/taskclf/report/export.py
def export_report_json(report: DailyReport, path: Path) -> Path:
    """Write *report* to a JSON file, rejecting sensitive keys.

    Args:
        report: A ``DailyReport`` instance to serialize.
        path: Destination JSON file path.

    Returns:
        The *path* that was written.

    Raises:
        ValueError: If the serialized output contains any key from
            the sensitive-fields blocklist.
    """
    data = report.model_dump(exclude_none=True)
    _check_no_sensitive_fields(data)

    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2))
    return path

export_report_csv(report, path)

Write report as a flat CSV with one row per label.

Columns: date, label_type (core or mapped), label, minutes.

Parameters:

Name Type Description Default
report DailyReport

A DailyReport instance.

required
path Path

Destination CSV file path.

required

Returns:

Type Description
Path

The path that was written.

Raises:

Type Description
ValueError

If the report contains sensitive fields.

Source code in src/taskclf/report/export.py
def export_report_csv(report: DailyReport, path: Path) -> Path:
    """Write *report* as a flat CSV with one row per label.

    Columns: ``date``, ``label_type`` (``core`` or ``mapped``),
    ``label``, ``minutes``.

    Args:
        report: A ``DailyReport`` instance.
        path: Destination CSV file path.

    Returns:
        The *path* that was written.

    Raises:
        ValueError: If the report contains sensitive fields.
    """
    _check_no_sensitive_fields(report.model_dump(exclude_none=True))

    rows = _breakdown_to_rows(report)
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", newline="") as f:
        writer = csv.DictWriter(
            f, fieldnames=["date", "label_type", "label", "minutes"]
        )
        writer.writeheader()
        writer.writerows(rows)
    return path

export_report_parquet(report, path)

Write report as a Parquet file with one row per label.

Schema matches :func:export_report_csv.

Parameters:

Name Type Description Default
report DailyReport

A DailyReport instance.

required
path Path

Destination Parquet file path.

required

Returns:

Type Description
Path

The path that was written.

Raises:

Type Description
ValueError

If the report contains sensitive fields.

Source code in src/taskclf/report/export.py
def export_report_parquet(report: DailyReport, path: Path) -> Path:
    """Write *report* as a Parquet file with one row per label.

    Schema matches :func:`export_report_csv`.

    Args:
        report: A ``DailyReport`` instance.
        path: Destination Parquet file path.

    Returns:
        The *path* that was written.

    Raises:
        ValueError: If the report contains sensitive fields.
    """
    _check_no_sensitive_fields(report.model_dump(exclude_none=True))

    rows = _breakdown_to_rows(report)
    df = pd.DataFrame(rows)
    path.parent.mkdir(parents=True, exist_ok=True)
    df.to_parquet(path, index=False)
    return path