Use Data Quality Gates Locally in Python
Use this page when you want to validate records in your own Python code without sending them over the network for evaluation.
Here, local means the firewall definition runs inside your own Python runtime, wherever that runs — your machine, a job on a data platform, or a service in your pipeline — not only your local computer. Unlike the DQ Firewalls API, where you send records to Ataccama ONE for evaluation, DQ Gates let you run the firewall definition yourself in your own runtime, so no data leaves your environment.
Before you begin
Make sure you’ve reviewed prerequisites and limitations before continuing.
The exported firewall definition ZIP file contains the firewall definition together with other assets needed for local evaluation, such as reference data and metadata files. Make sure the ZIP file is accessible to the Python runtime at evaluation time.
How it works
The Python flow has four steps:
-
Install the ONE Python SDK with Python DQ Engine.
-
Authenticate to Ataccama ONE.
-
Download and export the firewall definition to a ZIP file.
-
Evaluate records locally in Python.
Installation
Install the ONE Python SDK and Python DQ Engine packages:
pip install ataccama_one-<version>-py3-none-any.whl ataccama_one_expressions-<version>-py3-none-any.whl
Configuration
Create an authenticated client
Create a client using the credentials from Authentication setup:
import os
from dotenv import find_dotenv, load_dotenv
from ataccama_one import Client, OpenIdConnectAuth
def get_env_var(name: str) -> str:
value = os.getenv(name)
if value is None:
raise ValueError(f"Environment variable {name} is required but not set.")
return value
load_dotenv(find_dotenv(".env"))
client = Client(
platform_version=get_env_var("ATACCAMA_PLATFORM_VERSION"),
url=get_env_var("ATACCAMA_INSTANCE_URL"),
auth=OpenIdConnectAuth(
client_id=get_env_var("ATACCAMA_CLIENT_ID"),
client_secret=get_env_var("ATACCAMA_CLIENT_SECRET"),
keycloak_host=get_env_var("ATACCAMA_KEYCLOAK_HOST"),
keycloak_realm=get_env_var("ATACCAMA_KEYCLOAK_REALM"),
),
)
If you run into SSL or certificate issues while connecting to Ataccama ONE, see Data Quality Gates Troubleshooting.
Fetch and export the firewall definition
Local Python execution works with exported DQ firewall definitions.
firewall_id = get_env_var("YOUR_DQ_FIREWALL_ID")
client.export_firewall_definition(firewall_id, save_to="firewall_definition.zip")
If you want to fetch multiple firewalls at once, use client.get_firewall_definitions(), which returns a Python iterator:
# Adjust `firewall_filter` to limit which firewalls are fetched from Ataccama ONE
firewall_filter = None
for firewall in client.get_firewall_definitions(filter=firewall_filter):
client.export_firewall_definition(
firewall.firewall_id,
save_to=f"{firewall.name}.zip",
)
For details about filtering fetched firewalls, see Firewall filtering.
Evaluate data locally
Load the exported firewall definition ZIP file into the local Python runtime. The ZIP contains the firewall definition together with other assets needed for local evaluation, such as reference data and metadata files.
After the ZIP is loaded, validate_records(…) evaluates the input records locally in Python without sending them to Ataccama ONE.
validate_records(…) takes a Python list, where each item is one record, and each record is a list of attribute values in the order configured on the firewall.
The following example loads a firewall and evaluates a small list of records written inline:
from ataccama_one.local import DqFirewall
firewall = DqFirewall.from_file("continent_firewall_definition.zip")
results = firewall.validate_records([
["invalid"],
["africa"],
["america"],
["prague"],
["south america"],
["europe"],
])
print(results.record_results)
In practice, your records usually come from a table or file rather than being written inline.
validate_records(…) requires a plain Python list, where each record is a list of attribute values, so prepare that list however suits your environment.
The following example reads the records from a CSV file using the standard library.
Because csv.reader also returns the header row, skip it before evaluation.
import csv
# Load your source data into a list of records, where each record is a
# list of attribute values. Here, the values are read from a CSV file.
with open("customers.csv", newline="") as file:
reader = csv.reader(file)
next(reader) # skip the header row
records = list(reader)
results = firewall.validate_records(records)
If you already work with pandas, you can read the data into a DataFrame instead. pandas skips the header automatically and can optimize the column data types.
import pandas as pd
# na_filter=False keeps empty values as empty strings instead of NaN.
input_frame = pd.read_csv("customers.csv", na_filter=False)
records = input_frame.values.tolist()
results = firewall.validate_records(records)
| The attribute values in each record must match the number, order, and data types of the input attributes configured on the firewall in Ataccama ONE. |
You get one result record back for every input record. The results are returned as a separate object and are not merged into your input data automatically (see Integrate DQ Gates into your data pipelines).
Assign record identifiers
Each result has a record_id.
By default, this is a generated value in the form record-0, record-1, and so on, based on the record’s position.
Results are always returned in the same order as the input records, so you can pair each result with its source record by index without any extra configuration.
If you’d prefer a meaningful identifier, such as a primary key or customer number, pass a record_identifier function to validate_records(…).
This function receives each record and its index and returns the string to use as that record’s record_id.
The following example uses a key column from the source data as the identifier:
# Return the value to use as each record's identifier.
# Adapt this to your data: here, the first value in each record is used.
def get_record_id(record, index):
return str(records[index][0])
results = firewall.validate_records(records, record_identifier=get_record_id)
|
This is only an example. Decide what uniquely identifies a record in your data, such as a primary key or customer number, and implement the function to return it. |
Results
validate_records(…) returns a DqFirewallResult object.
Its record_results attribute is a list with one entry per evaluated record.
Record result
Each entry in record_results is a DqFirewallRecordResult with the following attributes:
| Attribute | Description |
|---|---|
|
Identifier of the record.
Set this with the |
|
Overall DQ result for the record: |
|
Combined data quality score for the record, calculated from all of its rule instance results. |
|
List of per-rule results for the record (see Rule instance result). |
|
A record’s |
Rule instance result
Each entry in rule_instance_results is a DqFirewallRuleInstanceResult with the following attributes:
| Attribute | Description |
|---|---|
|
Label of the rule instance, as defined when the rule was applied to the attribute in the firewall. |
|
Result of this rule instance: |
|
Result for the DQ dimension the rule belongs to, for example, |
|
Explanation for the result, as defined in the rule implementation, for example, |
The result holds the same information as the DQ firewall API results, but uses Python naming: attribute names use underscores, and each rule result is identified by rule_instance_label rather than a rule instance ID.
DqFirewallResult(
record_results=[
DqFirewallRecordResult(
record_id='893794994',
overall_result='FAILED',
score=1000,
rule_instance_results=[
DqFirewallRuleInstanceResult(
rule_instance_label='Not empty (String)',
overall_result='PASSED',
dimension_result='COMPLETE',
explanation='OTHER',
),
DqFirewallRuleInstanceResult(
rule_instance_label='customername evaluation',
overall_result='FAILED',
dimension_result='INVALID',
explanation='INVALID_FORMAT',
),
DqFirewallRuleInstanceResult(
rule_instance_label='accuracy prefix',
overall_result='FAILED',
dimension_result='INVALID',
explanation='WRONG_REGIONAL_PREFIX',
),
],
),
],
)
Access the results in Python
Iterate over record_results and read the attributes you need.
Because the results are Python objects, you access values as attributes, for example, record_result.overall_result:
for record_result in results.record_results:
print(record_result.record_id, record_result.overall_result, record_result.score)
for rule_result in record_result.rule_instance_results:
print(" ", rule_result.rule_instance_label, rule_result.overall_result)
If you use pandas, you can flatten the results into a DataFrame for a more readable, tabular view.
import pandas as pd
flat_data = []
def mark_failed(input: str) -> str:
return f"🔴 {input}" if "FAILED" in input else f"🟢 {input}"
for record_result in results.record_results:
flat_data.append(
{
"record_id": record_result.record_id,
"overall_result": mark_failed(record_result.overall_result),
"score": record_result.score,
**{
f"{rule.rule_instance_label}": f"{mark_failed(rule.overall_result)} "
f"(dimension: {rule.dimension_result} - {rule.explanation})"
for rule in record_result.rule_instance_results
},
}
)
results_frame = pd.DataFrame(flat_data)
results_frame
Integrate DQ Gates into your data pipelines
|
The following examples are illustrative. The ONE Python SDK evaluates records and returns results; it does not read your input data or write your output. You decide how to read your source data and where to send valid, failed, or quarantined records, and you implement that logic in a way that suits your use case. |
Combine results with the original records
Pair each source record with its DQ result in a single list, so you keep the original values and the outcome together.
combined = []
for source_record, record_result in zip(records, results.record_results):
combined.append({
"record": source_record,
"record_id": record_result.record_id,
"overall_result": record_result.overall_result,
"score": record_result.score,
"rule_instance_results": record_result.rule_instance_results,
})
If you used the pandas approach for reading and viewing the records, combining the input with the results is a single call.
combined_frame = pd.concat([input_frame, results_frame], axis=1)
Filter valid records and quarantine failures
Route records based on their result: pass valid records to the next layer, and send failed records to a quarantine target together with the explanations of why they failed.
valid_records = []
quarantined_records = []
# Choose a score threshold that fits your quality requirements.
SCORE_THRESHOLD = 1000
for source_record, record_result in zip(records, results.record_results):
if record_result.overall_result == "PASSED":
valid_records.append(source_record)
elif record_result.score < SCORE_THRESHOLD:
failed_reasons = [
(rule.rule_instance_label, rule.explanation)
for rule in record_result.rule_instance_results
if rule.overall_result == "FAILED"
]
quarantined_records.append((source_record, record_result.score, failed_reasons))
# Pass valid_records on to the next layer.
# Write quarantined_records, with their explanations, to your quarantine target.
Write only results or only failures
Persist just the DQ results, or filter to only the records that failed, depending on what you need downstream.
# Only the DQ results, without the source data.
results_only = [
{
"record_id": record_result.record_id,
"overall_result": record_result.overall_result,
"score": record_result.score,
}
for record_result in results.record_results
]
# Only the records that failed, from the combined list.
failed_only = [row for row in combined if row["overall_result"] == "FAILED"]
Gate a pipeline on data quality
Calculate the pass rate for a batch and stop the pipeline if it falls below a threshold. You can also record the pass rate over time for monitoring and reporting.
total = len(results.record_results)
passed = sum(1 for r in results.record_results if r.overall_result == "PASSED")
pass_rate = (passed / total) * 100 if total else 0
# Set the minimum acceptable pass rate for your pipeline.
PASS_RATE_THRESHOLD = 95
if pass_rate < PASS_RATE_THRESHOLD:
raise RuntimeError(f"Data quality below threshold: {pass_rate:.1f}% passed")
# For monitoring, record pass_rate and the passed/total counts to your metrics store.
Keep firewall definitions up to date
When you change a firewall’s configuration in Ataccama ONE, for example, by adding or editing rules, re-export the firewall definition so that local evaluation uses the latest version. To refresh the ZIP file, re-run the export step (see Fetch and export the firewall definition).
Was this page useful?