---
title: "The DCC cleaning pipeline: Detect, Execute, Report"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{The DCC cleaning pipeline: Detect, Execute, Report}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
# The rule engine reads YAML, so only evaluate chunks when it is installed.
has_yaml <- requireNamespace("yaml", quietly = TRUE)
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = has_yaml
)
library(DCC)
```

DCC treats data cleaning as an auditable pipeline. Raw data is immutable, every
correction is driven by a declarative and versioned rule, and each change is
recorded at the cell level so the whole run can be reproduced from a manifest.
This vignette walks one small dataset through the full **Detect -> Execute ->
Report** workflow.

## A small dataset and a rule set

We start from a tiny response file. `S2` has an out-of-range score and `S3` is
missing an item.

```{r data}
csv <- tempfile(fileext = ".csv")
writeLines(c(
  "sid,score,q1,q2,q3",
  "S1,90,1,2,3",
  "S2,150,2,2,2",
  "S3,70,1,,3"
), csv)
```

Rules live outside the code as declarative YAML. Here we flag scores outside
`[0, 100]` and respondents missing more than a third of their items.

```{r rules}
rules_file <- tempfile(fileext = ".yaml")
writeLines(c(
  "checks:",
  "  - id: R001",
  "    type: range",
  "    variable: score",
  "    min: 0",
  "    max: 100",
  "    severity: fail",
  "  - id: D001",
  "    type: missing_items",
  "    items: [q1, q2, q3]",
  "    max_prop: 0.3"
), rules_file)
```

## Detect

`dcc_read()` loads the file (with encoding detection and structural
diagnostics) and `dcc_rules()` parses the rule set, recording its hash for the
audit trail. `dcc_detect()` then returns a structured `dcc_findings` object.

```{r detect}
x     <- dcc_read(csv)
rules <- dcc_rules(rules_file)
found <- dcc_detect(x, rules, id_var = "sid")
found
```

## Execute

`dcc_execute()` applies declarative actions mapped to rule IDs. Input data is
never mutated, the whole plan is validated before any change, and findings
without an explicit action are returned *unhandled* rather than silently
flagged or dropped. Below we blank the out-of-range score and flag the
incomplete respondent.

```{r execute}
res <- dcc_execute(
  x, found,
  actions = list(R001 = "set_na", Q_MISSING_ITEMS = "flag"),
  id_var = "sid"
)
dcc_cleaned(res)
```

Every change is captured in the cell-level audit log, with the old and new
value, the triggering rule, and the method.

```{r audit}
dcc_audit_log(res)
```

## Report

`dcc_report()` writes a self-contained HTML report -- a management summary plus
an audit layer that reconciles findings against changes -- with no external
rendering dependency.

```{r report}
report_path <- tempfile(fileext = ".html")
dcc_report(res, report_path)
file.exists(report_path)
```

## Trace and reproduce

`dcc_trace()` returns the full history of any cell in the cleaned data.

```{r trace}
dcc_trace(res, "S2", "score")
```

Finally, `dcc_manifest()` captures the input and rule hashes, the actions, and
the output hashes; `dcc_rerun()` re-executes the whole pipeline and verifies the
result is byte-identical (timestamps excluded).

```{r rerun}
check <- dcc_rerun(dcc_manifest(res))
check$reproduced
```

## Larger-than-memory input

For files that do not fit in memory, `dcc_detect_chunked()` streams the input
with an adaptive backend -- `data.table` for delimited text, `arrow` for
Parquet/Feather -- producing findings identical to the in-memory path for
record-local checks.

```{r chunked}
dcc_detect_chunked(csv, rules, chunk_size = 2L, id_var = "sid",
                   encoding = "UTF-8")
```
