# cog-sail – Sailboat data quality rules

Example SHACL rules for the **sailboat** data model (`sailboat_sailboat_v1.graphql`) and RAW table validation.

## Directory Structure

```
cog-sail/
├── settings.yaml              # Global settings for the environment
├── views/                     # DMS view-specific validation configs
│   ├── smallboat.yaml
│   ├── largeboat.yaml
│   └── ...
├── tables/                    # RAW table validation configs
│   └── abb_example_motor_list.yaml
└── shacl_rules/              # SHACL rules (shared by views and tables)
    ├── smallboat_shacl_rules.ttl
    └── abb_example_motor_list_shacl_rules.ttl
```

## Data model (summary)

- **Space:** `sailboat`
- **Boat** (interface): `boat_guid`, `mmsi` (→ MMSINumber)
- **SmallBoat** (implements Boat): `boat_guid`, `mmsi`, `small_boat_guid` – class B AIS
- **LargeBoat** (implements Boat): `boat_guid`, `mmsi`, `large_boat_guid` – class A AIS (two trailing zeros in MMSI)
- **MMSINumber** (interface): `boat` (→ Boat), `mmsi_number_guid`
- **PGN** (type): PGN NMEA sentence code; reverse relation `mmsi` from NMEATimeSeries via `pgn`
- **NMEATimeSeries** (type): `mmsi` (→ MMSINumber), `pgn` (→ PGN)
- **SailingBoat** (type, implements SmallBoat & Boat): `mmsi` (→ MMSINumber), `sailing_boat_guid`, …
- **ORCCertificate** (type): `sailing_boat` (→ SailingBoat)
- **RegattaEntry** (type): `sailing_boat` (→ SailingBoat), `regatta_class` (→ RegattaClass)

## Rules in `shacl_rules/sailboat_data_quality_rules.ttl`

Traversal rules are only defined for views that typically have data (e.g. NMEATimeSeries, PGN, LargeBoat, NavigationAid, SailingBoat). Views with 0 instances (ORCCertificate, RegattaEntry, etc.) are omitted to avoid unnecessary warnings.

| Rule | Type | Constraint |
|------|------|------------|
| **SmallBoat boat_guid** | SmallBoat | `boat_guid` must **not** end with `00` |
| **SmallBoat name** | SmallBoat | Should have a `name` (warning if missing) |
| **LargeBoat boat_guid** | LargeBoat | `boat_guid` must end with `00` |
| **PGN mmsi reference** | PGN | Must have at least one mmsi reference (NMEATimeSeries). Checks both inverse path `^pgn` and forward path `mmsi`. |
| **NMEATimeSeries boat traversal** | NMEATimeSeries | Must be linked to a Boat via path `mmsi` → `boat`. |
| **NMEATimeSeries name** | NMEATimeSeries | Must have a `name` (violation if missing). |
| **NMEATimeSeries value** | NMEATimeSeries | Should have a `value` (TimeSeries reference; warning if missing). |
| **SailingBoat mmsi traversal** | SailingBoat | Must have at least one `mmsi` (MMSINumber) reference. |
| **LargeBoat mmsi traversal** | LargeBoat | Must have at least one `mmsi` (MMSINumber) reference. |
| **NavigationAid boat traversal** | NavigationAid | Must have a `boat` connection. |

## Not all views have data

Many sailboat views may have **0 instances** in your space (e.g. ORCCertificate, RegattaEntry, Race, Regatta). Rules that target those views will trigger the validation-time warning (“targets X (0 instances)”) and will produce no violations until instances exist. You can leave the rules in place; they will apply once data is loaded.

## Namespace

- `sb:` = `http://purl.org/cognite/sailboat/sailboat/`  
  (adjust if your view or container IDs differ.)

For a comparison with other SHACL rule files (cog-ai, abp-dev) and why rules might not fire, see [SHACL_RULES_COMPARISON.md](SHACL_RULES_COMPARISON.md).

## RAW Table Validation

The `tables/` folder contains configurations for validating RAW tables using SHACL rules.

Each table config (e.g., `abb_example_motor_list.yaml`) specifies:
- RAW database and table name
- SHACL rules file
- Validation namespace and settings
- Records API configuration

The validation mode (incremental vs historic) is determined by which function you call at runtime, not by config.

### Generating RAW Table Templates

Use the `generate_raw_table_template()` function to automatically create config and SHACL rules:

```python
from cognite_data_quality import generate_raw_table_template, load_cognite_client_from_toml

client = load_cognite_client_from_toml("./cog-sail.toml")

result = generate_raw_table_template(
    client=client,
    output_dir=".",
    environment_name="cog-sail",
    db_name="abb",
    table_name="Example Motor List",
    required_columns=["motor_id", "status"],
)
```

This will:
1. Analyze the RAW table schema (column names and types)
2. Generate SHACL rules in `shacl_rules/`
3. Create table config in `tables/`

## Usage with cognite_data_quality

### DMS View Validation

Point `rules_path` at this TTL and pass the sailboat datamodel:

```python
from cognite_data_quality import load_cognite_client_from_toml, run_validation
from cognite_data_quality import DataModelConfig

client = load_cognite_client_from_toml("./cog-sail.toml")

result = run_validation(
    client=client,
    instances=[...],
    rules_path="data-quality-validation/config/environments/cog-sail/shacl_rules/sailboat_data_quality_rules.ttl",
    rules_format="ttl",
    datamodel=DataModelConfig(space="sailboat", external_id="sailboat", version="v1"),
    post_to_records=False,
)
```

### RAW Table Validation

For RAW table validation, see test notebooks in `test_and_deploy/` for examples of using the RAW validation handlers.
