# Executing a checklist on a run

Execute a published [checklist](https://dev.nominal.io/platform/reference/glossary.md#checklist "A versioned set of checks that you execute on runs or assets to evaluate test data during or after a test.") on a run that has ended, so each of its checks is evaluated against the run’s data. To build a checklist, or to execute one live on an asset, see [Automated checks](https://dev.nominal.io/platform/data-model/paradigms/stories/automated-checks.md).

## In code

**Python**

```python
from nominal.core import EventType, NominalClient

client = NominalClient.from_profile("default")
checklist = client.get_checklist("<ri.scout.instance.check-collection.identifier>")  # a published checklist

review = checklist.execute("<ri.scout.instance.run.identifier>").poll_for_completion()
violations = [e for e in review.get_events() if e.type == EventType.ERROR]
print(f"{len(violations)} violations", review.nominal_url)
```

**Rust**

```rust
use std::{collections::BTreeSet, sync::Arc, time::Duration};
use nominal::streaming::api::clients::event::{AsyncEventService, AsyncEventServiceClient};
use nominal::streaming::api::clients::scout::datareview::api::{AsyncDataReviewService, AsyncDataReviewServiceClient};
use nominal::streaming::api::objects::scout::datareview::api::{AutomaticCheckEvaluationState as State, BatchInitiateDataReviewRequest, CreateDataReviewRequest};
use nominal::streaming::api::objects::{event::EventType, scout::{rids::api::ChecklistRid, run::api::RunRid}};
use nominal::streaming::client::{async_conjure_client, conjure::http::client::{AsyncService, ConjureRuntime}};
use nominal::streaming::prelude::{BearerToken, ResourceIdentifier};

let config = nominal::Config::load()?;
let profile = config.get_profile("default").ok_or("no profile named default")?;
let token = BearerToken::new(profile.token())?;
let api = async_conjure_client("data-review", profile.base_url().parse()?).map_err(nominal::Error::from)?;
let runtime = Arc::new(ConjureRuntime::default());
let reviews = AsyncDataReviewServiceClient::new(api.clone(), &runtime);

// Execute the latest version of the checklist on the run
let request = BatchInitiateDataReviewRequest::builder()
    .push_requests(CreateDataReviewRequest::new(
        RunRid(ResourceIdentifier::new("<ri.scout.instance.run.identifier>")?),
        ChecklistRid(ResourceIdentifier::new("<ri.scout.instance.check-collection.identifier>")?),
    ))
    .build();
let review_rid = reviews.batch_initiate(&token, &request).await.map_err(nominal::Error::from)?.rids()[0].clone();

// Finished when no check is still pending or executing
let review = loop {
    let review = reviews.get(&token, &review_rid).await.map_err(nominal::Error::from)?;
    if !review.check_evaluations().iter().any(|c| matches!(c.state(), State::PendingExecution(_) | State::Executing(_))) {
        break review;
    }
    tokio::time::sleep(Duration::from_secs(2)).await;
};

// Each check that created events lists them
let event_rids: BTreeSet<_> = review.check_evaluations().iter()
    .filter_map(|c| match c.state() { State::GeneratedAlerts(s) => Some(s.event_rids().clone()), _ => None })
    .flatten()
    .collect();
let events = AsyncEventServiceClient::new(api, &runtime)
    .batch_get_events(&token, &event_rids).await.map_err(nominal::Error::from)?;
let violations = events.iter().filter(|e| *e.type_() == EventType::Error).count();
println!("{violations} violations");
```

## In the Nominal app

1. On the run’s page, open the **Checklist executions** tab and click **EXECUTE CHECKLIST**.
2. Select the checklist and click **EXECUTE**.

To execute a checklist on several runs at once, open the published checklist, click **EXECUTE ON…**, choose **Run**, select the runs, and click **EXECUTE CHECKLIST ON RUN**. Only runs with an end time are listed.
