---
lang: en-GB
title: "Analysing survey responses: running the plan"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Analysing survey responses: running the plan}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(surveyframe)
library(knitr)

# Tabulate analysis-plan results the same way the report template does.
results_table <- function(results) {
  g <- function(r, f) { v <- r[[f]]; if (is.null(v) || !length(v)) "" else as.character(v)[1] }
  df <- data.frame(
    RQ       = vapply(results, g, "", "block_id"),
    Question = vapply(results, g, "", "research_question"),
    Method   = vapply(results, g, "", "method"),
    Result   = vapply(results, g, "", "apa"),
    Effect   = vapply(results, g, "", "effect_label"),
    check.names = FALSE, stringsAsFactors = FALSE
  )
  kable(df, row.names = FALSE,
        col.names = c("RQ", "Research question", "Method", "Result (APA)", "Effect"),
        align = c("l", "l", "l", "r", "l"))
}
```

```{=html}
<style>
body { color: #1a1a2e; }
h1, h2, h3 { color: #1a1a2e; }
a { color: #0e7c7a; }
table { border-collapse: collapse; margin: 1em 0; }
table caption { caption-side: top; font-style: italic; color: #444; padding-bottom: .3em; }
th { border-top: 2px solid #1a1a2e; border-bottom: 1px solid #1a1a2e; padding: 6px 12px; }
td { padding: 5px 12px; border: none; }
tbody tr:last-child td { border-bottom: 2px solid #1a1a2e; }
/* WCAG 2.2 AA pass: darker link and syntax-token colours (at least
   4.5:1 on the #f7f7f7 code background), wrapped code lines instead of a
   keyboard-inaccessible scroll region, empty per-line anchors removed from
   the accessibility tree, and 24px minimum TOC link targets. */
code span.at { color: #576419; }
code span.dv, code span.fl, code span.bn { color: #276245; }
code span.co { color: #396a80; }
pre, pre code { white-space: pre-wrap; word-break: break-word; }
div.sourceCode { overflow: visible; }
pre.sourceCode a:empty { display: none; }
#TOC a { display: inline-block; min-height: 24px; }
</style>
```

The analysis in surveyframe is driven by the plan stored in the instrument.
Once responses are imported, scored, and checked, `run_analysis_plan()` runs
every research question in one pass and returns results formatted for reporting.

The worked-study vignette uses a published study whose data is private. This
vignette uses the bundled tourism demo, a synthetic dataset shaped around the
same digital marketing and tourism constructs, so the analysis runs end to end
without internet access or private data.

## Load the demo

```{r load}
demo      <- sframe_demo_data()
instr     <- demo$instrument
responses <- demo$responses

dim(responses)
```

## Import responses

Response data uses instrument item IDs as column names. Metadata columns are
declared explicitly. Use `strict = TRUE` to keep only known columns.

```{r import}
responses <- read_responses(
  demo$responses_path,
  instr,
  respondent_id = "respondent_id",
  submitted_at  = "submitted_at",
  meta_cols     = "started_at",
  strict        = TRUE
)

dim(responses)
```

## Missing data and quality

```{r screening}
mr <- missing_data_report(responses, instr)
kable(as.data.frame(mr), digits = 2,
      col.names = c("Variable", "Missing (n)", "Missing (%)", "Valid (n)"),
      caption = "Item-level missingness")

qr <- quality_report(
  responses, instr,
  respondent_id = "respondent_id",
  submitted_at  = "submitted_at",
  started_at    = "started_at"
)
# as.data.frame() gives the summary row.
qr_summary <- as.data.frame(qr)
quality_summary <- data.frame(
  Metric = c("Respondents", "Items", "Flagged for review", "Flag rate"),
  Value  = c(qr_summary$n_respondents, qr_summary$n_items, qr_summary$n_flagged,
             sprintf("%.1f%%", 100 * qr_summary$flag_rate)),
  stringsAsFactors = FALSE
)
kable(quality_summary, align = c("l", "r"), caption = "Quality screening summary")
```

## Score scales

`run_analysis_plan()` scores the scales for you, but scoring once up front lets
you inspect the construct scores and run the assumption checks below.

```{r score}
scored    <- score_scales(responses, instr, keep_items = TRUE, keep_meta = TRUE)
scale_ids <- vapply(instr$scales, function(x) x$id, character(1))
score_cols <- intersect(scale_ids, names(scored))

kable(head(scored[, score_cols, drop = FALSE]), digits = 2,
      caption = "Scale scores, first respondents")
```

The scale-score distributions show the shape of each construct before the plan
runs.

```{r score-distributions, fig.width = 7, fig.height = 3, fig.align = "left", fig.alt = "Histograms of the scored scale distributions, one panel per scale"}
op <- par(mfrow = c(1, length(score_cols)), mar = c(4, 3, 2, 1))
for (s in score_cols) {
  v <- scored[[s]]; v <- v[is.finite(v)]
  hist(v, col = "#16B3B1", border = "white", main = s,
       xlab = "Score", ylab = "")
}
par(op)
```

## Check assumptions before the plan

`assumption_report()` reports the checks a technique relies on, such as residual
normality, variance inflation, and influence for a regression.

```{r assumptions}
assumption_report(
  scored,
  predictors = c("digital_marketing", "service_quality", "sustainability"),
  outcome    = "satisfaction"
)
```

## Define the plan

Each block binds a research question to a technique and to the variables that
fill each role. A correlation expects `x` and `y`. A regression expects
`predictors` and a `dependent` variable. A group comparison expects a `group`
and an `outcome`.

```{r plan}
sf_plan(instr) <- list(
  list(id = "RQ1",
       research_question = "Is digital marketing perception associated with satisfaction?",
       family = "association", method = "correlation_pearson",
       roles = list(x = "digital_marketing", y = "satisfaction"),
       options = list(alpha = 0.05)),
  list(id = "RQ2",
       research_question = "Do the three perception scales predict satisfaction?",
       family = "regression", method = "regression_linear",
       roles = list(predictors = c("digital_marketing", "service_quality", "sustainability"),
                    dependent = "satisfaction"),
       options = list(alpha = 0.05)),
  list(id = "RQ3",
       research_question = "Do first-time and repeat visitors differ in behavioural intention?",
       family = "group_comparison", method = "mann_whitney",
       roles = list(group = "visit_type", outcome = "behavioural_intention"),
       options = list(alpha = 0.05))
)
```

## Run the plan

```{r run}
results <- run_analysis_plan(responses, instr)
results_table(results)
```

Pass `plots = TRUE` to attach a brand-styled `ggplot2` chart to each result
that supports one (descriptive, correlation, chi-square, and regression
blocks). The chart sits in `$plot` alongside the existing `$table` and
`$apa` elements.

```{r run-plots, eval = requireNamespace("ggplot2", quietly = TRUE), fig.alt = "Chart attached to the first analysis-plan result by run_analysis_plan with plots enabled"}
results_plots <- run_analysis_plan(responses, instr, plots = TRUE)
results_plots[[1]]$plot
```

## Read a single result

Each result holds more than the printed line. It carries the APA statistic, the
effect-size label where the technique reports one, a writing prompt, and the
references that support the technique.

```{r single-result}
rq1 <- results[[1]]

rq1$apa
rq1$effect_label
rq1$prompt
unlist(rq1$citations)
```

## Render the results report

`render_results()` writes a self-contained HTML report with one section per
research question, each holding the APA result, the writing prompt, a space for
the interpretation, and a reference list compiled from the techniques used.

```{r render, eval = FALSE}
render_results(results, instr, output_file = "results.html", citation_format = "apa")
```

## In SurveyStudio

SurveyStudio runs the same plan. Open it on the analysis screen, upload the
responses, and the Analysis Plan screen runs the saved plan and shows a table of
each question with its method and APA result. The full report is produced on the
Export screen.

```{r gui, eval = FALSE}
launch_studio(
  instrument     = instr,
  responses      = responses,
  screen         = "analysis",
  launch.browser = FALSE
)
```
