Skip to content

Quality

PyCharter provides two quality systems. This page covers contract quality — row-level validation with scoring, violations, and profiling. For column/dataset-level checks after ETL loads, see Pipeline Quality and PostLoadChecker.

Quick Start

One-Liner Quality Check

The fastest way to check quality:

from pycharter import check_quality

report = check_quality(
    contract={"json_schema": {
        "version": "1.0.0",
        "properties": {"name": {"type": "string"}, "email": {"type": "string", "format": "email"}},
        "required": ["name", "email"]
    }},
    data=[
        {"name": "Alice", "email": "alice@example.com"},
        {"name": "", "email": "invalid"},
    ],
)

print(f"Score: {report.quality_score.overall_score:.1f}/100")
print(f"Valid: {report.valid_count}/{report.record_count}")

Quick Data Profiling

Profile a dataset without a contract:

from pycharter import profile_data

profile = profile_data([{"name": "Alice", "age": 30}, {"name": "Bob", "age": None}])
print(f"Records: {profile['record_count']}")
print(f"Age nulls: {profile['field_profiles']['age']['null_count']}")

Convenience Functions

check_quality

from pycharter import check_quality

report = check_quality(
    contract=contract_dict_or_file_path,
    data=records_or_file_path,
    options=None,  # Defaults to QualityCheckOptions.basic()
)
Parameter Type Description
contract dict \| str Contract dict or file path
data list[dict] \| str \| Callable Records, file path, or callable
options QualityCheckOptions \| None Options (defaults to basic())

Returns: QualityReport

check_quality_with_store

from pycharter import check_quality_with_store

report = check_quality_with_store(
    store=store,
    contract_name="user",
    contract_version="1.0.0",
    data=records,
)
Parameter Type Description
store ContractStoreClient Connected contract store
contract_name str Contract name in the store
contract_version str Contract version
data list[dict] \| str \| Callable Records, file path, or callable
options QualityCheckOptions \| None Options (defaults to basic())

Returns: QualityReport

profile_data

from pycharter import profile_data

profile = profile_data(data, fields=["name", "age"])  # or fields=None for all
Parameter Type Description
data list[dict] Records to profile
fields list[str] \| None Subset of fields (all if None)

Returns: dict with record_count, field_profiles, overall_stats

See the Data Profiling Guide for the full profile structure.


QualityCheckOptions Presets

Instead of configuring every option, use a preset:

from pycharter import QualityCheckOptions

opts = QualityCheckOptions.basic()       # Quick check
opts = QualityCheckOptions.strict()      # Gated check with thresholds
opts = QualityCheckOptions.monitoring()  # Recurring check with dedup
Preset Metrics Violations Profiling Thresholds Skip unchanged Dedup
basic() Yes Yes No No No Yes
strict() Yes Yes Yes Yes (defaults) No Yes
monitoring() Yes Yes Yes Yes (defaults) Yes Yes

You can also customize any preset:

opts = QualityCheckOptions.strict()
opts.sample_size = 1000  # Only check a sample

QualityCheck Class

For store-backed schemas, database persistence, or advanced control:

from pycharter import QualityCheck, QualityThresholds

check = QualityCheck(store=store)
report = check.run(
    schema_id="user_schema",
    data=records,
    thresholds=QualityThresholds(min_overall_score=95.0)
)

API Reference

QualityCheck

QualityCheck(store: ContractStoreClient | None = None, db_session: Session | None = None)

Contract-based quality scoring engine -- orchestrator-agnostic.

Validates data against a data contract, calculates quality scores, records violations, and optionally checks thresholds.

This class can be used: - Standalone (CLI, API, Python scripts) - Within orchestrators (Airflow, Prefect, Dagster) - Via API calls

For post-load structural checks (row count, null rate, uniqueness), see PostLoadChecker in pycharter.pipeline_generator.

Parameters:

Name Type Description Default
store ContractStoreClient | None

Optional contract store for retrieving contracts and storing violations.

None
db_session Session | None

Optional SQLAlchemy database session for persisting metrics and violations.

None
Source code in src/pycharter/quality/_check_engine.py
def __init__(
    self,
    store: ContractStoreClient | None = None,
    db_session: Session | None = None,
):
    """Initialize quality check.

    Args:
        store: Optional contract store for retrieving contracts and
            storing violations.
        db_session: Optional SQLAlchemy database session for persisting
            metrics and violations.
    """
    self.store = store
    self.db_session = db_session
    self.metrics = QualityMetrics()
    self.violation_tracker = ViolationTracker(store=store, db_session=db_session)
    self.profiler = DataProfiler()

run

run(contract_name: str | None = None, contract_version: str | None = None, contract: dict[str, Any] | DataContract | str | None = None, data: list[dict[str, Any]] | str | Callable[[], Any] | None = None, options: QualityCheckOptions | None = None) -> QualityReport

Run a quality check against a data contract.

Use (contract_name, contract_version) for store-based validation, or contract for in-memory.

Parameters:

Name Type Description Default
contract_name str | None

Contract name for store-based validation.

None
contract_version str | None

Contract version for store-based validation.

None
contract dict[str, Any] | DataContract | str | None

In-memory contract (dict, DataContract, or file path).

None
data list[dict[str, Any]] | str | Callable[[], Any] | None

Data source (list, file path, or callable).

None
options QualityCheckOptions | None

Optional quality check options.

None

Returns:

Type Description
QualityReport

QualityReport with scores, violations, and metadata.

Source code in src/pycharter/quality/_check_engine.py
def run(
    self,
    contract_name: str | None = None,
    contract_version: str | None = None,
    contract: dict[str, Any] | DataContract | str | None = None,
    data: list[dict[str, Any]] | str | Callable[[], Any] | None = None,
    options: QualityCheckOptions | None = None,
) -> QualityReport:
    """Run a quality check against a data contract.

    Use ``(contract_name, contract_version)`` for store-based validation,
    or ``contract`` for in-memory.

    Args:
        contract_name: Contract name for store-based validation.
        contract_version: Contract version for store-based validation.
        contract: In-memory contract (dict, DataContract, or file path).
        data: Data source (list, file path, or callable).
        options: Optional quality check options.

    Returns:
        QualityReport with scores, violations, and metadata.
    """
    if options is None:
        options = QualityCheckOptions()

    if isinstance(contract, DataContract):
        contract = contract.to_dict()

    options = self._resolve_thresholds(options, contract)

    schema_id = (
        f"{contract_name}:{contract_version}"
        if (contract_name and contract_version)
        else None
    )

    data_list = load_data(data)
    data_fingerprint = calculate_data_fingerprint(data_list)
    data_source = options.data_source or get_data_source(data)

    if options.sample_size and options.sample_size < len(data_list):
        import random

        data_list = random.sample(data_list, options.sample_size)

    if options.skip_if_unchanged and self.db_session and data_fingerprint:
        existing_metric = get_existing_metric(
            db_session=self.db_session,
            schema_id=schema_id,
            data_fingerprint=data_fingerprint,
            data_version=options.data_version,
        )
        if existing_metric:
            pass

    profile_data = None
    if options.include_profiling:
        profile_data = self.profiler.profile(data_list)

    validation_results = self._validate_data(
        contract_name=contract_name,
        contract_version=contract_version,
        contract=contract,
        data_list=data_list,
    )

    quality_score, field_metrics = self._calculate_scores(
        validation_results, options
    )

    violation_count = 0
    if options.record_violations:
        violation_count = record_violations(
            violation_tracker=self.violation_tracker,
            schema_id=schema_id,
            contract=contract,
            data_list=data_list,
            validation_results=validation_results,
            options=options,
        )

    threshold_breaches = self._check_thresholds(options, quality_score)

    report = self._build_report(
        schema_id=schema_id,
        contract_name=contract_name,
        contract_version=contract_version,
        quality_score=quality_score,
        field_metrics=field_metrics,
        violation_count=violation_count,
        data_list=data_list,
        validation_results=validation_results,
        threshold_breaches=threshold_breaches,
        data_fingerprint=data_fingerprint,
        data_source=data_source,
        profile_data=profile_data,
        options=options,
    )

    if self.db_session and quality_score:
        persist_quality_metrics(
            db_session=self.db_session,
            report=report,
            schema_id=schema_id,
            schema_version=report.schema_version,
            data_fingerprint=data_fingerprint,
            data_version=options.data_version,
            data_source=data_source,
            skip_if_unchanged=options.skip_if_unchanged,
        )

    return report

run_by_state

run_by_state(contract_name: str | None = None, contract_version: str | None = None, contract: dict[str, Any] | str | None = None, data: list[dict[str, Any]] | str | Callable[[], Any] | None = None, state_field: str = 'status', options: QualityCheckOptions | None = None) -> dict[str, QualityReport]

Run quality check segmented by state value.

Groups the data by the value of state_field, runs a separate quality check for each group, and returns a mapping from state value to :class:QualityReport.

Parameters:

Name Type Description Default
contract_name str | None

Contract name for store-based validation.

None
contract_version str | None

Contract version for store-based validation.

None
contract dict[str, Any] | str | None

In-memory contract dict or file path.

None
data list[dict[str, Any]] | str | Callable[[], Any] | None

Data source (list, file path, or callable).

None
state_field str

Field name to group records by (default "status").

'status'
options QualityCheckOptions | None

Optional quality check options (applied to each group).

None

Returns:

Type Description
dict[str, QualityReport]

Dict mapping each unique state value to its QualityReport.

Source code in src/pycharter/quality/_check_engine.py
def run_by_state(
    self,
    contract_name: str | None = None,
    contract_version: str | None = None,
    contract: dict[str, Any] | str | None = None,
    data: list[dict[str, Any]] | str | Callable[[], Any] | None = None,
    state_field: str = "status",
    options: QualityCheckOptions | None = None,
) -> dict[str, QualityReport]:
    """Run quality check segmented by state value.

    Groups the data by the value of *state_field*, runs a separate
    quality check for each group, and returns a mapping from state
    value to :class:`QualityReport`.

    Args:
        contract_name: Contract name for store-based validation.
        contract_version: Contract version for store-based validation.
        contract: In-memory contract dict or file path.
        data: Data source (list, file path, or callable).
        state_field: Field name to group records by (default ``"status"``).
        options: Optional quality check options (applied to each group).

    Returns:
        Dict mapping each unique state value to its ``QualityReport``.
    """
    data_list = load_data(data)

    grouped: dict[str, list[dict[str, Any]]] = {}
    for record in data_list:
        state = record.get(state_field, "_unknown")
        state_str = str(state) if state is not None else "_unknown"
        grouped.setdefault(state_str, []).append(record)

    results: dict[str, QualityReport] = {}
    for state_value, records in grouped.items():
        results[state_value] = self.run(
            contract_name=contract_name,
            contract_version=contract_version,
            contract=contract,
            data=records,
            options=options,
        )
    return results

QualityThresholds

QualityThresholds

Bases: BaseModel

Quality thresholds for alerting.

min_overall_score class-attribute instance-attribute

min_overall_score: float = 95.0

max_violation_rate class-attribute instance-attribute

max_violation_rate: float = 0.05

min_completeness class-attribute instance-attribute

min_completeness: float = 0.95

min_accuracy class-attribute instance-attribute

min_accuracy: float = 0.95

QualityCheckOptions

QualityCheckOptions

Bases: BaseModel

Options for quality checks.

basic classmethod

Create options for quick one-off quality checks.

Enables metrics and violation recording. Disables profiling and threshold checking for speed.

Returns:

Type Description
QualityCheckOptions

QualityCheckOptions configured for basic checks.

Source code in src/pycharter/quality/models.py
@classmethod
def basic(cls) -> QualityCheckOptions:
    """Create options for quick one-off quality checks.

    Enables metrics and violation recording. Disables profiling and
    threshold checking for speed.

    Returns:
        QualityCheckOptions configured for basic checks.
    """
    return cls(
        record_violations=True,
        calculate_metrics=True,
        check_thresholds=False,
        thresholds=None,
        include_field_metrics=True,
        include_profiling=False,
    )

strict classmethod

strict() -> QualityCheckOptions

Create options for gated quality checks.

Enables all features including profiling and threshold checking with default thresholds. Use this when quality must meet minimum standards before proceeding.

Returns:

Type Description
QualityCheckOptions

QualityCheckOptions configured for strict checks.

Source code in src/pycharter/quality/models.py
@classmethod
def strict(cls) -> QualityCheckOptions:
    """Create options for gated quality checks.

    Enables all features including profiling and threshold checking
    with default thresholds. Use this when quality must meet minimum
    standards before proceeding.

    Returns:
        QualityCheckOptions configured for strict checks.
    """
    return cls(
        record_violations=True,
        calculate_metrics=True,
        check_thresholds=True,
        thresholds=QualityThresholds(),
        include_field_metrics=True,
        include_profiling=True,
    )

monitoring classmethod

monitoring() -> QualityCheckOptions

Create options for scheduled/recurring quality checks.

Enables all features plus deduplication and skip-if-unchanged to avoid redundant work in monitoring pipelines.

Returns:

Type Description
QualityCheckOptions

QualityCheckOptions configured for monitoring.

Source code in src/pycharter/quality/models.py
@classmethod
def monitoring(cls) -> QualityCheckOptions:
    """Create options for scheduled/recurring quality checks.

    Enables all features plus deduplication and skip-if-unchanged
    to avoid redundant work in monitoring pipelines.

    Returns:
        QualityCheckOptions configured for monitoring.
    """
    return cls(
        record_violations=True,
        calculate_metrics=True,
        check_thresholds=True,
        thresholds=QualityThresholds(),
        include_field_metrics=True,
        include_profiling=True,
        skip_if_unchanged=True,
        deduplicate_violations=True,
    )

QualityReport

The report returned by QualityCheck.run() and the convenience functions:

Attribute Type Description
schema_id str Schema identifier
check_timestamp str ISO timestamp
quality_score QualityScore Quality metrics
field_metrics dict Per-field metrics
record_count int Total records
valid_count int Valid records
invalid_count int Invalid records
violation_count int Total violations
threshold_breaches list[str] Breached thresholds
passed bool All thresholds passed

QualityScore

Attribute Type Description
overall_score float 0-100 quality score
violation_rate float 0-1 violation ratio
completeness float 0-1 completeness ratio
accuracy float 0-1 accuracy ratio
field_scores dict[str, float] Per-field scores

Examples

One-Liner with Strict Thresholds

from pycharter import check_quality, QualityCheckOptions

report = check_quality(
    contract="contracts/user.yaml",
    data="data/users.json",
    options=QualityCheckOptions.strict(),
)

if not report.passed:
    print(f"Breaches: {report.threshold_breaches}")

Store-Based with Custom Options

from pycharter import QualityCheck, QualityCheckOptions, QualityThresholds

check = QualityCheck(store=store)
report = check.run(
    schema_id="user_schema",
    data=records,
    options=QualityCheckOptions(
        calculate_metrics=True,
        record_violations=True,
        check_thresholds=True,
        thresholds=QualityThresholds(min_overall_score=95.0),
        include_field_metrics=True,
        sample_size=1000,
    )
)

Quality Gate in a Pipeline

from pycharter import check_quality, QualityCheckOptions

report = check_quality(contract="contracts/orders.yaml", data=loaded_records,
                       options=QualityCheckOptions.strict())

if not report.passed:
    raise RuntimeError(f"Quality gate failed: {report.threshold_breaches}")

See Also