Samples
Basic Concepts
A quality verification is a test run based on a specific quality specification. A quality specification holds one or more quality conditions. Each quality condition represents a test algorithm configured for one or more specific datasets.
In order to execute a verification the prosuite.verification.service class is used to create the communication channel to the server and to start the verification of a specific quality specification.
service = prosuite.verification.Service(host_name='localhost', port_nr=5151)
service.verify(specification=my_specification)
A quality verification can be based on an XML specification
(exported from the ProSuite Data Dictionary) or on a specification created in code,
containing a list of quality conditions. Quality conditions are created with the factory class
prosuite.factories.quality_conditions.Conditions
which contains all available test algorithms.
Intellisense provides the method parameters and help/docstrings but for an overview over the available tests
or a general introduction, refer to the ProSuite HTML help or the ‘Quick Reference’.
Before running the verification in Python, make sure the server is running, for example by starting prosuite-qa-microservice.exe. By default the communication channel is http://localhost:5151.
Verify a specification created in code
The Specification class holds a set of conditions that can be configured programmatically.
Define the Data Model (= workspace)
Create the Datasets (= feature classes, tables with optional filter) in the model
Create a Service instance containing the connection properties
Define the specification: create a prosuite.quality instance
Create Condition instances using the static Conditions class and add them to the specification
Optionally define the verification perimeter
Optionally define the verification output directory
Execute the verification
1 import prosuite as ps
2
3 model = ps.Model("TopoModel", "D:\Test Data\ExtractStGallen.gdb") # From "ProSuite Documentation / SampleData.zip"
4 datasets = [ps.Dataset("TLM_FLIESSGEWAESSER", model),
5 ps.Dataset("TLM_STRASSE", model)]
6
7 service = ps.Service(host_name='localhost', port_nr=5151) # You might want to change this to the host and port of your ProSuite installation
8
9 simpleSpecification = ps.Specification(
10 name='MinimumLengthSpecification',
11 description='A very simple quality specification checking feature and segment length of roads and rivers')
12
13 for dataset in datasets:
14 simpleSpecification.add_condition(ps.Conditions.qa_min_length_0(dataset, limit=10, is3_d=False))
15 simpleSpecification.add_condition(ps.Conditions.qa_segment_length_0(dataset, 1.5, False))
16
17 envelope = ps.EnvelopePerimeter(x_min=2750673, y_min=1215551, x_max=2765845, y_max=1206640)
18
19 out_dir = 'C:/temp/verification_output' # You might want to change this to a directory that exists on your system, also make sure no Issue.gdb exists in this directory
20
21 verification_responses = service.verify(specification=simpleSpecification, output_dir=out_dir, perimeter=envelope)
22
23 for verification_response in verification_responses:
24 print(verification_response.message)
Response Messages
1Creating external issue file geodatabase
2Starting quality verification using quality specification MinimumLengthSpecification with verification tile size 5000Extent: 15172 x 8911
3X-Min: 2750673
4Y-Min: 1206640
5X-Max: 2765845
6Y-Max: 1215551
7
8Verifying quality conditions per cached tiles (container tests)
9Processing tile 0 of 8: XMin: 2’750’673.00 YMin: 1’206’640.00 XMax: 2’755’673.00 YMax: 1’211’640.00
10Processing tile 1 of 8: XMin: 2’755’673.00 YMin: 1’206’640.00 XMax: 2’760’673.00 YMax: 1’211’640.00
11Processing tile 2 of 8: XMin: 2’760’673.00 YMin: 1’206’640.00 XMax: 2’765’673.00 YMax: 1’211’640.00
12Processing tile 3 of 8: XMin: 2’765’673.00 YMin: 1’206’640.00 XMax: 2’765’845.00 YMax: 1’211’640.00
13Processing tile 4 of 8: XMin: 2’750’673.00 YMin: 1’211’640.00 XMax: 2’755’673.00 YMax: 1’215’551.00
14Processing tile 5 of 8: XMin: 2’755’673.00 YMin: 1’211’640.00 XMax: 2’760’673.00 YMax: 1’215’551.00
15Processing tile 6 of 8: XMin: 2’760’673.00 YMin: 1’211’640.00 XMax: 2’765’673.00 YMax: 1’215’551.00
16Processing tile 7 of 8: XMin: 2’765’673.00 YMin: 1’211’640.00 XMax: 2’765’845.00 YMax: 1’215’551.00
17Quality verification finishedNumber of verified datasets: 2.
18Number of verified conditions: 4
19No category
20QaMinLength(0) TLM_FLIESSGEWAESSER - errors: 290
21QaMinLength(0) TLM_STRASSE - errors: 974
22QaSegmentLength(0) TLM_FLIESSGEWAESSER - errors: 1733
23QaSegmentLength(0) TLM_STRASSE - errors: 1939
24Warning count: 0
25Error count: 4’936
26The quality specification is not fulfilled
27
28Issues written to C:\temp\verification_output\Issues.gdb
29
30Verification report written to C:\temp\verification_output\verification.xml
31Html report:
32C:\temp\verification_output\verification.html
33Quality specification report:
34C:\temp\verification_output\qualityspecification.html
Control verification with the Issue class
The Issue class can be used to control the verification process. It can be used to stop the verification process when a certain issue condition is met. A sample Python script can be found in the ProSuite Python Samples repository.
1issue_allowable = True
2
3 for verification_response in verification_responses:
4 if len(verification_response.issues) > 0:
5 for issue in verification_response.issues:
6 # Demo Prints
7
8 # print(issue.description)
9 # print(issue.involved_objects)
10 # print(issue.geometry)
11 # print(issue.issue_code)
12 # print(issue.allowable)
13 # print(issue.stop_condition)
14
15 if issue.allowable is False:
16 print(f"Not allowed issue met: {issue.description} in {issue.involved_objects[0].table_name}")
17 print("Stopping verification")
18 issue_allowable = False
19 break
20
21 if issue_allowable is False:
22 break
Verification using XML Specification
Create a Service instance. In this example the service runs on a remote server machine.
Define the quality specification: create a XmlSpecification instance from a specification.qa.xml file.
Define the verification output directory
Optionally define the verification perimeter
Execute the verification
1import prosuite
2
3service = prosuite.verification.Service(host_name='arcgis_server', port_nr=5151)
4
5xml_file = "\\share\QA\specifications\road_specification.qa.xml"
6sde_file = "\\share\connection_files\production_QA_version.sde"
7
8# Data source replacements: see notes below
9xml_spec = prosuite.XmlSpecification(specification_file=xml_file,
10 specification_name="Produktionsunterstuetzung",
11 data_source_replacements=[["ProductionModel", sde_file]])
12
13out_dir = '\\share\QA\results\verification_output'
14
15for verification_response in service.verify(specification=xml_spec, output_dir = out_dir):
16 print(verification_response.message_level)
17 print(verification_response.service_call_status)
18 print(verification_response.message)
Notes:
Directories: The specified paths must be accessible by the server, hence use UNC-paths.
Data Source Replacements: The datasets in the XML specifications use a Workspace ID as reference to the database. The Workspace ID is defined at the bottom of the XML file. Data source replacements are provided in the format of string pairs [<workspace_id>, <path_to_geodatabase>].
Examples:
[“TLM_Data”, “C:/full/path/to/connection.sde”]
[“ERM_Data”, “C:/full/path/to/data.gdb”]
For each Workspace ID defined at the very end of the XML file that should be replaced, provide a path to a file geodatabase or an sde file.
If no data source replacement is provided, a connectionString must be defined for the respective workspace in the XML. This is achieved by exporting the full workspace information when exporting the XML specification in the data dictionary editor by checking the Option ‘Export workspace connection information’.
If one or more datasource replacements are provided they will be used to open the referenced datasets in the XML by using the specified geodatabase for the respective workspace id.
For more details see the documentation of the verification service: prosuite.verification.Service
.
Get specification names from XmlSpecification
1import prosuite
2xml_file = 'C:/temp/road_specification.qa.xml'
3names_list = prosuite.XmlSpecification.get_specification_names(xml_file)
4print(names_list)
Verification using DDX Specification
Using the DDX Specification, you can directly run quality verification defined in your Data Dictionary Editor through the Python API. This approach allows you to leverage predefined quality specifications and dataset configurations, making it simple to execute targeted data quality checks programmatically based on the settings in your data dictionary.
Create a Service instance. In this example, the service runs on a local server machine.
Define the quality specification by creating a DdxSpecification instance using the ddx_id and the project_short_name. These identifiers can be found in the Data Dictionary Editor.
Define the verification parameters, including any specific dataset ID and object IDs to verify. Both the dataset ID and object IDs can also be checked in the Data Dictionary Editor.
Optionally define advanced parameters such as update_issues_in_verified_model and save_verification_statistics.
Define the verification output directory.
Execute the verification.
1import prosuite
2
3service = prosuite.Service(host_name='localhost', port_nr=5151)
4
5ddx_id = 9
6project_short_name = "..."
7specification = prosuite.DdxSpecification(ddx_id=ddx_id, project_short_name=project_short_name)
8
9road_dataset_id = 138
10road_object_ids_to_verify = [3606433]
11rail_dataset_id = 141
12rail_object_ids_to_verify = [2105461, 2105452, 2105593]
13
14params = prosuite.VerificationParameters(user_name="UJR")
15
16# Optionally provided object ID lists per dataset (use dataset ID from data dictionary)
17params.add_objects_to_verify(road_dataset_id, road_object_ids_to_verify)
18params.add_objects_to_verify(rail_dataset_id, rail_object_ids_to_verify)
19
20# Optionally, write the issue to the error datasets in the data model
21params.update_issues_in_verified_model = True
22
23# Optionally, save the verification statistics to the respective data dictionary table (requires DDX schema 1.0.0.1)
24params.save_verification_statistics = True
25
26out_dir = 'C:/temp/verification_output'
27extent = None # Optionally define the verification extent
28
29for verification_response in service.verify(specification=specification, output_dir=out_dir, perimeter=extent, parameters=params):
30 print(verification_response.message_level)
31 print(verification_response.service_call_status)
32 print(verification_response.message)
Notes:
Directories: Ensure the specified paths are accessible by the server, especially for output directories.
update_issues_in_verified_model: If set to True, this updates issues within the error datasets in the verified model.
save_verification_statistics: If set to True, this option saves verification statistics into the Data Dictionary database.
Verification using DDX Specification with Automatic Object List Generation
In addition to specifying individual object IDs, you can also automatically retrieve all feature IDs within a dataset for comprehensive quality verification. This can be done by using a function (e.g., get_object_ids_from_feature_class) to fetch all object IDs, allowing you to apply checks across an entire dataset. This approach is especially useful for verifying large datasets.
Use this method to streamline quality verification for complete datasets or to apply selection queries for filtered checks. Simply generate the object list and add it to your verification parameters.
1import arcpy
2
3def get_object_ids_from_feature_class(feature_class, selection_query=None):
4 object_ids = []
5 with arcpy.da.SearchCursor(feature_class, ["OBJECTID"], selection_query) as cursor:
6 for row in cursor:
7 object_ids.append(row[0])
8 return object_ids
9
10ddx_id = 9
11ddx_project_short_name = ""
12road_dataset_id = 138
13
14
15road_feature_class = r"C:\DIRA\... sde\feature_class"
16road_selection_query = None # Optional: SQL filter query
17
18road_object_ids_to_verify = get_object_ids_from_feature_class(road_feature_class, road_selection_query)
Verification on Secure Channel
In this example, the grpc.ssl_channel_credentials object is created by a utility method, that gets the required root certificates automatically from the windows certificate store. For advanced scenarios or credentials on a non-windows platform, see the gRPC Python docs.
1import prosuite
2ssl_credentials = prosuite.utils.get_ssl_channel_credentials()
3
4# if channel_credentials are passed to the Verification constructor, a secure channel will be established.
5service = prosuite.verification.Service(host_name='localhost', port_nr=5151, channel_credentials=ssl_credentials)
Define a WKB perimeter
1import prosuite
2poly_as_hex_string = '01ee0300000100000001eb03000001000000050000004060e5e8cfd5434100c3640aa44f32410000000000000000f8065f282dd6434100c3640aa44f32410000000000000000f8065f282dd6434170d71262d64f324100000000000000004060e5e8cfd5434170d71262d64f324100000000000000004060e5e8cfd5434100c3640aa44f32410000000000000000'
3wkb_perimeter = prosuite.WkbPerimeter(bytes.fromhex(poly_as_hex_string))
4
5# the wkb_perimeter can be assigned to the perimeter parameter in verify()
Note
The variable ‘poly_as_hex_string’ is the hex string representation of a polygon or envelope. It can be produced for example from an arcpy.Geometry. Any arcpy.Geometry can be converted to WKB and encoded as hex based string:
poly_as_hex_string = arcpy_polygon_geometry.WKB.hex()
Acessing a verification response
service.verify() returns an iterable of ResponseVerification objects. It is iterable because the verification service returns a reponse stream. Hence the progress can be printed in real-time.
1for verification_response in service.verify():
2 print(verification_response.message_level)
3 print(verification_response.service_call_status)
4 print(verification_response.message)
Advanced Parameters
Optionally, change advanced verification parameters, such as the Verification tile_size (the default is 5000m)
1import prosuite
2
3xml_file = 'C:/temp/road_specification.qa.xml'
4service = prosuite.verification.Service(host_name='localhost', port_nr=5151)
5
6xml_spec = prosuite.XmlSpecification(
7 specification_file=xml_file, specification_name="Produktionsunterstuetzung",
8 data_source_replacements=[["ProductionModel", sde_file]])
9
10params = prosuite.verification.VerificationParameters(tile_size=10000)
11
12out_dir = 'C:/temp/verification_output'
13
14for verification_response in service.verify(specification=spec, output_dir=out_dir, parameters=params):
15 print(verification_response)
16
17for verification_response in service.verify(specification=spec, output_dir = out_dir):
18 print(verification_response)
Start and stop the local service process
If no service is constantly running and the python script should run without interaction, e.g. as a batch job, the server process can be started directly from python on the local machine. In this example, an XML specification is used.
1import time
2import subprocess
3import prosuite
4
5# Start the service from a local server installation with the default port.
6# It will fail and shut down immediately if another service is already serving on the same port.
7server_process = subprocess.Popen(r"C:\ProSuite\Server\prosuite-qa-microservice.exe")
8
9# Alternatively, provide a host name and custom port like this:
10# server_process = subprocess.Popen(
11# [r"C:\ProSuite\Server\prosuite-qa-microservice.exe",
12# "--hostname", "LOCALHOST", "--port", "12345"])
13
14# Wait for the process to start, initialize the ArcGIS license and the communication channl
15time.sleep(10)
16
17service = prosuite.verification.Service(host_name='LOCALHOST', port_nr=5151)
18
19xml_file = "C:/Data/specifications/road_specification.qa.xml"
20workspace = "C:/Data/TopographicData.gdb"
21
22xml_spec = prosuite.XmlSpecification(specification_file=xml_file,
23 specification_name="Produktionsunterstuetzung",
24 data_source_replacements=[["ProductionModel", workspace]])
25
26out_dir = 'C:/Temp/verification_output'
27
28for verification_response in service.verify(specification=spec, output_dir = out_dir):
29 print(verification_response)
30
31# Stop the service
32server_process.kill()
Issue Filters
Issue Filters allow filtering of issues found by specific conditions during the QA process. These filters are useful for focusing on particular types of issues and ignoring irrelevant ones.
Issue Filters can be configured for specific QA conditions and will be evaluated for each issue found. Typical use cases include spatial filtering or attribute-based conditions on the involved rows or features that caused the issue.
Common uses:
Filter by Area (IfWithin): Limit issues to a defined boundary or region.
Filter by Proximity (IfNear): Display issues that are near a specified feature.
Filter by Row Involvement (IfInvolvedRows): Focus on issues related to specific table rows.
For the full list of available Issue Filters, see the ProSuite QA Help for Tests Algorithm documentation.
Issue Filters are part of the module: prosuite.factories.issue_filters
.
Basic Issue Filter Workflow with `IfWithin`
Use case: Focus on issues within a specific area, such as a district or a defined project boundary where data has already been prepared for a specific purpose.
1import prosuite
2
3# Create a service to verify the specification
4service = prosuite.Service(host_name='localhost', port_nr=5151)
5
6# Define the dataset (e.g., roads)
7model = prosuite.Model("SimpleModel", r"C:\Data\SimpleCity.gdb")
8roads = prosuite.Dataset("TLM_STRASSEN", model)
9
10# Define the spatial filter: only include issues within a specific boundary
11boundary = prosuite.EnvelopePerimeter(x_min=2750673, y_min=1215551, x_max=2765845, y_max=1206640)
12issue_filter = prosuite.IssueFilters.IfWithin(boundary)
13
14# Define the condition (e.g., minimum length of roads)
15condition = prosuite.Conditions.qa_min_length_0(roads, limit=5.0, is3_d=False)
16
17# Create a specification and add the condition
18specification = prosuite.Specification(name="Check Road Length within Boundary")
19specification.add_condition(condition)
20
21# Apply the filter to the verification process
22output_dir = r"C:\temp\filtered_road_verification"
23for verification_response in service.verify(specification=specification, filters=[issue_filter], output_dir=output_dir):
24 print(verification_response.message_level)
25 print(verification_response.service_call_status)
26 print(verification_response.message)
This workflow ensures that only the issues occurring within the specified area are considered during verification.
Basic Issue Filter Workflow with Multiple Filters
Use case: Focus on issues within a specific area that are also near a given feature (e.g., roads near rivers within a designated region).
1import prosuite
2
3# Create a service to verify the specification
4service = prosuite.Service(host_name='localhost', port_nr=5151)
5
6# Define the dataset (e.g., roads and rivers)
7model = prosuite.Model("SimpleModel", r"C:\Data\SimpleCity.gdb")
8roads = prosuite.Dataset("TLM_STRASSEN", model)
9rivers = prosuite.Dataset("TLM_FLUSS", model)
10
11# Define multiple issue filters:
12# 1. Only include issues within a specific boundary.
13boundary = prosuite.EnvelopePerimeter(x_min=2750673, y_min=1215551, x_max=2765845, y_max=1206640)
14area_filter = prosuite.IssueFilters.IfWithin(boundary)
15
16# 2. Additionally, include only issues that are near the river features.
17proximity_filter = prosuite.IssueFilters.IfNear(rivers, distance=50)
18
19# Combine filters into a list
20combined_filters = [area_filter, proximity_filter]
21
22# Define the condition (e.g., minimum length of roads)
23condition = prosuite.Conditions.qa_min_length_0(roads, limit=5.0, is3_d=False)
24
25# Create a specification and add the condition
26specification = prosuite.Specification(name="Check Road Length within Boundary and Near Rivers")
27specification.add_condition(condition)
28
29# Apply the combined filters to the verification process
30output_dir = r"C:\temp\filtered_combined_verification"
31for verification_response in service.verify(specification=specification, filters=combined_filters, output_dir=output_dir):
32 print(verification_response.message_level)
33 print(verification_response.service_call_status)
34 print(verification_response.message)
This workflow demonstrates how to use multiple filters to refine the QA check, focusing on both spatial boundaries and proximity to other features.
Transformers
Transformers allow dynamic preprocessing of datasets, often before they are passed into a QA condition. The process happens in-memory without altering the original dataset or physically writing new tables.
Common Uses of Transformers
Transformers can be categorized into two main groups based on the complexity of the transformation:
- 1. Spatial Filtering:
These transformers filter the input datasets using a spatial constraint (TrOnlyContainedFeatures, TrOnlyDisjointFeatures, TrOnlyIntersectingFeatures)
- 2. Geometry Transformation:
These transformers perform a transformation of a single geometry (TrGeometryToPoints, TrMultipolygonToPolygon, TrLineToPolygon, TrProject etc.)
- 3. Topological Operations:
These transformers perform a topological operation involving several features (TrDissolve, TrIntersect, TrSpatialJoin)
- 4. Creation of Tables:
If a complex SQL query (e.g. containing sub-queries) is required to create a new table, the TrMakeTable transformer can be used. This allows for dynamic SQL execution to filter or transform data before it is used in QA checks. For Joins between multiple datasets, the TrJoinInMemory transformer can be used to perform in-memory joins across databases while TrJoin uses standard ArcGIS functionality to create a database join.
For the full list of available transformers, see the ProSuite QA Help for Tests Algorithm documentation:
Transformers are part of the module: prosuite.factories.transformers
.
Basic Transformer Workflow with `TrMakeTable`
In this example, we demonstrate how to use the TrMakeTable transformer to create a filtered subset of data based on an SQL query containing a sub-query. This is helpful when you want to extract specific records or perform conditional filtering before using the data in a QA check.
Use case: Filter a dataset to select only specific road types (e.g., roads that belong to a particular street type) and then perform a quality check.
1import prosuite
2
3service = prosuite.Service(host_name='localhost', port_nr=5151)
4
5# Load the geodatabase model
6model = prosuite.Model("SimpleModel", r"C:\Data\SimpleCity.gdb")
7
8# Define the dataset (e.g., roads)
9roads = prosuite.Dataset("Roads", model)
10
11# Define the SQL query to filter the roads based on a specific type
12sql_query = "SELECT * FROM TLM_STRASSEN WHERE OBJEKTART IN (SELECT OBJEKTART FROM TLM_STRASSE_AUS_EINFAHRT)"
13oid_field = "OBJECTID"
14
15# Apply the TrMakeTable transformer to filter the dataset
16filtered_roads = prosuite.Transformers.tr_make_table_1(roads, sql_query, oid_field)
17
18# Perform a quality check on the filtered dataset (e.g., check that roads have a minimum length)
19condition = prosuite.Conditions.qa_min_length_0(filtered_roads, limit=5.0, is3_d=False)
20
21# Add the condition to a specification and verify
22specification = prosuite.Specification("Check Road Length")
23specification.add_condition(condition)
24
25server = prosuite.Service(host_name="localhost", port_nr=5151)
26output_dir = r"C:\temp\filtered_road_verification"
27
28for verification_response in service.verify(specification=specification, output_dir=output_dir):
29 print(verification_response.message_level)
30 print(verification_response.service_call_status)
31 print(verification_response.message)
Intersection of Features Using `TrIntersect`
The TrIntersect transformer is used to calculate intersections between two spatial datasets (e.g. roads and rivers). This can be useful to identify where features cross or overlap, such as finding all points where roads intersect rivers.
Use case: Find intersections between roads and rivers to identify potential bridge locations.
1import prosuite
2
3service = prosuite.Service(host_name='localhost', port_nr=5151)
4
5# Load the geodatabase model
6model = prosuite.Model("SimpleModel", r"C:\Data\SimpleCity.gdb")
7
8# Define two input feature classes (roads and rivers)
9roads = prosuite.Dataset("Roads", model)
10rivers = prosuite.Dataset("Rivers", model)
11lakes = prosuite.Dataset("Lakes", model)
12
13# Apply the TrIntersect transformer
14intersect_dataset = prosuite.Transformers.tr_intersect(roads, rivers)
15
16# Make sure only point intersections (and no lines, in case of roads running along rivers)
17intersect_dataset.result_dimension = 1
18
19# Use the result in a QA condition (e.g. check that intersections exist)
20
21#Test that the bridges of rails are not within a lake:
22condition = prosuite.Conditions.qa_interior_intersects_other_0(intersect_dataset, lakes)
23
24# Add to a specification and verify
25specification = prosuite.Specification("Check Road-River Intersections")
26specification.add_condition(condition)
27
28server = prosuite.Service(host_name="localhost", port_nr=5151)
29output_dir = r"C:\temp\intersection_verification"
30
31for verification_response in service.verify(specification=specification, output_dir=output_dir):
32 print(verification_response.message_level)
33 print(verification_response.service_call_status)
34 print(verification_response.message)
DDX Verification with Transformer (TrMakeTable)
This example demonstrates how a quality specification can be loaded from the ProSuite Data Dictionary (DDX) and adapted with an SQL-based transformer (TrMakeTable) replacing an existing dataset. This shows how a configured input dataset can be adapted to using a dynamic query before the QA test runs.
Use case: Run a DDX-based quality verification, but only on features that match certain criteria (e.g. only features with status = ‘ACTIVE`), without modifying the original view or dataset.
1import logging
2import prosuite
3
4from prosuite.quality import Parameter
5from prosuite.data_model.transformed_dataset import TransformedDataset
6
7# === Configuration ===
8HOSTNAME = "localhost"
9PORT = 5151
10SPECIFICATION_ID = 5
11PROJECT_NAME = "MY_PROJECT"
12OUTPUT_DIR = r"C:\temp\verification_output"
13
14TARGET_DATASET_NAME = "my_schema.v_feature_edges"
15OID_FIELD = "object_id"
16 SQL_FILTER = """
17 SELECT object_id, change_column, type_column, geom
18 FROM schema_name.source_view
19 WHERE object_id NOT IN (
20 SELECT other.object_id
21 FROM schema_name.reference_table other
22 WHERE other.object_id = schema_name.source_view.object_id
23 AND other.status_column = 'some_terminated_status'
24 )
25 AND (
26 change_column = 'some_terminated_status' OR change_column = 'active_status'
27 )
28 """
29
30
31 # === Logging Setup ===
32 logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
33
34
35 def redirect_datasets(params: list[Parameter]):
36 for param in params:
37 if not param.is_dataset_parameter():
38 continue
39
40 if param.is_dataset_value():
41 dataset = param.dataset
42 if dataset.model.default_database_schema_owner and '.' not in dataset.name:
43 dataset.name = f"{dataset.model.default_database_schema_owner}.{dataset.name}"
44
45 if dataset.name == TARGET_NAME:
46 transformed = prosuite.Transformers.tr_make_table_1(dataset, SQL_FILTER, OID_FIELD)
47 param.dataset = transformed
48 logging.info(f"➔ Applied SQL filter on {dataset.name}")
49
50 elif param.is_transformed_dataset_value():
51 transformed = param.dataset
52 if not transformed.transformer_descriptor.startswith("TrMakeTable"):
53 redirect_datasets(transformed.parameters)
54
55
56 def main():
57 service = prosuite.Service(host_name=HOST, port_nr=PORT)
58 spec = prosuite.DdxService(host_name=HOST, port_nr=PORT).get_specification(SPEC_ID)
59 spec.project_short_name = "shortname"
60
61 logging.info(f"Loaded Specification: {spec.name} with {len(spec.get_conditions())} conditions")
62
63 for cond in spec.get_conditions():
64 logging.info(f"Processing condition: {cond.name}")
65 redirect_datasets(cond.parameters)
66
67 perimeter = prosuite.EnvelopePerimeter(x_min=2599839, y_min=1200036, x_max=2599900, y_max=1200050)
68 params = prosuite.VerificationParameters(user_name="user01")
69
70 logging.info("=== Starting QA Verification ===")
71
72 result = None
73 for response in service.verify(specification=spec, output_dir=OUTPUT_DIR, perimeter=perimeter, parameters=params):
74 result = response
75 logging.info(f"[{response.message_level}] {response.message}")
76
77 if result and result.service_call_status == "Finished":
78 logging.info("=== Verification completed successfully ===")
79 else:
80 logging.error("Verification failed or was incomplete.")
81
82
83 if __name__ == "__main__":
84 main()
Explanation:
The TrMakeTable transformer filters records using a custom SQL query before the QA condition executes.
The DDX specification is reused as-is, no modification in the Data Dictionary is required.
Works with PostgreSQL/PostGIS views or other SQL-based sources supported by the QA microservice.
Export transformed datasets using `QaExportTables`
Use case: After applying SQL-based transformers like TrMakeTable, you may want to inspect the intermediate results in a file geodatabase. This is helpful for understanding which features are being passed into QA checks.
1import prosuite
2import os
3
4service = prosuite.Service(host_name='localhost', port_nr=5151)
5
6# Load the model and base dataset
7model = prosuite.Model("MyModel", r"C:\\Data\\MyDatabase.gdb")
8roads = prosuite.Dataset("v_roads", model)
9
10# Apply an SQL filter via TrMakeTable
11sql = """
12 SELECT object_id, change_column, type_column, geom
13 FROM schema_name.source_view
14 WHERE object_id NOT IN (
15 SELECT other.object_id
16 FROM schema_name.reference_table other
17 WHERE other.object_id = schema_name.source_view.object_id
18 AND other.status_column = 'some_terminated_status'
19 )
20 AND (
21 change_column = 'some_terminated_status' OR change_column = 'active_status'
22 )
23 """
24filtered_roads = prosuite.Transformers.tr_make_table_1(roads, sql, object_id_field="object_id")
25
26# Define a basic QA condition
27condition = prosuite.Conditions.qa_min_length_0(filtered_roads, limit=10.0, is3_d=False)
28
29# Create specification and add the condition
30spec = prosuite.Specification(name="Check Active Roads")
31spec.add_condition(condition)
32
33# Export transformed dataset to GDB for inspection
34export_gdb = r"C:\\temp\\qa_export\\transformed_data.gdb"
35spec.add_condition(
36 prosuite.Conditions.qa_export_tables_0([filtered_roads], export_gdb)
37)
38
39# Run verification
40for response in service.verify(specification=spec, output_dir=r"C:\\temp\\qa_export"):
41 print(response.message_level)
42 print(response.message)
Custom Names for Conditions and Transformers
ProSuite automatically generates names for QA conditions and transformers by combining the descriptor (e.g., TrMakeTable, QaMinLength) with the name of the first involved dataset. Dots in dataset names are replaced by underscores.
Example of an automatically generated name:
TrMakeTable1_my_schema_v_roads
This naming logic applies to both transformers and QA conditions.
Assigning custom names:
You can override the default name:s by explicitly setting the name attribute of the transformer or condition. This is particularly useful for clarity in output directories and verification reports.
transformer = prosuite.Transformers.tr_make_table_1(
base_table="my_schema.v_roads",
sql="""
SELECT object_id, status, category, geom
FROM my_schema.v_roads AS r
WHERE object_id NOT IN (
SELECT h.object_id
FROM my_schema.roads_history h
WHERE h.object_id = r.object_id
AND h.version_end = '9999-12-31'
)
AND (
status = 'active' OR status = 'open'
)
""",
object_id_field="object_id"
)
transformer.name = "FilteredRoads_ActiveOrOpen"
condition = prosuite.Conditions.qa_min_length_0(transformer, limit=5.0, is3_d=False)
condition.name = "CheckMinLengthOfActiveRoads"