Validation Package ceplas-04-connectedData

Language: F#

ceplas; quality-arc

v1.0.0 released on 08/20/2026

by Dominik Brilhaus; Heinrich Lukas Weil

5 Downloads

Validates whether data and metadata in the ARC are properly connected


Install with arc-validate

 arc-validate package install ceplas-04-connectedData --version 1.0.0

Include in a PLANTDataHUB CQC pipeline

validation_packages:
  - name: ceplas-04-connectedData
    version: 1.0.0

Description

## Critical quality criteria

- Every annotation table contains an Input

- Every annotation table contains an Output

- Every annotation table contains a Protocol reference

- ARC annotation tables are connected


Release notes

- Release leveled validation packages.


Browse code for v1.0.0 (fsharp)
let [<Literal>]PACKAGE_METADATA = """(*
---
Name: ceplas-04-connectedData
Summary: Validates whether data and metadata in the ARC are properly connected
Description: |
    ## Critical quality criteria
    - Every annotation table contains an Input
    - Every annotation table contains an Output
    - Every annotation table contains a Protocol reference
    - ARC annotation tables are connected
MajorVersion: 1
MinorVersion: 0
PatchVersion: 0
Publish: true
Authors:
  - FullName: Dominik Brilhaus
    Email: brilhaus@hhu.de
    Affiliation: CEPLAS
    AffiliationLink: https://ceplas.eu
  - FullName: Heinrich Lukas Weil
    Email: weil@nfdi4plants.org
    Affiliation: RPTU Kaiserslautern
    AffiliationLink: http://rptu.de/startseite
Tags:
  - Name: ceplas
  - Name: quality-arc
ReleaseNotes: |
    - Release leveled validation packages.
---
*)"""

#r "nuget: ARCExpect.Core, 7.0.0-alpha"
#r "nuget: ARCtrl.QueryModel, 3.0.0-alpha.4"
#r "nuget: Fable.SimpleHttp"

open ARCtrl
open ARCtrl.QueryModel
open Expecto
open ARCExpect
open System.IO
open Fable.SimpleHttp
open System.Text

//////////////////////////// 
// TODO to be added to ARCtrl https://github.com/nfdi4plants/ARCtrl/pull/633
type ArcTable with
    member this.TryGetProtocolUriColumn() =
        this.TryGetColumnByHeader(CompositeHeader.ProtocolUri)
////////////////////////////

// Input:

let arcDir = Directory.GetCurrentDirectory()

////////////////////////

let arc =
    try ARC.load arcDir with
    | _ -> ARC(identifier = "placeholder")

arc.MakeDataFilesAbsolute()
arc.DataContextMapping()

// Collection of all tables in the ARC together with a set of their I/O nodes
// This is used to check whether each table contains at least one overlapping I/O with any other table
// Allows duplicate table names (i.e. between multiple studies / assays)

let tableNodes = 

    let trimDataFileName (n: string) = n.TrimStart(Array.ofSeq "./")

    let tableNodeGetter (collectionID : string ) (tables : ArcTables) = 
        tables
        |> Seq.map (fun t ->
            $"'{t.Name}' in {collectionID}", 
            set [
                yield! t.InputNames |> List.map trimDataFileName
                yield! t.OutputNames |> List.map trimDataFileName
                ]
        )

    let assayTables = 
        arc.Assays
        |> Seq.collect (fun a -> tableNodeGetter $"Assay '{a.Identifier}'" a)
    let studyTables = 
        arc.Studies
        |> Seq.collect (fun s -> tableNodeGetter $"Study '{s.Identifier}'" s)
    let runTables = 
        arc.Runs
        |> Seq.collect (fun r -> tableNodeGetter $"Run '{r.Identifier}'" r)
    
    Seq.concat [
        assayTables
        studyTables
        runTables
        ]

// Values:

let criticalCases =     
    testList "criticalCases" [

    /////////////////////////////////////////////////////////////////

    for t in arc.ArcTables do

        // TestCase Critical: Every annotation table contains an Input
        
        testCase $"Table '{t.Name}' contains `Input`" <| fun _ ->
                
            Expect.isGreaterThanOrEqual t.InputNames.Length 1
                $"Table '{t.Name}' contains no Input"

        // TestCase Critical: Every annotation table contains an Output
        
        testCase $"Table '{t.Name}' contains `Output`" <| fun _ ->
                
                Expect.isGreaterThanOrEqual t.OutputNames.Length 1
                    $"Table '{t.Name}' contains no Output"

        // TestCase Critical: Every annotation table contains a Protocol reference

        testCase $"Table '{t.Name}' contains `Protocol`" <| fun _ ->
            Expect.isTrue
                (t.TryGetProtocolUriColumn().IsSome || t.TryGetProtocolNameColumn().IsSome)
                $"Table '{t.Name}' contains no 'Protocol Uri' nor 'Protocol REF' column"
    
    /////////////////////////////////////////////////////////////////

    // TestCase Critical: ARC annotation tables are connected
    for name, nodes in tableNodes do    
        
        testCase $"ARC annotation table {name} is connected"  <| fun _ ->

            let tableConnection = tableNodes |> Seq.exists (fun (n, nds) ->
                    if n <> name then
                        Set.intersect nodes nds
                        |> Seq.length
                        |> (<>) 0
                    else
                        false
                )
            
            Expect.isTrue tableConnection
                $"Annotation table {name} is not connected to any other annotation table"
    ]
    

// let nonCriticalCases =
//     testList "nonCriticalCases" [
                

//     ]

// Execution:
Setup.ValidationPackage(
    metadata = Setup.Metadata(
        PACKAGE_METADATA,
        AVPRIndex.Frontmatter.FrontmatterLanguage.FSharpFrontmatter
        ),
    CriticalValidationCases = [criticalCases],
    NonCriticalValidationCases = []
)
|> Execute.ValidationPipeline(
    basePath = arcDir
)



Available versions
Version Released on
1.0.0 08/20/2026