# 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](../guides/pipeline-quality.md) and [`PostLoadChecker`](../guides/pipeline-quality.md#postloadchecker-alias).

## Quick Start

### One-Liner Quality Check

The fastest way to check quality:

```python
from pycharter import check_quality

report = check_quality(
    contract={"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:

```python
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`

```python
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`

```python
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` | `MetadataStoreClient` | Connected metadata 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`

```python
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](../guides/data-profiling.md) for the full profile structure.

---

## QualityCheckOptions Presets

Instead of configuring every option, use a preset:

```python
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:

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

---

## QualityCheck Class

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

```python
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

::: pycharter.quality.QualityCheck
    options:
      show_root_heading: true
      show_source: true

## QualityThresholds

::: pycharter.quality.QualityThresholds
    options:
      show_root_heading: true
      members:
        - min_overall_score
        - max_violation_rate
        - min_completeness
        - min_accuracy

## QualityCheckOptions

::: pycharter.quality.QualityCheckOptions
    options:
      show_root_heading: 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

```python
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

```python
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

```python
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

- [Quality Monitoring Tutorial](../tutorials/quality-monitoring.md) — full walkthrough
- [Data Profiling Guide](../guides/data-profiling.md) — profile structure and drift detection
- [Pipeline Quality Checks](../guides/pipeline-quality.md) — column/dataset-level checks with `PostLoadChecker`
- [Contract vs Pipeline Quality](../getting-started/data-quality.md) — when to use each
- [Metadata Store](metadata-store.md) — storing schemas and quality metrics
