diff --git a/Directory.Packages.props b/Directory.Packages.props
index 77d02835..43c2f2ae 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,7 @@
-
+
diff --git a/build/ProjectInfo.fs b/build/ProjectInfo.fs
index cf0aa003..976bc619 100644
--- a/build/ProjectInfo.fs
+++ b/build/ProjectInfo.fs
@@ -5,19 +5,22 @@ open Helpers
let project = "ARCtrl"
+let allTestsProject = "tests/All"
+
/// Dotnet and JS test paths
let testProjects =
[
"tests/All"
- //"tests/Core"
- //"tests/Json"
- //"tests/Spreadsheet"
- //"tests/FileSystem"
- //"tests/ARCtrl"
- //"tests/Yaml"
- //"tests/ValidationPackages"
- //"tests/Contract"
- //"tests/ROCrate"
+ "tests/ARCtrl"
+ "tests/Contract"
+ "tests/Core"
+ "tests/CWL"
+ "tests/FileSystem"
+ "tests/Json"
+ "tests/ROCrate"
+ "tests/Spreadsheet"
+ "tests/ValidationPackages"
+ "tests/Yaml"
]
/// Native JS test paths
diff --git a/build/TestTasks.fs b/build/TestTasks.fs
index 6f4eb765..12f155be 100644
--- a/build/TestTasks.fs
+++ b/build/TestTasks.fs
@@ -6,6 +6,8 @@ open Fake.DotNet
open ProjectInfo
open BasicTasks
open Fake.Core
+open Fake.IO
+open Fake.IO.Globbing.Operators
module RunTests =
@@ -33,19 +35,26 @@ module RunTests =
Trace.traceImportant "Skipping JavaScript tests"
)
+ let prePareJsTests = BuildTask.create "PrepareJsTests" [] {
+ !! "tests/TestingUtils/TestResults"
+ |> Shell.cleanDirs
+ System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/js") |> ignore
+ //System.IO.File.Copy(jsHelperFilePath, $"{allTestsProject}/js/{jsHelperFileName}") |> ignore
+
+ }
+
+
let runTestsJs = BuildTask.createFn "runTestsJS" [clean] (fun tp ->
if tp.Context.Arguments |> List.exists (fun a -> a.ToLower() = skipTestsFlag.ToLower()) |> not then
Trace.traceImportant "Start Js tests"
- for path in ProjectInfo.testProjects do
- // Setup test results directory after clean
- System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/js") |> ignore
- // transpile js files from fsharp code
- run dotnet $"fable {path} -o {path}/js --nocache" ""
-
- System.IO.File.Copy(jsHelperFilePath, $"{path}/js/{jsHelperFileName}") |> ignore
- // run mocha in target path to execute tests
- // "--timeout 20000" is used, because json schema validation takes a bit of time.
- run node $"{path}/js/Main.js" ""
+ // Setup test results directory after clean
+ System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/js") |> ignore
+ // transpile js files from fsharp code
+ run dotnet $"fable {allTestsProject} -o {allTestsProject}/js --nocache" ""
+ System.IO.File.Copy(jsHelperFilePath, $"{allTestsProject}/js/{jsHelperFileName}") |> ignore
+ // run mocha in target path to execute tests
+ // "--timeout 20000" is used, because json schema validation takes a bit of time.
+ run node $"{allTestsProject}/js/Main.js" ""
else
Trace.traceImportant "Skipping Js tests"
)
@@ -65,13 +74,12 @@ module RunTests =
let runTestsPy = BuildTask.createFn "runTestsPy" [clean] (fun tp ->
if tp.Context.Arguments |> List.exists (fun a -> a.ToLower() = skipTestsFlag.ToLower()) |> not then
Trace.traceImportant "Start Python tests"
- for path in ProjectInfo.testProjects do
- // Setup test results directory after clean
- System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/py") |> ignore
- //transpile py files from fsharp code
- run dotnet $"fable {path} -o {path}/py --lang python --nocache" ""
- // run pyxpecto in target path to execute tests in python
- run python $"{path}/py/main.py" ""
+ // Setup test results directory after clean
+ System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/py") |> ignore
+ //transpile py files from fsharp code
+ run dotnet $"fable {allTestsProject} -o {allTestsProject}/py --lang python --nocache" ""
+ // run pyxpecto in target path to execute tests in python
+ run python $"{allTestsProject}/py/main.py" ""
else
Trace.traceImportant "Skipping Python tests"
@@ -81,8 +89,7 @@ module RunTests =
if tp.Context.Arguments |> List.exists (fun a -> a.ToLower() = skipTestsFlag.ToLower()) |> not then
Trace.traceImportant "Start .NET tests"
let dotnetRun = run dotnet "run"
- testProjects
- |> Seq.iter dotnetRun
+ dotnetRun allTestsProject
else
Trace.traceImportant "Skipping .NET tests"
)
@@ -105,6 +112,7 @@ module RunTests =
run python $"{p}/py/main.py" ""
// transpile js files from fsharp code
run dotnet $"fable {p} -o {p}/js" ""
+ System.IO.Directory.CreateDirectory("./tests/TestingUtils/TestResults/js") |> ignore
System.IO.File.Copy(jsHelperFilePath, $"{p}/js/{jsHelperFileName}") |> ignore
// run mocha in target path to execute tests
// "--timeout 20000" is used, because json schema validation takes a bit of time.
diff --git a/src/ARCtrl/ARC.fs b/src/ARCtrl/ARC.fs
index f05bbc6c..fc1c4d8a 100644
--- a/src/ARCtrl/ARC.fs
+++ b/src/ARCtrl/ARC.fs
@@ -301,6 +301,59 @@ type ARC(?isa : ArcInvestigation, ?cwl : unit, ?fs : FileSystem.FileSystem) =
#endif
+ member this.MakeDataFilesAbsolute() =
+ let filesPaths = this.FileSystem.Tree.ToFilePaths() |> set
+ let checkExistenceFromRoot = fun p -> filesPaths |> Set.contains p
+ match this.ISA with
+ | Some inv ->
+ inv.Studies |> Seq.iter (fun s ->
+ s.Tables |> Seq.iter (fun t ->
+ match t.TryGetInputColumn() with
+ | Some col when col.Header.IsDataColumn ->
+ col.Cells |> Array.iter (fun c ->
+ if c.AsData.FilePath.IsSome then
+
+ let newFilePath =
+ c.AsData.GetAbsolutePathForStudy(s.Identifier,checkExistenceFromRoot)
+ c.AsData.FilePath <- Some newFilePath
+ )
+ | _ -> ()
+ match t.TryGetOutputColumn() with
+ | Some col when col.Header.IsDataColumn ->
+ col.Cells |> Array.iter (fun c ->
+ if c.AsData.FilePath.IsSome then
+ let newFilePath =
+ c.AsData.GetAbsolutePathForStudy(s.Identifier,checkExistenceFromRoot)
+ c.AsData.FilePath <- Some newFilePath
+ )
+ | _ -> ()
+ )
+ )
+ inv.Assays |> Seq.iter (fun a ->
+ a.Tables |> Seq.iter (fun t ->
+ match t.TryGetInputColumn() with
+ | Some col when col.Header.IsDataColumn ->
+ col.Cells |> Array.iter (fun c ->
+ if c.AsData.FilePath.IsSome then
+ let newFilePath =
+ c.AsData.GetAbsolutePathForAssay (a.Identifier,checkExistenceFromRoot)
+ c.AsData.FilePath <- Some newFilePath
+ )
+ | _ -> ()
+ match t.TryGetOutputColumn() with
+ | Some col when col.Header.IsDataColumn ->
+ col.Cells |> Array.iter (fun c ->
+ if c.AsData.FilePath.IsSome then
+ let newFilePath =
+ c.AsData.GetAbsolutePathForAssay (a.Identifier,checkExistenceFromRoot)
+ c.AsData.FilePath <- Some newFilePath
+ )
+ | _ -> ()
+ )
+ )
+ | None -> ()
+
+
//static member updateISA (isa : ISA.Investigation) (arc : ARC) : ARC =
// raise (System.NotImplementedException())
@@ -701,11 +754,24 @@ type ARC(?isa : ArcInvestigation, ?cwl : unit, ?fs : FileSystem.FileSystem) =
ARCtrl.Contract.Git.gitattributesFileName, ARCtrl.Contract.Git.gitattributesContract
|]
- static member fromROCrateJsonString (s:string) =
- let isa = ARCtrl.Json.Decode.fromJsonString ARCtrl.Json.ARC.ROCrate.decoder s
- ARC(?isa = isa)
+ static member fromDeprecatedROCrateJsonString (s:string) =
+ try
+ let isa = ARCtrl.Json.Decode.fromJsonString ARCtrl.Json.ARC.ROCrate.decoderDeprecated s
+ ARC(isa = isa)
+ with
+ | ex ->
+ failwithf "Could not parse deprecated ARC-RO-Crate metadata: \n%s" ex.Message
+
+ static member fromROCrateJsonString (s:string) =
+ try
+ let isa = ARCtrl.Json.Decode.fromJsonString ARCtrl.Json.ARC.ROCrate.decoder s
+ ARC(isa = isa)
+ with
+ | ex ->
+ failwithf "Could not parse ARC-RO-Crate metadata: \n%s" ex.Message
member this.ToROCrateJsonString(?spaces) =
+ this.MakeDataFilesAbsolute()
ARCtrl.Json.ARC.ROCrate.encoder (Option.get _isa)
|> ARCtrl.Json.Encode.toJsonString (ARCtrl.Json.Encode.defaultSpaces spaces)
diff --git a/src/ARCtrl/ARCtrl.Javascript.fsproj b/src/ARCtrl/ARCtrl.Javascript.fsproj
index ec77a70a..2d13112f 100644
--- a/src/ARCtrl/ARCtrl.Javascript.fsproj
+++ b/src/ARCtrl/ARCtrl.Javascript.fsproj
@@ -10,6 +10,7 @@
+
@@ -48,7 +49,8 @@
-
+
+
diff --git a/src/ARCtrl/ARCtrl.Python.fsproj b/src/ARCtrl/ARCtrl.Python.fsproj
index 1859409e..bd18d04d 100644
--- a/src/ARCtrl/ARCtrl.Python.fsproj
+++ b/src/ARCtrl/ARCtrl.Python.fsproj
@@ -12,6 +12,7 @@
+
@@ -50,7 +51,8 @@
-
+
+
diff --git a/src/ARCtrl/ARCtrl.fsproj b/src/ARCtrl/ARCtrl.fsproj
index 5bf53f9f..aacbe5f8 100644
--- a/src/ARCtrl/ARCtrl.fsproj
+++ b/src/ARCtrl/ARCtrl.fsproj
@@ -10,6 +10,7 @@
+
@@ -48,13 +49,14 @@
-
+
+
-
-
-
+
+
+
diff --git a/src/ARCtrl/ColumnIndex.fs b/src/ARCtrl/ColumnIndex.fs
new file mode 100644
index 00000000..ffe0c6ac
--- /dev/null
+++ b/src/ARCtrl/ColumnIndex.fs
@@ -0,0 +1,176 @@
+module ARCtrl.Process.ColumnIndex
+
+open ARCtrl
+
+let private tryInt (str:string) =
+ match System.Int32.TryParse str with
+ | true,int -> Some int
+ | _ -> None
+
+let orderName = "ColumnIndex"
+
+let createOrderComment (index : int) =
+ Comment.create(orderName,(string index))
+
+let tryGetIndex (comments : ResizeArray) =
+ match comments |> CommentArray.tryItem orderName with
+ | Some ci ->
+ let i = comments |> Seq.findIndex (fun c -> c.Name = Some orderName)
+ comments.RemoveAt(i)
+ tryInt ci
+ | _ -> None
+
+let setOntologyAnnotationIndexInplace i (oa : OntologyAnnotation) =
+ oa.Comments.Add(createOrderComment i)
+
+let setOntologyAnnotationIndex i (oa : OntologyAnnotation) =
+ let oac = oa.Copy()
+ setOntologyAnnotationIndexInplace i oac
+ oac
+
+let tryGetOntologyAnnotationIndex (oa : OntologyAnnotation) =
+ oa.Comments |> tryGetIndex
+
+let tryGetParameterIndex (param : ProtocolParameter) =
+ param.ParameterName
+ |> Option.bind (fun oa ->
+ oa.Comments |> tryGetIndex
+ )
+
+let tryGetParameterColumnIndex (paramValue : ProcessParameterValue) =
+ paramValue.Category
+ |> Option.bind tryGetParameterIndex
+
+let tryGetFactorIndex (factor : Factor) =
+ factor.FactorType
+ |> Option.bind (fun oa ->
+ oa.Comments |> tryGetIndex
+ )
+
+let tryGetFactorColumnIndex (factorValue : FactorValue) =
+ factorValue.Category
+ |> Option.bind tryGetFactorIndex
+
+let tryGetCharacteristicIndex (characteristic : MaterialAttribute) =
+ characteristic.CharacteristicType
+ |> Option.bind (fun oa ->
+ oa.Comments |> tryGetIndex
+ )
+
+let tryGetCharacteristicColumnIndex (characteristicValue : MaterialAttributeValue) =
+ characteristicValue.Category
+ |> Option.bind tryGetCharacteristicIndex
+
+let tryGetComponentIndex (comp : Component) =
+ comp.ComponentType
+ |> Option.bind (fun oa ->
+ oa.Comments |> tryGetIndex
+ )
+
+
+[]
+module ColumnIndexExtensions =
+
+ type OntologyAnnotation with
+
+ /// Create a ISAJson Factor from ISATab string entries
+ static member fromStringWithColumnIndex (name:string) (term:string) (source:string) (accession:string) valueIndex =
+ Factor.fromString(name,term,source,accession,ResizeArray [|createOrderComment valueIndex|])
+
+ static member getColumnIndex(f) = tryGetOntologyAnnotationIndex f |> Option.get
+
+ member this.GetColumnIndex() = tryGetOntologyAnnotationIndex this |> Option.get
+
+ static member tryGetColumnIndex(f) = tryGetOntologyAnnotationIndex f
+
+ member this.TryGetColumnIndex() = tryGetOntologyAnnotationIndex this
+
+ static member setColumnIndex i oa = setOntologyAnnotationIndex i oa
+
+ member this.SetColumnIndex i = setOntologyAnnotationIndexInplace i this
+
+ type Factor with
+
+ /// Create a ISAJson Factor from ISATab string entries
+ static member fromStringWithColumnIndex (name:string) (term:string) (source:string) (accession:string) valueIndex =
+ Factor.fromString(name,term,source,accession,ResizeArray [|createOrderComment valueIndex|])
+
+ static member getColumnIndex(f) = tryGetFactorIndex f |> Option.get
+
+ member this.GetColumnIndex() = tryGetFactorIndex this |> Option.get
+
+ static member tryGetColumnIndex(f) = tryGetFactorIndex f
+
+ member this.TryGetColumnIndex() = tryGetFactorIndex this
+
+ type FactorValue with
+
+ static member getColumnIndex(f) = tryGetFactorColumnIndex f |> Option.get
+
+ member this.GetColumnIndex() = tryGetFactorColumnIndex this |> Option.get
+
+ static member tryGetColumnIndex(f) = tryGetFactorColumnIndex f
+
+ member this.TryGetColumnIndex() = tryGetFactorColumnIndex this
+
+ type MaterialAttribute with
+
+ /// Create a ISAJson characteristic from ISATab string entries
+ static member fromStringWithColumnIndex (term:string) (source:string) (accession:string) valueIndex =
+ MaterialAttribute.fromString(term,source,accession,ResizeArray [|createOrderComment valueIndex|])
+
+ static member getColumnIndex(m) = tryGetCharacteristicIndex m |> Option.get
+
+ member this.GetColumnIndex() = tryGetCharacteristicIndex this |> Option.get
+
+ static member tryGetColumnIndex(m) = tryGetCharacteristicIndex m
+
+ member this.TryGetColumnIndex() = tryGetCharacteristicIndex this
+
+ type MaterialAttributeValue with
+
+ static member getColumnIndex(m) = tryGetCharacteristicColumnIndex m |> Option.get
+
+ member this.GetColumnIndex() = tryGetCharacteristicColumnIndex this |> Option.get
+
+ static member tryGetColumnIndex(m) = tryGetCharacteristicColumnIndex m
+
+ member this.TryGetColumnIndex() = tryGetCharacteristicColumnIndex this
+
+ type ProtocolParameter with
+
+ /// Create a ISAJson parameter from ISATab string entries
+ static member fromStringWithColumnIndex (term:string) (source:string) (accession:string) valueIndex =
+ ProtocolParameter.fromString(term,source,accession,ResizeArray [|createOrderComment valueIndex|])
+
+ static member getColumnIndex(p) = tryGetParameterIndex p |> Option.get
+
+ member this.GetColumnIndex() = tryGetParameterIndex this |> Option.get
+
+ static member tryGetColumnIndex(p) = tryGetParameterIndex p
+
+ member this.TryGetColumnIndex() = tryGetParameterIndex this
+
+ type ProcessParameterValue with
+
+ static member getColumnIndex(p) = tryGetParameterColumnIndex p |> Option.get
+
+ member this.GetColumnIndex() = tryGetParameterColumnIndex this |> Option.get
+
+ static member tryGetColumnIndex(p) = tryGetParameterColumnIndex p
+
+ member this.TryGetColumnIndex() = tryGetParameterColumnIndex this
+
+ type Component with
+
+ /// Create a ISAJson Factor from ISATab string entries
+ static member fromStringWithColumnIndex (name:string) (term:string) (source:string) (accession:string) valueIndex =
+ Component.fromISAString(name,term,source,accession,ResizeArray [|createOrderComment valueIndex|])
+
+ static member getColumnIndex(f) = tryGetComponentIndex f |> Option.get
+
+ member this.GetColumnIndex() = tryGetComponentIndex this |> Option.get
+
+ static member tryGetColumnIndex(f) = tryGetComponentIndex f
+
+ member this.TryGetColumnIndex() = tryGetComponentIndex this
diff --git a/src/ARCtrl/Conversion.fs b/src/ARCtrl/Conversion.fs
new file mode 100644
index 00000000..8dcbf138
--- /dev/null
+++ b/src/ARCtrl/Conversion.fs
@@ -0,0 +1,1436 @@
+namespace ARCtrl.Conversion
+
+open ARCtrl.ROCrate
+open ARCtrl
+open ARCtrl.Helper
+open System.Collections.Generic
+//open ColumnIndex
+
+module DateTime =
+
+ let tryFromString (s : string) =
+ try Json.Decode.fromJsonString Json.Decode.datetime s |> Some
+ with _ -> None
+
+ let toString (d : System.DateTime) =
+ Json.Encode.dateTime d
+ |> Json.Encode.toJsonString 0
+
+module ColumnIndex =
+
+ open ARCtrl
+
+ let private tryInt (str:string) =
+ match System.Int32.TryParse str with
+ | true,int -> Some int
+ | _ -> None
+
+ let orderName = "columnIndex"
+
+ let tryGetIndex (node : LDNode) =
+ match node.TryGetPropertyAsSingleton(orderName) with
+ | Some (:? string as ci) -> tryInt ci
+ | _ -> None
+
+ let setIndex (node : LDNode) (index : int) =
+ node.SetProperty(orderName,(string index))
+
+ []
+ module ColumnIndexExtensions =
+
+ type LDNode with
+
+ member this.GetColumnIndex() = tryGetIndex this |> Option.get
+
+ member this.TryGetColumnIndex() = tryGetIndex this
+
+ member this.SetColumnIndex (index : int) = setIndex this index
+
+/// Functions for transforming base level ARC Table and ISA Json Objects
+type BaseTypes =
+
+ static member composeComment (comment : ARCtrl.Comment) =
+ let name = match comment.Name with | Some n -> n | None -> failwith "Comment must have a name"
+ ARCtrl.ROCrate.Comment.create(name = name, ?text = comment.Value)
+
+ static member decomposeComment (comment : LDNode, ?context : LDContext) =
+ let name = Comment.getNameAsString(comment, ?context = context)
+ let text = Comment.tryGetTextAsString(comment, ?context = context)
+ Comment(name = name,?value = text)
+
+ static member composeDefinedTerm (term : OntologyAnnotation) =
+ let tan = term.TermAccessionAndOntobeeUrlIfShort |> Option.fromValueWithDefault ""
+ DefinedTerm.create(name = term.NameText, ?termCode = tan)
+
+ static member decomposeDefinedTerm (term : LDNode, ?context : LDContext) =
+ let name = DefinedTerm.getNameAsString(term, ?context = context)
+ match DefinedTerm.tryGetTermCodeAsString(term, ?context = context) with
+ | Some t -> OntologyAnnotation.fromTermAnnotation(tan = t, name = name)
+ | None -> OntologyAnnotation.create(name = name)
+
+ static member composePropertyValueFromOA (term : OntologyAnnotation) =
+ let tan = term.TermAccessionAndOntobeeUrlIfShort |> Option.fromValueWithDefault ""
+ PropertyValue.create(name = term.NameText, ?propertyID = tan)
+
+ static member decomposePropertyValueToOA (term : LDNode, ?context : LDContext) =
+ let name = PropertyValue.getNameAsString(term, ?context = context)
+ match PropertyValue.tryGetPropertyIDAsString(term, ?context = context) with
+ | Some t -> OntologyAnnotation.fromTermAnnotation(tan = t, name = name)
+ | None -> OntologyAnnotation.create(name = name)
+
+ /// Convert a CompositeCell to a ISA Value and Unit tuple.
+ static member valuesOfCell (value : CompositeCell) =
+ match value with
+ | CompositeCell.FreeText ("") -> None, None, None, None
+ | CompositeCell.FreeText (text) -> Some text, None, None, None
+ | CompositeCell.Term (term) when term.isEmpty() -> None, None, None, None
+ | CompositeCell.Term (term) when term.TANInfo.IsSome -> term.Name, Some term.TermAccessionAndOntobeeUrlIfShort, None, None
+ | CompositeCell.Term (term) -> term.Name, None, None, None
+ | CompositeCell.Unitized (text,unit) ->
+ let unitName, unitAccession = if unit.isEmpty() then None, None else unit.Name, Some unit.TermAccessionAndOntobeeUrlIfShort
+ (if text = "" then None else Some text),
+ None,
+ unitName,
+ unitAccession
+ | CompositeCell.Data (data) -> failwith "Data cell should not be parsed to isa value"
+
+ static member termOfHeader (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.Component oa
+ | CompositeHeader.Parameter oa
+ | CompositeHeader.Factor oa
+ | CompositeHeader.Characteristic oa ->
+ oa.NameText, if oa.TANInfo.IsSome then Some oa.TermAccessionAndOntobeeUrlIfShort else None
+ | h -> failwithf "header %O should not be parsed to isa value" h
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA Component
+ static member composeComponent (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ let v,va,u,ua = BaseTypes.valuesOfCell value
+ let n, na = BaseTypes.termOfHeader header
+ PropertyValue.createComponent(n, ?value = v, ?propertyID = na, ?valueReference = va, ?unitCode = ua, ?unitText = u)
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA ProcessParameterValue
+ static member composeParameterValue (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ let v,va,u,ua = BaseTypes.valuesOfCell value
+ let n, na = BaseTypes.termOfHeader header
+ PropertyValue.createParameterValue(n, ?value = v, ?propertyID = na, ?valueReference = va, ?unitCode = ua, ?unitText = u)
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA FactorValue
+ static member composeFactorValue (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ let v,va,u,ua = BaseTypes.valuesOfCell value
+ let n, na = BaseTypes.termOfHeader header
+ PropertyValue.createFactorValue(n, ?value = v, ?propertyID = na, ?valueReference = va, ?unitCode = ua, ?unitText = u)
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA MaterialAttributeValue
+ static member composeCharacteristicValue (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ let v,va,u,ua = BaseTypes.valuesOfCell value
+ let n, na = BaseTypes.termOfHeader header
+ PropertyValue.createCharacteristicValue(n, ?value = v, ?propertyID = na, ?valueReference = va, ?unitCode = ua, ?unitText = u)
+
+ static member composeFreetextMaterialName (headerFT : string) (name : string) =
+ $"{headerFT}={name}"
+
+
+ static member composeFile (d : Data) : LDNode =
+ let dataType = d.DataType |> Option.map (fun dt -> dt.AsString)
+ File.create(d.NameText,d.NameText,?disambiguatingDescription = dataType, ?encodingFormat = d.Format, ?usageInfo = d.SelectorFormat)
+
+ static member decomposeFile (f : LDNode, ?context : LDContext) : Data =
+ let dataType = File.tryGetDisambiguatingDescriptionAsString(f, ?context = context) |> Option.map DataFile.fromString
+ let format = File.tryGetEncodingFormatAsString(f, ?context = context)
+ let selectorFormat = File.tryGetUsageInfoAsString(f, ?context = context)
+ let data = Data(id = f.Id, name = File.getNameAsString(f, ?context = context), ?dataType = dataType, ?format = format, ?selectorFormat = selectorFormat)
+ data
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA ProcessInput
+ static member composeProcessInput (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ match header with
+ | CompositeHeader.Input IOType.Source -> Sample.createSource(value.AsFreeText)
+ | CompositeHeader.Input IOType.Sample -> Sample.createSample(value.AsFreeText)
+ | CompositeHeader.Input IOType.Material -> Sample.createMaterial(value.AsFreeText)
+ | CompositeHeader.Input IOType.Data ->
+ match value with
+ | CompositeCell.FreeText ft ->
+ File.create(ft,ft)
+ | CompositeCell.Data od ->
+ BaseTypes.composeFile od
+ | _ -> failwithf "Could not parse input data %O" value
+ | CompositeHeader.Input (IOType.FreeText ft) ->
+ let n = LDNode(id = BaseTypes.composeFreetextMaterialName ft value.AsFreeText, schemaType = ResizeArray [ft])
+ n.SetProperty(Sample.name, value.AsFreeText)
+ n
+ | _ ->
+ failwithf "Could not parse input header %O" header
+
+
+ /// Convert a CompositeHeader and Cell tuple to a ISA ProcessOutput
+ static member composeProcessOutput (header : CompositeHeader) (value : CompositeCell) : LDNode =
+ match header with
+ | CompositeHeader.Output IOType.Source
+ | CompositeHeader.Output IOType.Sample -> Sample.createSample(value.AsFreeText)
+ | CompositeHeader.Output IOType.Material -> Sample.createMaterial(value.AsFreeText)
+ | CompositeHeader.Output IOType.Data ->
+ match value with
+ | CompositeCell.FreeText ft ->
+ File.create(ft,ft)
+ | CompositeCell.Data od ->
+ BaseTypes.composeFile od
+ | _ -> failwithf "Could not parse output data %O" value
+ | CompositeHeader.Output (IOType.FreeText ft) ->
+ let n = LDNode(id = BaseTypes.composeFreetextMaterialName ft value.AsFreeText, schemaType = ResizeArray [ft])
+ n.SetProperty(Sample.name, value.AsFreeText)
+ n
+ | _ -> failwithf "Could not parse output header %O" header
+
+ static member headerOntologyOfPropertyValue (pv : LDNode, ?context : LDContext) =
+ let n = PropertyValue.getNameAsString(pv, ?context = context)
+ match PropertyValue.tryGetPropertyIDAsString(pv, ?context = context) with
+ | Some nRef -> OntologyAnnotation.fromTermAnnotation(tan = nRef, name = n)
+ | None -> OntologyAnnotation(name = n)
+
+ /// Convert an ISA Value and Unit tuple to a CompositeCell
+ static member cellOfPropertyValue (pv : LDNode, ?context : LDContext) =
+ let v = PropertyValue.tryGetValueAsString(pv, ?context = context)
+ let vRef = PropertyValue.tryGetValueReference(pv, ?context = context)
+ let u = PropertyValue.tryGetUnitTextAsString(pv, ?context = context)
+ let uRef = PropertyValue.tryGetUnitCodeAsString(pv, ?context = context)
+ match vRef,u,uRef with
+ | Some vr, None, None ->
+ CompositeCell.Term (OntologyAnnotation.fromTermAnnotation(vr,?name = v))
+ | None, Some u, None ->
+ CompositeCell.Unitized ((Option.defaultValue "" v),OntologyAnnotation(name = u))
+ | None, _, Some uRef ->
+ CompositeCell.Unitized ((Option.defaultValue "" v),OntologyAnnotation.fromTermAnnotation(uRef, ?name = u))
+ | None, None, None ->
+ CompositeCell.Term (OntologyAnnotation(?name = v))
+ | _ ->
+ failwithf "Could not parse value %s with unit %O and unit reference %O" (Option.defaultValue "" v) u uRef
+
+ /// Convert an ISA Component to a CompositeHeader and Cell tuple
+ static member decomposeComponent (c : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ let header = BaseTypes.headerOntologyOfPropertyValue(c, ?context = context) |> CompositeHeader.Component
+ let bodyCell = BaseTypes.cellOfPropertyValue (c, ?context = context)
+ header, bodyCell
+
+ /// Convert an ISA ProcessParameterValue to a CompositeHeader and Cell tuple
+ static member decomposeParameterValue (c : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ let header = BaseTypes.headerOntologyOfPropertyValue (c, ?context = context) |> CompositeHeader.Parameter
+ let bodyCell = BaseTypes.cellOfPropertyValue (c, ?context = context)
+ header, bodyCell
+
+ /// Convert an ISA FactorValue to a CompositeHeader and Cell tuple
+ static member decomposeFactorValue (c : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ let header = BaseTypes.headerOntologyOfPropertyValue (c, ?context = context) |> CompositeHeader.Factor
+ let bodyCell = BaseTypes.cellOfPropertyValue (c, ?context = context)
+ header, bodyCell
+
+ /// Convert an ISA MaterialAttributeValue to a CompositeHeader and Cell tuple
+ static member decomposeCharacteristicValue (c : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ let header = BaseTypes.headerOntologyOfPropertyValue (c, ?context = context) |> CompositeHeader.Characteristic
+ let bodyCell = BaseTypes.cellOfPropertyValue (c, ?context = context)
+ header, bodyCell
+
+ /// Convert an ISA ProcessOutput to a CompositeHeader and Cell tuple
+ static member decomposeProcessInput (pn : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ match pn with
+ | s when Sample.validateSource (s, ?context = context) -> CompositeHeader.Input IOType.Source, CompositeCell.FreeText (Sample.getNameAsString (s, ?context = context))
+ | m when Sample.validateMaterial (m, ?context = context) -> CompositeHeader.Input IOType.Material, CompositeCell.FreeText (Sample.getNameAsString (m, ?context = context))
+ | s when Sample.validate (s, ?context = context) -> CompositeHeader.Input IOType.Sample, CompositeCell.FreeText (Sample.getNameAsString (s, ?context = context))
+ | d when File.validate (d, ?context = context) -> CompositeHeader.Input IOType.Data, CompositeCell.Data (BaseTypes.decomposeFile (d, ?context = context))
+ | n -> CompositeHeader.Input (IOType.FreeText n.SchemaType.[0]), CompositeCell.FreeText (Sample.getNameAsString (n, ?context = context))
+
+
+ static member decomposeProcessOutput (pn : LDNode, ?context : LDContext) : CompositeHeader*CompositeCell =
+ match pn with
+ | m when Sample.validateMaterial (m, ?context = context) -> CompositeHeader.Output IOType.Material, CompositeCell.FreeText (Sample.getNameAsString (m, ?context = context))
+ | s when Sample.validate (s, ?context = context) -> CompositeHeader.Output IOType.Sample, CompositeCell.FreeText (Sample.getNameAsString (s, ?context = context))
+ | d when File.validate (d, ?context = context) -> CompositeHeader.Output IOType.Data, CompositeCell.Data (BaseTypes.decomposeFile (d, ?context = context))
+ | n -> CompositeHeader.Output (IOType.FreeText n.SchemaType.[0]), CompositeCell.FreeText (Sample.getNameAsString (n, ?context = context))
+
+ /// This function creates a string containing the name and the ontology short-string of the given ontology annotation term
+ ///
+ /// TechnologyPlatforms are plain strings in ISA-JSON.
+ ///
+ /// This function allows us, to parse them as an ontology term.
+ static member composeTechnologyPlatform (tp : OntologyAnnotation) =
+ match tp.TANInfo with
+ | Some _ ->
+ $"{tp.NameText} ({tp.TermAccessionShort})"
+ | None ->
+ $"{tp.NameText}"
+
+ /// This function parses the given string containing the name and the ontology short-string of the given ontology annotation term
+ ///
+ /// TechnologyPlatforms are plain strings in ISA-JSON.
+ ///
+ /// This function allows us, to parse them as an ontology term.
+ static member decomposeTechnologyPlatform (name : string) =
+ let pattern = """^(?.+) \((?[^(]*:[^)]*)\)$"""
+
+ match name with
+ | Regex.ActivePatterns.Regex pattern r ->
+ let oa = (r.Groups.Item "ontology").Value |> OntologyAnnotation.fromTermAnnotation
+ let v = (r.Groups.Item "value").Value
+ OntologyAnnotation.create(name = v, ?tan = oa.TermAccessionNumber, ?tsr = oa.TermSourceREF)
+ | _ ->
+ OntologyAnnotation.create(name = name)
+
+
+
+
+open ColumnIndex
+open ARCtrl.Helper.Regex.ActivePatterns
+
+/// Functions for parsing ArcTables to ISA json Processes and vice versa
+type ProcessConversion =
+
+ static member tryGetProtocolType (pv : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match LabProtocol.tryGetIntendedUseAsDefinedTerm(pv,?graph = graph, ?context = context) with
+ | Some dt ->
+ Some (BaseTypes.decomposeDefinedTerm(dt, ?context = context))
+ | None ->
+ match LabProtocol.tryGetIntendedUseAsString(pv, ?context = context) with
+ | Some s -> Some (OntologyAnnotation.create(name = s))
+ | None -> None
+
+ static member composeProcessName (processNameRoot : string) (i : int) =
+ $"{processNameRoot}_{i}"
+
+ static member decomposeProcessName (name : string) =
+ let pattern = """(?.+)_(?\d+)"""
+
+ match name with
+ | Regex pattern r ->
+ (r.Groups.Item "name").Value, Some ((r.Groups.Item "num").Value |> int)
+ | _ ->
+ name, None
+ // Explanation of the Getter logic:
+ // The getter logic is used to treat every value of the table only once
+ // First, the headers are checked for what getter applies to the respective column. E.g. a ProtocolType getter will only return a function for parsing protocolType cells if the header depicts a protocolType.
+ // The appropriate getters are then applied in the context of the processGetter, parsing the cells of the matrix
+
+ /// If the given headers depict a component, returns a function for parsing the values of the matrix to the values of this component
+ static member tryComponentGetter (generalI : int) (valueI : int) (valueHeader : CompositeHeader) =
+ match valueHeader with
+ | CompositeHeader.Component oa ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let c = BaseTypes.composeComponent valueHeader matrix.[generalI,i]
+ c.SetColumnIndex valueI
+ c
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a parameter, returns a function for parsing the values of the matrix to the values of this type
+ static member tryParameterGetter (generalI : int) (valueI : int) (valueHeader : CompositeHeader) =
+ match valueHeader with
+ | CompositeHeader.Parameter oa ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let p = BaseTypes.composeParameterValue valueHeader matrix.[generalI,i]
+ p.SetColumnIndex valueI
+ p
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a factor, returns a function for parsing the values of the matrix to the values of this type
+ static member tryFactorGetter (generalI : int) (valueI : int) (valueHeader : CompositeHeader) =
+ match valueHeader with
+ | CompositeHeader.Factor oa ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let f = BaseTypes.composeFactorValue valueHeader matrix.[generalI,i]
+ f.SetColumnIndex valueI
+ f
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a protocolType, returns a function for parsing the values of the matrix to the values of this type
+ static member tryCharacteristicGetter (generalI : int) (valueI : int) (valueHeader : CompositeHeader) =
+ match valueHeader with
+ | CompositeHeader.Characteristic oa ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let c = BaseTypes.composeCharacteristicValue valueHeader matrix.[generalI,i]
+ c.SetColumnIndex valueI
+ c
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a protocolType, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetProtocolTypeGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.ProtocolType ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ matrix.[generalI,i].AsTerm |> BaseTypes.composeDefinedTerm
+ |> Some
+ | _ -> None
+
+
+ /// If the given headers depict a protocolREF, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetProtocolREFGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.ProtocolREF ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ matrix.[generalI,i].AsFreeText
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a protocolDescription, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetProtocolDescriptionGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.ProtocolDescription ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ matrix.[generalI,i].AsFreeText
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a protocolURI, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetProtocolURIGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.ProtocolUri ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ matrix.[generalI,i].AsFreeText
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a protocolVersion, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetProtocolVersionGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.ProtocolVersion ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ matrix.[generalI,i].AsFreeText
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict an input, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetInputGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.Input io ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ BaseTypes.composeProcessInput header matrix.[generalI,i]
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict an output, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetOutputGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.Output io ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ BaseTypes.composeProcessOutput header matrix.[generalI,i]
+ |> Some
+ | _ -> None
+
+ /// If the given headers depict a comment, returns a function for parsing the values of the matrix to the values of this type
+ static member tryGetCommentGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.Comment c ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ //Comment.create(c,matrix.[generalI,i].AsFreeText)
+ Comment(c,matrix.[generalI,i].AsFreeText).ToString()
+ |> Some
+ | _ -> None
+
+ static member tryGetPerformerGetter (generalI : int) (header : CompositeHeader) =
+ match header with
+ | CompositeHeader.Performer ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let performer = matrix.[generalI,i].AsFreeText
+ let person = ARCtrl.ROCrate.Person.create(performer,performer)
+ person
+ |> Some
+ | _ -> None
+
+ /// Given the header sequence of an ArcTable, returns a function for parsing each row of the table to a process
+ static member getProcessGetter (processNameRoot : string) (headers : CompositeHeader seq) =
+
+ let headers =
+ headers
+ |> Seq.indexed
+
+ let valueHeaders =
+ headers
+ |> Seq.filter (snd >> fun h -> h.IsCvParamColumn)
+ |> Seq.indexed
+ |> Seq.toList
+
+ let charGetters =
+ valueHeaders
+ |> List.choose (fun (valueI,(generalI,header)) -> ProcessConversion.tryCharacteristicGetter generalI valueI header)
+
+ let factorValueGetters =
+ valueHeaders
+ |> List.choose (fun (valueI,(generalI,header)) -> ProcessConversion.tryFactorGetter generalI valueI header)
+
+ let parameterValueGetters =
+ valueHeaders
+ |> List.choose (fun (valueI,(generalI,header)) -> ProcessConversion.tryParameterGetter generalI valueI header)
+
+ let componentGetters =
+ valueHeaders
+ |> List.choose (fun (valueI,(generalI,header)) -> ProcessConversion.tryComponentGetter generalI valueI header)
+
+ let protocolTypeGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetProtocolTypeGetter generalI header)
+
+ let protocolREFGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetProtocolREFGetter generalI header)
+
+ let protocolDescriptionGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetProtocolDescriptionGetter generalI header)
+
+ let protocolURIGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetProtocolURIGetter generalI header)
+
+ let protocolVersionGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetProtocolVersionGetter generalI header)
+
+ let performerGetter =
+ headers
+ |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetPerformerGetter generalI header)
+
+ let commentGetters =
+ headers
+ |> Seq.choose (fun (generalI,header) -> ProcessConversion.tryGetCommentGetter generalI header)
+ |> Seq.toList
+
+ let inputGetter =
+ match headers |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetInputGetter generalI header) with
+ | Some inputGetter ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let chars = charGetters |> Seq.map (fun f -> f matrix i) |> ResizeArray
+ let input = inputGetter matrix i
+
+ if chars.Count > 0 then
+ Sample.setAdditionalProperties(input,chars)
+ input
+ |> ResizeArray.singleton
+ | None when charGetters.Length <> 0 ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let chars = charGetters |> Seq.map (fun f -> f matrix i) |> ResizeArray
+ Sample.createSample(name = $"{processNameRoot}_Input_{i}", additionalProperties = chars)
+ |> ResizeArray.singleton
+ | None ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ ResizeArray []
+
+ // This is a little more complex, as data and material objects can't contain factors. So in the case where the output of the table is a data object but factors exist. An additional sample object with the same name is created to contain the factors.
+ let outputGetter =
+ match headers |> Seq.tryPick (fun (generalI,header) -> ProcessConversion.tryGetOutputGetter generalI header) with
+ | Some outputGetter ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let factors = factorValueGetters |> Seq.map (fun f -> f matrix i) |> ResizeArray
+ let output = outputGetter matrix i
+ if factors.Count > 0 then
+ Sample.setAdditionalProperties(output,factors)
+ output
+ |> ResizeArray.singleton
+ | None when factorValueGetters.Length <> 0 ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ let factors = factorValueGetters |> Seq.map (fun f -> f matrix i) |> ResizeArray
+ Sample.createSample(name = $"{processNameRoot}_Output_{i}", additionalProperties = factors)
+ |> ResizeArray.singleton
+ | None ->
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+ ResizeArray []
+ fun (matrix : System.Collections.Generic.Dictionary<(int * int),CompositeCell>) i ->
+
+ let rowCount = matrix.Keys |> Seq.map snd |> Seq.max |> (+) 1
+ let pn =
+ if rowCount = 1 then processNameRoot
+ else ProcessConversion.composeProcessName processNameRoot i
+
+ let paramvalues = parameterValueGetters |> List.map (fun f -> f matrix i) |> Option.fromValueWithDefault [] |> Option.map ResizeArray
+ //let parameters = paramvalues |> Option.map (List.map (fun pv -> pv.Category.Value))
+
+ let comments = commentGetters |> List.map (fun f -> f matrix i) |> Option.fromValueWithDefault [] |> Option.map ResizeArray
+
+ let components = componentGetters |> List.map (fun f -> f matrix i) |> Option.fromValueWithDefault [] |> Option.map ResizeArray
+
+ let protocol : LDNode option =
+ let name = (protocolREFGetter |> Option.map (fun f -> f matrix i))
+ let protocolId = LabProtocol.genId(?name = name, processName = processNameRoot)
+ LabProtocol.create(
+ id = protocolId,
+ ?name = name,
+ ?description = (protocolDescriptionGetter |> Option.map (fun f -> f matrix i)),
+ ?intendedUse = (protocolTypeGetter |> Option.map (fun f -> f matrix i)),
+ //?comments = comments,
+ ?url = (protocolURIGetter |> Option.map (fun f -> f matrix i)),
+ ?version = (protocolVersionGetter |> Option.map (fun f -> f matrix i)),
+ ?labEquipments = components
+ )
+ |> Some
+
+ let input,output = inputGetter matrix i, outputGetter matrix i
+
+ let agent = performerGetter |> Option.map (fun f -> f matrix i)
+
+ LabProcess.create(
+ name = pn,
+ objects = input,
+ results = output,
+ ?agent = agent,
+ ?executesLabProtocol = protocol,
+ ?parameterValues = paramvalues,
+ ?disambiguatingDescriptions = comments
+
+ )
+
+ /// Groups processes by their name, or by the name of the protocol they execute
+ ///
+ /// Process names are taken from the Worksheet name and numbered: SheetName_1, SheetName_2, etc.
+ ///
+ /// This function decomposes this name into a root name and a number, and groups processes by root name.
+ static member groupProcesses (processes : LDNode list, ?graph : LDGraph, ?context : LDContext) =
+ processes
+ |> List.groupBy (fun p ->
+ match LabProcess.tryGetNameAsString (p, ?context = context), LabProcess.tryGetExecutesLabProtocol(p,?graph = graph, ?context = context) with
+ | Some name, _ when ProcessConversion.decomposeProcessName name |> snd |> Option.isSome ->
+ ProcessConversion.decomposeProcessName name |> fst
+ | _, Some protocol when LabProtocol.tryGetNameAsString (protocol, ?context = context) |> Option.isSome ->
+ LabProtocol.tryGetNameAsString (protocol, ?context = context) |> Option.defaultValue ""
+ | Some name, _ when name.Contains "_" ->
+ let lastUnderScoreIndex = name.LastIndexOf '_'
+ name.Remove lastUnderScoreIndex
+ | Some name, _ ->
+ name
+ | _, Some protocol ->
+ protocol.Id
+ | _ ->
+ Identifier.createMissingIdentifier()
+ )
+
+ /// Merges processes with the same name, protocol and param values
+ //let mergeIdenticalProcesses (processes : list) =
+ // processes
+ // |> List.groupBy (fun x ->
+ // if x.Name.IsSome && (x.Name.Value |> Process.decomposeName |> snd).IsSome then
+ // (x.Name.Value |> Process.decomposeName |> fst), HashCodes.boxHashOption x.ExecutesProtocol, x.ParameterValues |> Option.map HashCodes.boxHashSeq, x.Comments |> Option.map HashCodes.boxHashSeq
+ // elif x.ExecutesProtocol.IsSome && x.ExecutesProtocol.Value.Name.IsSome then
+ // x.ExecutesProtocol.Value.Name.Value, HashCodes.boxHashOption x.ExecutesProtocol, x.ParameterValues |> Option.map HashCodes.boxHashSeq, x.Comments |> Option.map HashCodes.boxHashSeq
+ // else
+ // Identifier.createMissingIdentifier(), HashCodes.boxHashOption x.ExecutesProtocol, x.ParameterValues |> Option.map HashCodes.boxHashSeq, x.Comments |> Option.map HashCodes.boxHashSeq
+ // )
+ // |> fun l ->
+ // l
+ // |> List.mapi (fun i ((n,_,_,_),processes) ->
+ // let pVs = processes.[0].ParameterValues
+ // let inputs = processes |> List.collect (fun p -> p.Inputs |> Option.defaultValue []) |> Option.fromValueWithDefault []
+ // let outputs = processes |> List.collect (fun p -> p.Outputs |> Option.defaultValue []) |> Option.fromValueWithDefault []
+ // let n = if l.Length > 1 then Process.composeName n i else n
+ // Process.create(Name = n,?ExecutesProtocol = processes.[0].ExecutesProtocol,?ParameterValues = pVs,?Inputs = inputs,?Outputs = outputs,?Comments = processes.[0].Comments)
+ // )
+
+
+ // Transform a isa json process into a isa tab row, where each row is a header+value list
+ static member processToRows (p : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let pvs =
+ LabProcess.getParameterValues(p, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun ppv -> BaseTypes.decomposeParameterValue(ppv, ?context = context), ColumnIndex.tryGetIndex ppv)
+ // Get the component
+ let components =
+ match LabProcess.tryGetExecutesLabProtocol(p, ?graph = graph, ?context = context) with
+ | Some prot ->
+ LabProtocol.getComponents(prot, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun ppv -> BaseTypes.decomposeComponent(ppv, ?context = context), ColumnIndex.tryGetIndex ppv)
+ | None -> ResizeArray []
+ // Get the values of the protocol
+ let protVals =
+ match LabProcess.tryGetExecutesLabProtocol(p, ?graph = graph, ?context = context) with
+ | Some prot ->
+ [
+ match LabProtocol.tryGetNameAsString (prot, ?context = context) with | Some name -> yield (CompositeHeader.ProtocolREF, CompositeCell.FreeText name) | None -> ()
+ match LabProtocol.tryGetDescriptionAsString (prot, ?context = context) with | Some desc -> yield (CompositeHeader.ProtocolDescription, CompositeCell.FreeText desc) | None -> ()
+ match LabProtocol.tryGetUrl (prot, ?context = context) with | Some uri -> yield (CompositeHeader.ProtocolUri, CompositeCell.FreeText uri) | None -> ()
+ match LabProtocol.tryGetVersionAsString(prot, ?context = context) with | Some version -> yield (CompositeHeader.ProtocolVersion, CompositeCell.FreeText version) | None -> ()
+ match ProcessConversion.tryGetProtocolType(prot, ?graph = graph, ?context = context) with
+ | Some intendedUse -> yield (CompositeHeader.ProtocolType, CompositeCell.Term intendedUse)
+ | None -> ()
+ ]
+ | None -> []
+ let comments =
+ LabProcess.getDisambiguatingDescriptionsAsString(p, ?context = context)
+ |> ResizeArray.map (fun c ->
+ let c = Comment.fromString c
+ CompositeHeader.Comment (Option.defaultValue "" c.Name),
+ CompositeCell.FreeText (Option.defaultValue "" c.Value)
+ )
+
+ let inputs = LabProcess.getObjects(p, ?graph = graph, ?context = context)
+ let outputs = LabProcess.getResults(p, ?graph = graph, ?context = context)
+
+ let inputs,outputs =
+ if inputs.Count = 0 && outputs.Count <> 0 then
+ ResizeArray.create outputs.Count None,
+ ResizeArray.map Option.Some outputs
+ elif inputs.Count <> 0 && outputs.Count = 0 then
+ ResizeArray.map Option.Some inputs,
+ ResizeArray.create inputs.Count None
+ else
+ ResizeArray.map Option.Some inputs,
+ ResizeArray.map Option.Some outputs
+
+
+ if inputs.Count = 0 && outputs.Count = 0 then
+ let vals =
+ [
+ yield! components
+ yield! pvs
+ ]
+ |> List.sortBy (snd >> Option.defaultValue 10000)
+ |> List.map fst
+ [
+ yield! protVals
+ yield! vals
+ yield! comments
+ ]
+ |> ResizeArray.singleton
+ else
+ // zip the inputs and outpus so they are aligned as rows
+ outputs
+ |> ResizeArray.zip inputs
+ // This grouping here and the picking of the "inputForCharas" etc is done, so there can be rows where data do have characteristics, which is not possible in isa json
+ |> ResizeArray.map (fun (i,o) ->
+ let chars =
+ match i with
+ | Some i ->
+ Sample.getCharacteristics(i, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun cv -> BaseTypes.decomposeCharacteristicValue(cv, ?context = context), ColumnIndex.tryGetIndex cv)
+ | None -> ResizeArray []
+ let factors =
+ match o with
+ | Some o ->
+ Sample.getFactors(o, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun fv -> BaseTypes.decomposeFactorValue(fv, ?context = context), ColumnIndex.tryGetIndex fv)
+ | None -> ResizeArray []
+
+
+ let vals =
+ [
+ yield! chars
+ yield! components
+ yield! pvs
+ yield! factors
+ ]
+ |> List.sortBy (snd >> Option.defaultValue 10000)
+ |> List.map fst
+ [
+ if i.IsSome then yield BaseTypes.decomposeProcessInput(i.Value, ?context = context)
+ yield! protVals
+ yield! vals
+ yield! comments
+ if o.IsSome then yield BaseTypes.decomposeProcessOutput(o.Value, ?context = context)
+ ]
+ )
+
+//[]
+//module CoreTypeExtensions =
+
+// type CompositeHeader with
+
+// member this.TryParameter() =
+// match this with
+// | CompositeHeader.Parameter oa -> Some (DefinedTerm.create (ParameterName = oa))
+// | _ -> None
+
+// member this.TryFactor() =
+// match this with
+// | CompositeHeader.Factor oa -> Some (Factor.create(FactorType = oa))
+// | _ -> None
+
+// member this.TryCharacteristic() =
+// match this with
+// | CompositeHeader.Characteristic oa -> Some (MaterialAttribute.create(CharacteristicType = oa))
+// | _ -> None
+
+// member this.TryComponent() =
+// match this with
+// | CompositeHeader.Component oa -> Some (Component.create(componentType = oa))
+// | _ -> None
+
+// type CompositeCell with
+
+// ///
+// /// This function is used to improve interoperability with ISA-JSON types. It is not recommended for default ARCtrl usage.
+// ///
+// ///
+// ///
+// static member fromValue(value : Value, ?unit : OntologyAnnotation) =
+// BaseTypes.cellOfValue (Some value) unit
+
+
+module CompositeRow =
+
+ let toProtocol (tableName : string) (row : (CompositeHeader*CompositeCell) seq) =
+ let id = tableName
+ row
+ |> Seq.fold (fun p hc ->
+ match hc with
+ | CompositeHeader.ProtocolType, CompositeCell.Term oa ->
+ LabProtocol.setIntendedUseAsDefinedTerm(p, BaseTypes.composeDefinedTerm oa)
+
+ | CompositeHeader.ProtocolVersion, CompositeCell.FreeText v ->
+ LabProtocol.setVersionAsString(p,v)
+
+ | CompositeHeader.ProtocolUri, CompositeCell.FreeText v ->
+ LabProtocol.setUrl(p,v)
+
+ | CompositeHeader.ProtocolDescription, CompositeCell.FreeText v ->
+ LabProtocol.setDescriptionAsString(p,v)
+
+ | CompositeHeader.ProtocolREF, CompositeCell.FreeText v ->
+ LabProtocol.setNameAsString(p,v)
+ //| CompositeHeader.Parameter oa, _ ->
+ // DefinedTerm.create
+ // let pp = ProtocolParameter.create(ParameterName = oa)
+ // Protocol.addParameter (pp) p
+ | CompositeHeader.Component _, CompositeCell.Term _
+ | CompositeHeader.Component _, CompositeCell.Unitized _ ->
+ let c = BaseTypes.composeComponent (fst hc) (snd hc)
+ let newC = ResizeArray.appendSingleton c (LabProtocol.getLabEquipments(p))
+ LabProtocol.setLabEquipments(p,newC)
+ | _ -> ()
+ p
+ ) (LabProtocol.create(id = id, name = tableName))
+
+[]
+module TableTypeExtensions =
+
+ type ArcTable with
+
+ /// Create a new table from an ISA protocol.
+ ///
+ /// The table will have at most one row, with the protocol information and the component values
+ static member fromProtocol (p : LDNode, ?graph : LDGraph, ?context : LDContext) : ArcTable =
+
+ let name = LabProtocol.getNameAsString(p, ?context = context)
+ let t = ArcTable.init name
+
+ //for pp in LabProtocol.getPa p.Parameters |> Option.defaultValue [] do
+
+ // //t.AddParameterColumn(pp, ?index = pp.TryGetColumnIndex())
+
+ // t.AddColumn(CompositeHeader.Parameter pp.ParameterName.Value, ?index = pp.TryGetColumnIndex())
+
+ for c in LabProtocol.getComponents(p, ?graph = graph, ?context = context) do
+ let h,v = BaseTypes.decomposeComponent(c, ?context = context)
+ t.AddColumn(
+ h,
+ cells = Array.singleton v,
+ ?index = c.TryGetColumnIndex())
+ LabProtocol.tryGetDescriptionAsString(p, ?context = context) |> Option.map (fun d -> t.AddProtocolDescriptionColumn([|d|])) |> ignore
+ LabProtocol.tryGetVersionAsString(p, ?context = context) |> Option.map (fun d -> t.AddProtocolVersionColumn([|d|])) |> ignore
+ ProcessConversion.tryGetProtocolType(p, ?context =context) |> Option.map (fun d -> t.AddProtocolTypeColumn([|d|])) |> ignore
+ LabProtocol.tryGetUrl(p, ?context = context) |> Option.map (fun d -> t.AddProtocolUriColumn([|d|])) |> ignore
+ t.AddProtocolNameColumn([|name|])
+ t
+
+ /// Returns the list of protocols executed in this ArcTable
+ member this.GetProtocols() : LDNode list =
+
+ if this.RowCount = 0 then
+ this.Headers
+ |> Seq.fold (fun (p : LDNode) h ->
+ match h with
+ //| CompositeHeader.Parameter oa ->
+ // let pp = ProtocolParameter.create(ParameterName = oa)
+ // Protocol.addParameter (pp) p
+ | CompositeHeader.Component oa ->
+ let n, na = oa.NameText, oa.TermAccessionOntobeeUrl
+ let c = PropertyValue.createComponent(n, "Empty Component Value", propertyID = na)
+ let newC = ResizeArray.appendSingleton c (LabProtocol.getLabEquipments p)
+ LabProtocol.setLabEquipments(p,newC)
+ | _ -> ()
+ p
+ ) (LabProtocol.create(id = Identifier.createMissingIdentifier(), name = this.Name))
+ |> List.singleton
+ else
+ List.init this.RowCount (fun i ->
+ this.GetRow(i, SkipValidation = true)
+ |> Seq.zip this.Headers
+ |> CompositeRow.toProtocol this.Name
+ )
+ |> List.distinct
+
+ /// Returns the list of processes specidified in this ArcTable
+ member this.GetProcesses() : LDNode list =
+ if this.RowCount = 0 then
+ //let input = ResizeArray [Sample.createSample(name = $"{this.Name}_Input", additionalProperties = ResizeArray [])]
+ //let output = ResizeArray [Sample.createSample(name = $"{this.Name}_Output", additionalProperties = ResizeArray [])]
+ LabProcess.create(name = this.Name(*, objects = input, results = output*))
+ |> List.singleton
+ else
+ let getter = ProcessConversion.getProcessGetter this.Name this.Headers
+ [
+ for i in 0..this.RowCount-1 do
+ yield getter this.Values i
+ ]
+ //|> ProcessConversion.mergeIdenticalProcesses
+
+
+ /// Create a new table from a list of processes
+ ///
+ /// The name will be used as the sheet name
+ ///
+ /// The processes SHOULD have the same headers, or even execute the same protocol
+ static member fromProcesses(name,ps : LDNode list, ?graph : LDGraph, ?context : LDContext) : ArcTable =
+ ps
+ |> List.collect (fun p -> ProcessConversion.processToRows(p,?context = context,?graph = graph) |> List.ofSeq)
+ |> ArcTableAux.Unchecked.alignByHeaders true
+ |> fun (headers, rows) -> ArcTable.create(name,headers,rows)
+
+ type ArcTables with
+
+ /// Return a list of all the processes in all the tables.
+ member this.GetProcesses() : LDNode list =
+ this.Tables
+ |> Seq.toList
+ |> List.collect (fun t -> t.GetProcesses())
+
+ /// Create a collection of tables from a list of processes.
+ ///
+ /// For this, the processes are grouped by nameroot ("nameroot_1", "nameroot_2" ...) or exectued protocol if no name exists
+ ///
+ /// Then each group is converted to a table with this nameroot as sheetname
+ static member fromProcesses (ps : LDNode list, ?graph : LDGraph, ?context : LDContext) : ArcTables =
+ ps
+ |> ProcessConversion.groupProcesses
+ |> List.map (fun (name,ps) ->
+ ps
+ |> List.collect (fun p -> ProcessConversion.processToRows(p,?graph = graph, ?context = context) |> List.ofSeq)
+ |> fun rows -> ArcTableAux.Unchecked.alignByHeaders true rows
+ |> fun (headers, rows) -> ArcTable.create(name,headers,rows)
+ )
+ |> ResizeArray
+ |> ArcTables
+
+
+
+type PersonConversion =
+
+ static member orcidKey = "ORCID"
+
+ static member composeAffiliation (affiliation : string) : LDNode =
+ try
+ ARCtrl.Json.Decode.fromJsonString Json.LDNode.decoder affiliation
+ with
+ | _ -> Organization.create(name = affiliation)
+
+ static member decomposeAffiliation (affiliation : LDNode, ?context : LDContext) : string =
+ let hasOnlyName =
+ affiliation.GetPropertyNames(?context = context)
+ |> Seq.filter(fun n -> n <> Organization.name)
+ |> Seq.isEmpty
+ if hasOnlyName then
+ Organization.getNameAsString(affiliation, ?context = context)
+ else
+ Json.LDNode.encoder affiliation
+ |> ARCtrl.Json.Encode.toJsonString 0
+
+ static member composeAddress (address : string) : obj =
+ try
+ ARCtrl.Json.Decode.fromJsonString Json.LDNode.decoder address
+ |> box
+ with
+ | _ -> address
+
+ static member decomposeAddress (address : obj) : string =
+ match address with
+ | :? string as s -> s
+ | :? LDNode as n ->
+ Json.LDNode.encoder n
+ |> ARCtrl.Json.Encode.toJsonString 0
+ | _ -> failwith "Address must be a string or a Json.LDNode"
+
+ static member composePerson (person : ARCtrl.Person) =
+ let givenName =
+ match person.FirstName with
+ | Some fn -> fn
+ | None -> failwith "Person must have a given name"
+ let jobTitles =
+ person.Roles
+ |> ResizeArray.map BaseTypes.composeDefinedTerm
+ |> Option.fromSeq
+ let disambiguatingDescriptions =
+ person.Comments
+ |> ResizeArray.map (fun c -> c.ToString())
+ |> Option.fromSeq
+ let address =
+ person.Address
+ |> Option.map PersonConversion.composeAddress
+ let affiliation =
+ person.Affiliation
+ |> Option.map PersonConversion.composeAffiliation
+ ARCtrl.ROCrate.Person.create(givenName, ?orcid = person.ORCID, ?affiliation = affiliation, ?email = person.EMail, ?familyName = person.LastName, ?jobTitles = jobTitles, ?additionalName = person.MidInitials, ?address = address, ?disambiguatingDescriptions = disambiguatingDescriptions, ?faxNumber = person.Fax, ?telephone = person.Phone)
+
+ static member decomposePerson (person : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let orcid = ORCID.tryGetOrcidNumber person.Id
+ let address =
+ match Person.tryGetAddressAsString(person, ?context = context) with
+ | Some s ->
+ Some s
+ | None ->
+ match Person.tryGetAddressAsPostalAddress(person, ?graph = graph, ?context = context) with
+ | Some a -> Some (PersonConversion.decomposeAddress a)
+ | None -> None
+ let roles =
+ Person.getJobTitlesAsDefinedTerm(person, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun r -> BaseTypes.decomposeDefinedTerm(r, ?context = context))
+ let comments =
+ Person.getDisambiguatingDescriptionsAsString(person, ?context = context)
+ |> ResizeArray.map Comment.fromString
+ let affiliation =
+ Person.tryGetAffiliation(person, ?graph = graph, ?context = context)
+ |> Option.map (fun a -> PersonConversion.decomposeAffiliation(a, ?context = context))
+ ARCtrl.Person.create(
+ firstName = Person.getGivenNameAsString(person, ?context = context),
+ ?lastName = Person.tryGetFamilyNameAsString(person, ?context = context),
+ ?midInitials = Person.tryGetAdditionalNameAsString(person, ?context = context),
+ ?email = Person.tryGetEmailAsString(person, ?context = context),
+ ?fax = Person.tryGetFaxNumberAsString(person, ?context = context),
+ ?phone = Person.tryGetTelephoneAsString(person, ?context = context),
+ ?orcid = orcid,
+ ?affiliation = affiliation,
+ roles = roles,
+ ?address = address,
+ comments = comments
+ )
+
+
+type ScholarlyArticleConversion =
+
+
+ static member doiKey = "DOI"
+
+ static member doiURL = "http://purl.obolibrary.org/obo/OBI_0002110"
+
+ static member pubmedIDKey = "PubMedID"
+
+ static member pubmedIDURL = "http://purl.obolibrary.org/obo/OBI_0001617"
+
+ static member composeDOI (doi : string) : LDNode =
+ PropertyValue.create(name = ScholarlyArticleConversion.doiKey, value = doi, propertyID = ScholarlyArticleConversion.doiURL)
+
+ static member tryDecomposeDOI (doi : LDNode, ?context : LDContext) : string option =
+ match
+ PropertyValue.tryGetNameAsString(doi, ?context = context),
+ PropertyValue.tryGetValueAsString(doi, ?context = context),
+ PropertyValue.tryGetPropertyIDAsString(doi, ?context = context)
+ with
+ | Some name, Some value, Some id when name = ScholarlyArticleConversion.doiKey && id = ScholarlyArticleConversion.doiURL ->
+ Some value
+ | _ -> None
+
+ static member composePubMedID (pubMedID : string) : LDNode =
+ PropertyValue.create(name = ScholarlyArticleConversion.pubmedIDKey, value = pubMedID, propertyID = ScholarlyArticleConversion.pubmedIDURL)
+
+ static member tryDecomposePubMedID (pubMedID : LDNode, ?context : LDContext) : string option =
+ match
+ PropertyValue.tryGetNameAsString(pubMedID, ?context = context),
+ PropertyValue.tryGetValueAsString(pubMedID, ?context = context),
+ PropertyValue.tryGetPropertyIDAsString(pubMedID, ?context = context)
+ with
+ | Some name, Some value, Some id when name = ScholarlyArticleConversion.pubmedIDKey && id = ScholarlyArticleConversion.pubmedIDURL ->
+ Some value
+ | _ -> None
+
+ static member composeAuthor (author : string) : LDNode =
+ try
+ ARCtrl.Json.Decode.fromJsonString Json.LDNode.decoder author
+ with
+ | _ -> ARCtrl.ROCrate.Person.create(givenName = author)
+
+ static member splitAuthors (a : string) =
+ let mutable bracketCount = 0
+ let authors = ResizeArray()
+ let sb = System.Text.StringBuilder()
+ for c in a do
+ if c = '{' then
+ bracketCount <- bracketCount + 1
+ sb.Append(c) |> ignore
+ elif c = '}' then
+ bracketCount <- bracketCount - 1
+ sb.Append(c) |> ignore
+ elif c = ',' && bracketCount = 0 then
+ authors.Add(sb.ToString())
+ sb.Clear() |> ignore
+ else
+ sb.Append(c) |> ignore
+ authors.Add(sb.ToString())
+ authors
+
+ static member composeAuthors (authors : string) : ResizeArray =
+ ScholarlyArticleConversion.splitAuthors authors
+ |> Seq.map ScholarlyArticleConversion.composeAuthor
+ |> ResizeArray
+
+ static member decomposeAuthor (author : LDNode, ?context : LDContext) : string =
+ let hasOnlyGivenName =
+ author.GetPropertyNames(?context = context)
+ |> Seq.filter(fun n -> n <> Person.givenName)
+ |> Seq.isEmpty
+ if hasOnlyGivenName then
+ Person.getGivenNameAsString(author, ?context = context)
+ else
+ Json.LDNode.encoder author
+ |> ARCtrl.Json.Encode.toJsonString 0
+
+ static member decomposeAuthors (authors : ResizeArray, ?context : LDContext) : string =
+ authors
+ |> ResizeArray.map (fun a -> ScholarlyArticleConversion.decomposeAuthor (a,?context = context))
+ |> String.concat ","
+
+ static member composeScholarlyArticle (publication : Publication) =
+ let title = match publication.Title with | Some t -> t | None -> failwith "Publication must have a title"
+ let authors =
+ publication.Authors
+ |> Option.map ScholarlyArticleConversion.composeAuthors
+ let comments =
+ publication.Comments
+ |> ResizeArray.map (BaseTypes.composeComment)
+ |> Option.fromSeq
+ let identifiers = ResizeArray [
+ if publication.DOI.IsSome && publication.DOI.Value <> "" then
+ ScholarlyArticleConversion.composeDOI publication.DOI.Value
+ if publication.PubMedID.IsSome && publication.PubMedID.Value <> "" then
+ ScholarlyArticleConversion.composePubMedID publication.PubMedID.Value
+ ]
+ let status = publication.Status |> Option.map BaseTypes.composeDefinedTerm
+ let scholarlyArticle =
+ ScholarlyArticle.create(
+ headline = title,
+ identifiers = identifiers,
+ ?authors = authors,
+ //?url = publication.DOI,
+ ?creativeWorkStatus = status,
+ ?comments = comments
+ )
+ scholarlyArticle
+
+ static member decomposeScholarlyArticle (sa : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let title = ScholarlyArticle.getHeadlineAsString(sa, ?context = context)
+ let authors =
+ ScholarlyArticle.getAuthors(sa, ?graph = graph, ?context = context)
+ |> Option.fromSeq
+ |> Option.map (fun a -> ScholarlyArticleConversion.decomposeAuthors(a, ?context = context))
+ let comments =
+ ScholarlyArticle.getComments(sa, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> BaseTypes.decomposeComment(c, ?context = context))
+ let status =
+ ScholarlyArticle.tryGetCreativeWorkStatus(sa, ?graph = graph, ?context = context)
+ |> Option.map (fun s -> BaseTypes.decomposeDefinedTerm(s, ?context = context))
+ let identifiers = ScholarlyArticle.getIdentifiersAsPropertyValue(sa, ?graph = graph, ?context = context)
+ let pubMedID = identifiers |> ResizeArray.tryPick (fun i -> ScholarlyArticleConversion.tryDecomposePubMedID(i, ?context = context))
+ let doi = identifiers |> ResizeArray.tryPick (fun i -> ScholarlyArticleConversion.tryDecomposeDOI(i, ?context = context))
+ ARCtrl.Publication.create(
+ title = title,
+ ?authors = authors,
+ ?status = status,
+ comments = comments,
+ ?doi = doi,
+ ?pubMedID = pubMedID
+ )
+
+type AssayConversion =
+
+ static member getDataFilesFromProcesses (processes : LDNode ResizeArray, ?graph : LDGraph, ?context : LDContext) =
+ let data =
+ processes
+ |> ResizeArray.collect (fun p ->
+ let inputs = LabProcess.getObjectsAsData(p, ?graph = graph, ?context = context)
+ let outputs = LabProcess.getResultsAsData(p, ?graph = graph, ?context = context)
+ ResizeArray.append inputs outputs
+ )
+ |> ResizeArray.distinct
+ let files =
+ data
+ |> ResizeArray.filter (fun d ->
+ DataAux.pathAndSelectorFromName d.Id |> snd |> Option.isNone
+ )
+ let filesFromfragments =
+ data
+ |> ResizeArray.filter (fun d ->
+ DataAux.pathAndSelectorFromName d.Id |> snd |> Option.isSome
+ )
+ |> ResizeArray.groupBy (fun d ->
+ DataAux.pathAndSelectorFromName d.Id |> fst
+ )
+ |> ResizeArray.map (fun (path,fragments) ->
+ let file =
+ match files |> ResizeArray.tryFind (fun d -> d.Id = path) with
+ | Some f -> f
+ | None ->
+ let comments =
+ File.getComments(fragments.[0], ?graph = graph, ?context = context)
+ |> Option.fromSeq
+ File.create(
+ id = path,
+ name = path,
+ ?comments = comments,
+ ?disambiguatingDescription = File.tryGetDisambiguatingDescriptionAsString(fragments.[0], ?context = context),
+ ?encodingFormat = File.tryGetEncodingFormatAsString(fragments.[0], ?context = context),
+ ?context = fragments.[0].TryGetContext()
+ )
+ Dataset.setHasParts(file, fragments,?context = context)
+ file
+ )
+ ResizeArray.append files filesFromfragments
+
+ static member composeAssay (assay : ArcAssay) =
+ let measurementMethod = assay.TechnologyType |> Option.map BaseTypes.composeDefinedTerm
+ let measurementTechnique = assay.TechnologyPlatform |> Option.map BaseTypes.composeDefinedTerm
+ let variableMeasured = assay.MeasurementType |> Option.map BaseTypes.composePropertyValueFromOA
+ let creators =
+ assay.Performers
+ |> ResizeArray.map (fun c -> PersonConversion.composePerson c)
+ |> Option.fromSeq
+ let processSequence =
+ ArcTables(assay.Tables).GetProcesses()
+ |> ResizeArray
+ |> Option.fromSeq
+ let dataFiles =
+ processSequence
+ |> Option.map AssayConversion.getDataFilesFromProcesses
+ let comments =
+ assay.Comments
+ |> ResizeArray.map (fun c -> BaseTypes.composeComment c)
+ |> Option.fromSeq
+ Dataset.createAssay(
+ identifier = assay.Identifier,
+ ?description = None, // TODO
+ ?creators = creators,
+ ?hasParts = dataFiles,
+ ?measurementMethod = measurementMethod,
+ ?measurementTechnique = measurementTechnique,
+ ?variableMeasured = variableMeasured,
+ ?abouts = processSequence,
+ ?comments = comments
+ )
+
+ static member decomposeAssay (assay : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let measurementMethod =
+ Dataset.tryGetMeasurementMethodAsDefinedTerm(assay, ?graph = graph, ?context = context)
+ |> Option.map (fun m -> BaseTypes.decomposeDefinedTerm(m, ?context = context))
+ let measurementTechnique =
+ Dataset.tryGetMeasurementTechniqueAsDefinedTerm(assay, ?graph = graph, ?context = context)
+ |> Option.map (fun m -> BaseTypes.decomposeDefinedTerm(m, ?context = context))
+ let variableMeasured =
+ Dataset.tryGetVariableMeasuredAsPropertyValue(assay, ?graph = graph, ?context = context)
+ |> Option.map (fun v -> BaseTypes.decomposePropertyValueToOA(v, ?context = context))
+ let perfomers =
+ Dataset.getCreators(assay, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> PersonConversion.decomposePerson(c, ?graph = graph, ?context = context))
+ //let dataFiles =
+ // Assay.getHasParts(assay, ?graph = graph, ?context = context)
+ // |> Option.fromSeq
+ // |> Option.map (fun df -> BaseTypes.decomposeFile(df, ?graph = graph, ?context = context))
+ let tables =
+ Dataset.getAboutsAsLabProcess(assay, ?graph = graph, ?context = context)
+ |> fun ps -> ArcTables.fromProcesses(List.ofSeq ps, ?graph = graph, ?context = context)
+ let comments =
+ Dataset.getComments(assay, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> BaseTypes.decomposeComment(c, ?context = context))
+ ArcAssay.create(
+ identifier = Dataset.getIdentifierAsString(assay, ?context = context),
+ ?measurementType = variableMeasured,
+ ?technologyType = measurementMethod,
+ ?technologyPlatform = measurementTechnique,
+ tables = tables.Tables,
+ performers = perfomers,
+ comments = comments
+ )
+
+type StudyConversion =
+
+ static member composeStudy (study : ArcStudy) =
+ let dateCreated = study.SubmissionDate |> Option.bind DateTime.tryFromString
+ let datePublished = study.PublicReleaseDate |> Option.bind DateTime.tryFromString
+ let dateModified = System.DateTime.Now
+ let publications =
+ study.Publications
+ |> ResizeArray.map (fun p -> ScholarlyArticleConversion.composeScholarlyArticle p)
+ |> Option.fromSeq
+ let creators =
+ study.Contacts
+ |> ResizeArray.map (fun c -> PersonConversion.composePerson c)
+ |> Option.fromSeq
+ let processSequence =
+ ArcTables(study.Tables).GetProcesses()
+ |> ResizeArray
+ |> Option.fromSeq
+ let dataFiles =
+ processSequence
+ |> Option.map AssayConversion.getDataFilesFromProcesses
+ let comments =
+ study.Comments
+ |> ResizeArray.map (fun c -> BaseTypes.composeComment c)
+ |> Option.fromSeq
+ Dataset.createStudy(
+ identifier = study.Identifier,
+ ?name = study.Title,
+ ?description = study.Description,
+ ?dateCreated = dateCreated,
+ ?datePublished = datePublished,
+ dateModified = dateModified,
+ ?creators = creators,
+ ?citations = publications,
+ ?hasParts = dataFiles,
+ ?abouts = processSequence,
+ ?comments = comments
+ )
+
+ static member decomposeStudy (study : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let dateCreated =
+ Dataset.tryGetDateCreatedAsDateTime(study, ?context = context)
+ |> Option.map DateTime.toString
+ let datePublished =
+ Dataset.tryGetDatePublishedAsDateTime(study, ?context = context)
+ |> Option.map DateTime.toString
+ let publications =
+ Dataset.getCitations(study, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun p -> ScholarlyArticleConversion.decomposeScholarlyArticle(p, ?graph = graph, ?context = context))
+ let creators =
+ Dataset.getCreators(study, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> PersonConversion.decomposePerson(c, ?graph = graph, ?context = context))
+ //let dataFiles =
+ // Study.getHasParts(study, ?graph = graph, ?context = context)
+ // |> Option.fromSeq
+ // |> Option.map (fun df -> BaseTypes.decomposeFile(df, ?graph = graph, ?context = context))
+ let tables =
+ Dataset.getAboutsAsLabProcess(study, ?graph = graph, ?context = context)
+ |> fun ps -> ArcTables.fromProcesses(List.ofSeq ps, ?graph = graph, ?context = context)
+ let comments =
+ Dataset.getComments(study, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> BaseTypes.decomposeComment(c, ?context = context))
+ ArcStudy.create(
+ identifier = Dataset.getIdentifierAsString(study, ?context = context),
+ ?title = Dataset.tryGetNameAsString(study, ?context = context),
+ ?description = Dataset.tryGetDescriptionAsString(study, ?context = context),
+ ?submissionDate = dateCreated,
+ ?publicReleaseDate = datePublished,
+ contacts = creators,
+ publications = publications,
+ tables = tables.Tables,
+ comments = comments
+ )
+
+type InvestigationConversion =
+
+ static member composeInvestigation (investigation : ArcInvestigation) =
+ let name = match investigation.Title with | Some t -> t | None -> failwith "Investigation must have a title"
+ let dateCreated = investigation.SubmissionDate |> Option.bind DateTime.tryFromString
+ let datePublished =
+ investigation.PublicReleaseDate
+ |> Option.bind DateTime.tryFromString
+ |> Option.defaultValue (System.DateTime.Now)
+ //let dateModified = System.DateTime.Now
+ let publications =
+ investigation.Publications
+ |> ResizeArray.map (fun p -> ScholarlyArticleConversion.composeScholarlyArticle p)
+ |> Option.fromSeq
+ let creators =
+ investigation.Contacts
+ |> ResizeArray.map (fun c -> PersonConversion.composePerson c)
+ |> Option.fromSeq
+ let comments =
+ investigation.Comments
+ |> ResizeArray.map (fun c -> BaseTypes.composeComment c)
+ |> Option.fromSeq
+ let hasParts =
+ investigation.Assays
+ |> ResizeArray.map (fun a -> AssayConversion.composeAssay a)
+ |> ResizeArray.append (investigation.Studies |> ResizeArray.map (fun s -> StudyConversion.composeStudy s))
+ |> Option.fromSeq
+ let mentions =
+ ResizeArray [] // TODO
+ |> Option.fromSeq
+ Dataset.createInvestigation(
+ identifier = investigation.Identifier,
+ name = name,
+ ?description = investigation.Description,
+ ?dateCreated = dateCreated,
+ datePublished = datePublished,
+ //dateModified = dateModified,
+ ?creators = creators,
+ ?citations = publications,
+ ?hasParts = hasParts,
+ ?mentions = mentions,
+ ?comments = comments
+ )
+
+ static member decomposeInvestigation (investigation : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let title =
+ match Dataset.tryGetNameAsString(investigation, ?context = context) with
+ | Some t -> Some t
+ | None -> Dataset.tryGetHeadlineAsString(investigation, ?context = context)
+ let dateCreated =
+ Dataset.tryGetDateCreatedAsDateTime(investigation, ?context = context)
+ |> Option.map DateTime.toString
+ let datePublished =
+ Dataset.tryGetDatePublishedAsDateTime(investigation, ?context = context)
+ |> Option.map DateTime.toString
+ let publications =
+ Dataset.getCitations(investigation, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun p -> ScholarlyArticleConversion.decomposeScholarlyArticle(p, ?graph = graph, ?context = context))
+ let creators =
+ Dataset.getCreators(investigation, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> PersonConversion.decomposePerson(c, ?graph = graph, ?context = context))
+ let datasets =
+ Dataset.getHasPartsAsDataset (investigation, ?graph = graph, ?context = context)
+ let studies =
+ datasets
+ |> ResizeArray.filter (fun d -> Dataset.validateStudy(d, ?context = context))
+ |> ResizeArray.map (fun d -> StudyConversion.decomposeStudy(d, ?graph = graph, ?context = context))
+ let assays =
+ datasets
+ |> ResizeArray.filter (fun d -> Dataset.validateAssay(d, ?context = context))
+ |> ResizeArray.map (fun d -> AssayConversion.decomposeAssay(d, ?graph = graph, ?context = context))
+ let comments =
+ Dataset.getComments(investigation, ?graph = graph, ?context = context)
+ |> ResizeArray.map (fun c -> BaseTypes.decomposeComment(c, ?context = context))
+ ArcInvestigation.create(
+ identifier = Dataset.getIdentifierAsString(investigation, ?context = context),
+ ?title = title,
+ ?description = Dataset.tryGetDescriptionAsString(investigation, ?context = context),
+ ?submissionDate = dateCreated,
+ ?publicReleaseDate = datePublished,
+ contacts = creators,
+ publications = publications,
+ studies = studies,
+ assays = assays,
+ comments = comments
+ )
+
+[]
+module TypeExtensions =
+
+ type ArcAssay with
+ member this.ToROCrateAssay() = AssayConversion.composeAssay this
+
+ static member fromROCrateAssay (a : LDNode, ?graph : LDGraph, ?context : LDContext) = AssayConversion.decomposeAssay(a, ?graph = graph, ?context = context)
+
+ type ArcStudy with
+ member this.ToROCrateStudy() = StudyConversion.composeStudy this
+
+ static member fromROCrateStudy (a : LDNode, ?graph : LDGraph, ?context : LDContext) = StudyConversion.decomposeStudy(a, ?graph = graph, ?context = context)
+
+ type ArcInvestigation with
+ member this.ToROCrateInvestigation() = InvestigationConversion.composeInvestigation this
+
+ static member fromROCrateInvestigation (a : LDNode, ?graph : LDGraph, ?context : LDContext) = InvestigationConversion.decomposeInvestigation(a, ?graph = graph, ?context = context)
+
+ type Dataset with
+ static member toArcAssay(a : LDNode, ?graph : LDGraph, ?context : LDContext) = AssayConversion.decomposeAssay(a, ?graph = graph, ?context = context)
+
+ static member fromArcAssay (a : ArcAssay) = AssayConversion.composeAssay a
+
+ static member toArcStudy(a : LDNode, ?graph : LDGraph, ?context : LDContext) = StudyConversion.decomposeStudy(a, ?graph = graph, ?context = context)
+
+ static member fromArcStudy (a : ArcStudy) = StudyConversion.composeStudy a
+
+ static member toArcInvestigation(a : LDNode, ?graph : LDGraph, ?context : LDContext) = InvestigationConversion.decomposeInvestigation(a, ?graph = graph, ?context = context)
+
+ static member fromArcInvestigation (a : ArcInvestigation) = InvestigationConversion.composeInvestigation a
+
diff --git a/src/ARCtrl/JsonIO/ARC.fs b/src/ARCtrl/JsonIO/ARC.fs
deleted file mode 100644
index a129ec8c..00000000
--- a/src/ARCtrl/JsonIO/ARC.fs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace ARCtrl.Json
-
-open Thoth.Json.Core
-
-open ARCtrl
-open ARCtrl.Helper
-
-module ARC =
-
- /// Functions for serializing and deserializing ARC objects to RO-Crate Root Data Entity
- ///
- /// See https://www.researchobject.org/ro-crate/1.1/root-data-entity.html for more information
- module ROCrate =
-
- let encoder (isa : ArcInvestigation) =
- [
- Encode.tryInclude "@type" Encode.string (Some "CreativeWork")
- Encode.tryInclude "@id" Encode.string (Some "ro-crate-metadata.json")
- Encode.tryInclude "about" Investigation.ROCrate.encoder (Some isa)
- "conformsTo", ROCrateContext.ROCrate.conformsTo_jsonvalue |> Some
- "@context", ROCrateContext.ROCrate.context_jsonvalue |> Some
- ]
- |> Encode.choose
- |> Encode.object
-
- let decoder : Decoder =
- Decode.object (fun get ->
- let isa = get.Optional.Field "about" Investigation.ROCrate.decoder
- isa
- )
\ No newline at end of file
diff --git a/src/ARCtrl/JsonIO/LDObject.fs b/src/ARCtrl/JsonIO/LDObject.fs
index 1a4fe343..7d909f6b 100644
--- a/src/ARCtrl/JsonIO/LDObject.fs
+++ b/src/ARCtrl/JsonIO/LDObject.fs
@@ -8,18 +8,35 @@ open Thoth.Json.Core
open DynamicObj
[]
-module LDObjectExtensions =
+module LDNodeExtensions =
- type LDObject with
+ type LDNode with
static member fromROCrateJsonString (s:string) =
- Decode.fromJsonString LDObject.decoder s
+ Decode.fromJsonString LDNode.decoder s
/// exports in json-ld format
static member toROCrateJsonString(?spaces) =
- fun (obj:LDObject) ->
- LDObject.encoder obj
+ fun (obj:LDNode) ->
+ LDNode.encoder obj
|> Encode.toJsonString (Encode.defaultSpaces spaces)
member this.ToROCrateJsonString(?spaces) =
- LDObject.toROCrateJsonString(?spaces=spaces) this
\ No newline at end of file
+ LDNode.toROCrateJsonString(?spaces=spaces) this
+
+[]
+module LDGraphExtensions =
+
+ type LDGraph with
+
+ static member fromROCrateJsonString (s:string) =
+ Decode.fromJsonString LDGraph.decoder s
+
+ /// exports in json-ld format
+ static member toROCrateJsonString(?spaces) =
+ fun (obj:LDGraph) ->
+ LDGraph.encoder obj
+ |> Encode.toJsonString (Encode.defaultSpaces spaces)
+
+ member this.ToROCrateJsonString(?spaces) =
+ LDGraph.toROCrateJsonString(?spaces=spaces) this
\ No newline at end of file
diff --git a/src/ARCtrl/ROCrateIO.fs b/src/ARCtrl/ROCrateIO.fs
new file mode 100644
index 00000000..d9250993
--- /dev/null
+++ b/src/ARCtrl/ROCrateIO.fs
@@ -0,0 +1,63 @@
+namespace ARCtrl.Json
+
+open Thoth.Json.Core
+
+open ARCtrl
+open ARCtrl.ROCrate
+open ARCtrl.Helper
+open ARCtrl.Conversion
+
+module ARC =
+
+ /// Functions for serializing and deserializing ARC objects to RO-Crate Root Data Entity
+ ///
+ /// See https://www.researchobject.org/ro-crate/1.1/root-data-entity.html for more information
+ type ROCrate =
+
+ static member getDefaultLicense() =
+ //let cw = LDNode("License", ResizeArray ["https://schema.org/CreativeWork"])
+ //cw.SetProperty("https://schema.org/name", "ALL RIGHTS RESERVED BY THE AUTHORS")
+ //cw.SetProperty("https://schema.org/about",LDRef "./")
+ //cw
+ "ALL RIGHTS RESERVED BY THE AUTHORS"
+
+ static member metadataFileDescriptor =
+ let id = "ro-crate-metadata.json"
+ let schemaType = ResizeArray ["http://schema.org/CreativeWork"]
+ let node = LDNode(id, schemaType)
+ node.SetProperty("http://purl.org/dc/terms/conformsTo", LDRef("https://w3id.org/ro/crate/1.1"))
+ node.SetProperty("http://schema.org/about", LDRef("./"))
+ node
+
+ static member encoder (isa : ArcInvestigation, ?license : obj) =
+ let license = match license with
+ | Some license -> license
+ | None -> ROCrate.getDefaultLicense()
+ let isa = isa.ToROCrateInvestigation()
+ Dataset.setSDDatePublishedAsDateTime(isa, System.DateTime.Now)
+ Dataset.setLicenseAsCreativeWork(isa, license)
+ let graph = isa.Flatten()
+ let context = LDContext(baseContexts=ResizeArray[Context.initV1_1();Context.initBioschemasContext()])
+ graph.SetContext(context)
+ graph.AddNode(ROCrate.metadataFileDescriptor)
+ graph.Compact_InPlace()
+ LDGraph.encoder graph
+
+ static member decoder : Decoder =
+ LDGraph.decoder
+ |> Decode.map (fun graph ->
+ match graph.TryGetNode("./") with
+ | Some node ->
+ ArcInvestigation.fromROCrateInvestigation(node, graph = graph, ?context = graph.TryGetContext())
+ | None ->
+ failwith "RO-Crate graph did not contain root data Entity"
+ )
+
+ static member decoderDeprecated : Decoder =
+ LDNode.decoder
+ |> Decode.map (fun ldnode ->
+ ldnode
+ |> Dataset.getAbouts
+ |> Seq.exactlyOne
+ |> ArcInvestigation.fromROCrateInvestigation
+ )
\ No newline at end of file
diff --git a/src/Core/ARCtrl.Core.fsproj b/src/Core/ARCtrl.Core.fsproj
index 942a2114..6bae06bb 100644
--- a/src/Core/ARCtrl.Core.fsproj
+++ b/src/Core/ARCtrl.Core.fsproj
@@ -9,8 +9,10 @@
+
+
diff --git a/src/Core/Data.fs b/src/Core/Data.fs
index 5c028f45..66317bdf 100644
--- a/src/Core/Data.fs
+++ b/src/Core/Data.fs
@@ -10,6 +10,7 @@ module DataAux =
sprintf "%s#%s" path selector
let pathAndSelectorFromName (name : string) =
+ let name = name.Trim('#')
let parts = name.Split('#')
if parts.Length = 2 then
parts.[0], Some parts.[1]
@@ -88,6 +89,28 @@ type Data(?id,?name : string,?dataType,?format,?selectorFormat,?comments) =
this.Name
|> Option.defaultValue ""
+ member this.GetAbsolutePathForAssay(assayIdentifier : string, ?checkExistenceFromRoot : string -> bool) =
+ let folderPath = $"assays/{assayIdentifier}/dataset/"
+ let checkExistenceFromRoot = Option.defaultValue (fun _ -> false) checkExistenceFromRoot
+ match this.FilePath with
+ | Some p ->
+ if checkExistenceFromRoot p || p.StartsWith("assays/") || p.StartsWith("studies/") || p.StartsWith("http:") || p.StartsWith("https:") then
+ p
+ else
+ folderPath + p.TrimStart('/')
+ | None -> failwith "Data does not have a file path"
+
+ member this.GetAbsolutePathForStudy(studyIdentifier : string, ?checkExistenceFromRoot : string -> bool) =
+ let folderPath = $"studies/{studyIdentifier}/resources/"
+ let checkExistenceFromRoot = Option.defaultValue (fun _ -> false) checkExistenceFromRoot
+ match this.FilePath with
+ | Some p ->
+ if checkExistenceFromRoot p || p.StartsWith("assays/") || p.StartsWith("studies/") || p.StartsWith("http:") || p.StartsWith("https:") then
+ p
+ else
+ folderPath + p.TrimStart('/')
+ | None -> failwith "Data does not have a file path"
+
member this.Copy() =
let nextComments = this.Comments |> ResizeArray.map (fun c -> c.Copy())
Data(?id=this.ID,?name=this.Name,?dataType=this.DataType,?format=this.Format,?selectorFormat=this.SelectorFormat,comments=nextComments)
diff --git a/src/Core/DataFile.fs b/src/Core/DataFile.fs
index b8c3ecef..6c23b72f 100644
--- a/src/Core/DataFile.fs
+++ b/src/Core/DataFile.fs
@@ -1,4 +1,4 @@
-namespace ARCtrl
+namespace ARCtrl
open ARCtrl
@@ -15,9 +15,16 @@ type DataFile =
member this.AsString =
match this with
- | RawDataFile -> "RawDataFileJson"
- | DerivedDataFile -> "DerivedDataFileJson"
- | ImageFile -> "ImageFileJson"
+ | RawDataFile -> "Raw Data File"
+ | DerivedDataFile -> "Derived Data File"
+ | ImageFile -> "Image File"
+
+ static member fromString (dt : string) =
+ match dt with
+ | "RawDataFileJson" | "Raw Data File" -> RawDataFile
+ | "DerivedDataFileJson" | "Derived Data File" -> DerivedDataFile
+ | "ImageFileJson" | "Image File" -> ImageFile
+ | _ -> failwith $"Invalid DataFile type: {dt}"
member this.IsDerivedData =
match this with
diff --git a/src/Core/Helper/Collections.fs b/src/Core/Helper/Collections.fs
index ce094bc9..0c738410 100644
--- a/src/Core/Helper/Collections.fs
+++ b/src/Core/Helper/Collections.fs
@@ -1,4 +1,4 @@
-namespace ARCtrl.Helper
+namespace ARCtrl.Helper
module Seq =
let inline compare (a: seq<'a>) (b: seq<'a>) =
@@ -28,6 +28,11 @@ module Option =
| Some v -> Some (f v)
| None -> d
+ /// If the value matches the default, a None is returned, else a Some is returned
+ let fromSeq (v : 'T when 'T :> System.Collections.IEnumerable) =
+ if Seq.isEmpty v then None
+ else Some v
+
module internal List =
let tryPickAndRemove (f : 'T -> 'U option) (lst : 'T list) =
@@ -44,6 +49,12 @@ module Dictionary =
open System.Collections.Generic
+ let addOrUpdate (key : 'Key) (value : 'T) (dict : Dictionary<'Key,'T>) =
+ if dict.ContainsKey key then
+ dict.[key] <- value
+ else
+ dict.Add(key,value)
+
let ofSeq (s : seq<'Key*'T>) =
let dict = Dictionary()
s
@@ -79,6 +90,20 @@ module Dictionary =
module ResizeArray =
+ open System.Collections.Generic
+
+ let create (i : int) (v : 'T) =
+ let a = ResizeArray<_>()
+ if i > 0 then
+ for _ in 1 .. i do
+ a.Add(v)
+ a
+
+ let singleton (a : 'T) =
+ let b = ResizeArray<_>()
+ b.Add(a)
+ b
+
let map f (a : ResizeArray<_>) =
let b = ResizeArray<_>()
for i in a do
@@ -158,4 +183,37 @@ module ResizeArray =
for i in a do
c.Add(i)
c.Add(b)
- c
\ No newline at end of file
+ c
+
+
+ // Make sure that output type matches
+ let groupBy (f : 'T -> 'a) (a : ResizeArray<'T>) : ResizeArray<'a*ResizeArray<'T>> =
+ Seq.groupBy f a
+ |> Seq.map (fun (k,v) -> k, ResizeArray v)
+ |> ResizeArray
+
+ let tryPick f (a : ResizeArray<'T>) =
+ let rec loop i =
+ if i < a.Count then
+ match f a.[i] with
+ | Some v -> Some v
+ | None -> loop (i + 1)
+ else None
+ loop 0
+
+ let zip (a : ResizeArray<'T>) (b : ResizeArray<'U>) =
+ let c = ResizeArray<_>()
+ let n = min a.Count b.Count
+ for i in 0 .. n - 1 do
+ c.Add(a.[i], b.[i])
+ c
+
+ let tryFind f (a : ResizeArray<'T>) =
+ let rec loop i =
+ if i < a.Count then
+ if f a.[i] then
+ Some a.[i]
+ else
+ loop (i + 1)
+ else None
+ loop 0
\ No newline at end of file
diff --git a/src/Core/Helper/DateTime.fs b/src/Core/Helper/DateTime.fs
new file mode 100644
index 00000000..ccfb5ecd
--- /dev/null
+++ b/src/Core/Helper/DateTime.fs
@@ -0,0 +1,6 @@
+module ARCtrl.Helper.DateTime
+
+let tryParse (s : string) =
+ match System.DateTime.TryParse(s) with
+ | true, datetime -> Some datetime
+ | _ -> None
diff --git a/src/Core/Helper/ORCID.fs b/src/Core/Helper/ORCID.fs
new file mode 100644
index 00000000..118df022
--- /dev/null
+++ b/src/Core/Helper/ORCID.fs
@@ -0,0 +1,27 @@
+module ARCtrl.Helper.ORCID
+
+open ARCtrl.Helper.Regex.ActivePatterns
+
+[]
+let orcidPattern = @"[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]"
+
+let orcidRegex = System.Text.RegularExpressions.Regex(orcidPattern)
+
+let tryGetOrcidNumber (orcid : string) =
+ let m = orcidRegex.Match(orcid)
+ if m.Success then
+ Some m.Value
+ else
+ None
+
+let orcidPrefix = "http://orcid.org/"
+
+let (|ORCID|_|) input =
+ match input with
+ | Regex orcidPattern r -> Some r
+ | _ -> None
+
+let tryGetOrcidURL (orcid : string) =
+ match orcid with
+ | ORCID orcid -> Some $"{orcidPrefix}{orcid}"
+ | _ -> None
\ No newline at end of file
diff --git a/src/Json/ARC.fs b/src/Json/ARC.fs
deleted file mode 100644
index a129ec8c..00000000
--- a/src/Json/ARC.fs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace ARCtrl.Json
-
-open Thoth.Json.Core
-
-open ARCtrl
-open ARCtrl.Helper
-
-module ARC =
-
- /// Functions for serializing and deserializing ARC objects to RO-Crate Root Data Entity
- ///
- /// See https://www.researchobject.org/ro-crate/1.1/root-data-entity.html for more information
- module ROCrate =
-
- let encoder (isa : ArcInvestigation) =
- [
- Encode.tryInclude "@type" Encode.string (Some "CreativeWork")
- Encode.tryInclude "@id" Encode.string (Some "ro-crate-metadata.json")
- Encode.tryInclude "about" Investigation.ROCrate.encoder (Some isa)
- "conformsTo", ROCrateContext.ROCrate.conformsTo_jsonvalue |> Some
- "@context", ROCrateContext.ROCrate.context_jsonvalue |> Some
- ]
- |> Encode.choose
- |> Encode.object
-
- let decoder : Decoder =
- Decode.object (fun get ->
- let isa = get.Optional.Field "about" Investigation.ROCrate.decoder
- isa
- )
\ No newline at end of file
diff --git a/src/Json/ARCtrl.Json.fsproj b/src/Json/ARCtrl.Json.fsproj
index 64095531..cd657d89 100644
--- a/src/Json/ARCtrl.Json.fsproj
+++ b/src/Json/ARCtrl.Json.fsproj
@@ -35,6 +35,11 @@
+
+
+
+
+
@@ -75,8 +80,6 @@
-
-
diff --git a/src/Json/Decode.fs b/src/Json/Decode.fs
index a52231f0..720991d0 100644
--- a/src/Json/Decode.fs
+++ b/src/Json/Decode.fs
@@ -93,9 +93,7 @@ module Decode =
decoder.Decode(helpers,value)
}
-
-
- let resizeArray (decoder: Decoder<'value>) : Decoder> =
+ let resizeArrayOrSingleton (decoder: Decoder<'value>) : Decoder> =
{ new Decoder> with
member _.Decode(helpers, value) =
if helpers.isArray value then
@@ -123,7 +121,7 @@ module Decode =
Ok acc
)
else
- ("", BadPrimitive("an array", value)) |> Error
+ decoder.Decode(helpers, value) |> Result.map (fun x -> ResizeArray[x])
}
let datetime: Decoder =
diff --git a/src/Json/Encode.fs b/src/Json/Encode.fs
index fabebd65..58c9932c 100644
--- a/src/Json/Encode.fs
+++ b/src/Json/Encode.fs
@@ -63,4 +63,12 @@ module Encode =
let addPropertyToObject (name : string) (value : Json) (obj : Json) =
match obj with
| Json.Object kvs -> Json.Object (Seq.append kvs [name, value] )
- | _ -> failwith "Expected object"
\ No newline at end of file
+ | _ -> failwith "Expected object"
+
+ let resizeArrayOrSingleton (encoder : 'T -> IEncodable) (values: ResizeArray<'T>) =
+ if values.Count = 1 then
+ values.[0] |> encoder
+ else
+ values
+ |> Seq.map encoder
+ |> Encode.seq
\ No newline at end of file
diff --git a/src/Json/ROCrate/LDContext.fs b/src/Json/ROCrate/LDContext.fs
new file mode 100644
index 00000000..3b1aa8aa
--- /dev/null
+++ b/src/Json/ROCrate/LDContext.fs
@@ -0,0 +1,64 @@
+namespace ARCtrl.Json
+
+open ARCtrl
+open System
+open ARCtrl.ROCrate
+open Thoth.Json.Core
+open DynamicObj
+
+module LDContext =
+
+ let decoder : Decoder =
+ { new Decoder with
+ member this.Decode(helpers, value) =
+ if helpers.isObject value then
+ let getters = Decode.Getters(helpers, value)
+ let properties = helpers.getProperties value
+ let builder =
+ fun (get : Decode.IGetters) ->
+ let o = LDContext()
+ for property in properties do
+ if property <> "@id" && property <> "@type" then
+ o.AddMapping(property,get.Required.Field property Decode.string)
+ o
+ let result = builder getters
+ match getters.Errors with
+ | [] -> Ok result
+ | fst :: _ as errors ->
+ if errors.Length > 1 then
+ ("", BadOneOf errors) |> Error
+ else
+ Error fst
+ elif helpers.isString value then
+ let s = helpers.asString value
+ if s = Context.proxy_V1_2DRAFT then
+ Ok (Context.initV1_2DRAFT())
+ elif s = Context.proxy_V1_1 then
+ Ok (Context.initV1_1())
+ else
+ ("", BadPrimitive("an object", value)) |> Error
+ elif helpers.isArray value then
+ match Decode.resizeArray(this).Decode(helpers,value) with
+ | Ok baseContexts -> Ok (LDContext(baseContexts = baseContexts))
+ | Error e -> Error e
+ else
+ ("", BadPrimitive("an object", value)) |> Error
+ }
+
+ let rec encoder (ctx: LDContext) =
+ match ctx.Name with
+ | Some Context.proxy_V1_2DRAFT -> Encode.string Context.proxy_V1_2DRAFT
+ | Some Context.proxy_V1_1 -> Encode.string Context.proxy_V1_1
+ | _ ->
+ let mappings =
+ ctx.Mappings
+ |> Seq.map (fun kv -> kv.Key, kv.Value |> string |> Encode.string )
+ |> Encode.object
+ if ctx.BaseContexts.Count = 0 then
+ mappings
+ elif ctx.BaseContexts.Count = 1 && ctx.Mappings.Count = 0 then
+ ctx.BaseContexts.[0] |> encoder
+ else
+ ctx.BaseContexts |> Seq.map encoder
+ |> Seq.append [ if ctx.Mappings.Count <> 0 then mappings ]
+ |> Encode.seq
\ No newline at end of file
diff --git a/src/Json/ROCrate/LDGraph.fs b/src/Json/ROCrate/LDGraph.fs
new file mode 100644
index 00000000..08b53cae
--- /dev/null
+++ b/src/Json/ROCrate/LDGraph.fs
@@ -0,0 +1,56 @@
+namespace ARCtrl.Json
+
+open ARCtrl
+open System
+open ARCtrl.ROCrate
+open Thoth.Json.Core
+open DynamicObj
+
+
+module rec LDGraph =
+
+ let encoder(obj: LDGraph) =
+
+ [
+ Encode.tryInclude "@id" Encode.string obj.Id
+ Encode.tryInclude "@context" LDContext.encoder (obj.TryGetContext())
+ for kv in (obj.GetProperties true) do
+ let l = kv.Key.ToLower()
+ if l <> "id" && l <> "@context" && l <> "nodes" && l <> "mappings" then
+ kv.Key, Some (LDNode.genericEncoder kv.Value)
+ "@graph", obj.Nodes |> Seq.map LDNode.encoder |> Encode.seq |> Some
+ ]
+ |> Encode.choose
+ |> Encode.object
+
+
+ let decoder : Decoder =
+ { new Decoder with
+ member _.Decode(helpers, value) =
+ if helpers.isObject value then
+ let getters = Decode.Getters(helpers, value)
+ let properties = helpers.getProperties value
+ let builder =
+ fun (get : Decode.IGetters) ->
+ let id = get.Optional.Field "@id" Decode.string
+ let context = get.Optional.Field "@context" LDContext.decoder
+ let nodes = get.Required.Field "@graph" (Decode.seq LDNode.decoder)
+ let o = LDGraph(?id = id, ?context = context)
+ for property in properties do
+ if property <> "@id" && property <> "@graph" && property <> "@context" then
+ o.SetProperty(property,get.Required.Field property LDNode.genericDecoder)
+ for node in nodes do
+ o.AddNode node
+ o
+ let result = builder getters
+ match getters.Errors with
+ | [] -> Ok result
+ | fst :: _ as errors ->
+ if errors.Length > 1 then
+ ("", BadOneOf errors) |> Error
+ else
+ Error fst
+ else ("", BadPrimitive("an object", value)) |> Error
+
+ }
+
diff --git a/src/Json/LDObject.fs b/src/Json/ROCrate/LDNode.fs
similarity index 67%
rename from src/Json/LDObject.fs
rename to src/Json/ROCrate/LDNode.fs
index a9aae98c..888e58e4 100644
--- a/src/Json/LDObject.fs
+++ b/src/Json/ROCrate/LDNode.fs
@@ -6,8 +6,8 @@ open ARCtrl.ROCrate
open Thoth.Json.Core
open DynamicObj
-module rec LDObject =
+module rec LDNode =
#if !FABLE_COMPILER
let (|SomeObj|_|) =
// create generalized option type
@@ -36,7 +36,9 @@ module rec LDObject =
| :? bool as b -> Encode.bool b
| :? float as f -> Encode.float f
| :? DateTime as d -> Encode.dateTime d
- | :? LDObject as o -> encoder o
+ | :? LDValue as v -> LDValue.encoder v
+ | :? LDRef as r -> LDRef.encoder r
+ | :? LDNode as o -> encoder o
#if !FABLE_COMPILER
| SomeObj o -> genericEncoder o
#endif
@@ -44,21 +46,37 @@ module rec LDObject =
| :? System.Collections.IEnumerable as l -> [ for x in l -> genericEncoder x] |> Encode.list
| _ -> failwith "Unknown type"
- let rec encoder(obj: LDObject) =
- obj.GetProperties true
- |> Seq.choose (fun kv ->
- let l = kv.Key.ToLower()
- if l <> "id" && l <> "schematype" && l <> "additionaltype" then
- Some(kv.Key, genericEncoder kv.Value)
- else
- None
+ let rec encoder(obj: LDNode) =
+ //obj.GetProperties true
+ //|> Seq.choose (fun kv ->
+ // let l = kv.Key.ToLower()
+ // if l <> "id" && l <> "schematype" && l <> "additionaltype" && l <> "@context" then
+ // Some(kv.Key, genericEncoder kv.Value)
+ // else
+ // None
- )
- |> Seq.append [
- "@id", Encode.string obj.Id
- "@type", Encode.string obj.SchemaType
- if obj.AdditionalType.IsSome then
- "additionalType", Encode.string obj.AdditionalType.Value
+ //)
+ //|> Seq.append [
+ // "@id", Encode.string obj.Id
+ // "@type", LDType.encoder obj.SchemaType
+ // if obj.AdditionalType.IsSome then
+ // "additionalType", Encode.string obj.AdditionalType.Value
+ // match obj.TryGetContext() with
+ // | Some ctx -> "@context", LDContext.encoder ctx
+ // | _ -> ()
+ //]
+ [
+ yield "@id", Encode.string obj.Id
+ yield "@type", Encode.resizeArrayOrSingleton Encode.string obj.SchemaType
+ if obj.AdditionalType.Count <> 0 then
+ yield "additionalType", Encode.resizeArrayOrSingleton Encode.string obj.AdditionalType
+ match obj.TryGetContext() with
+ | Some ctx -> yield "@context", LDContext.encoder ctx
+ | _ -> ()
+ for kv in (obj.GetProperties true) do
+ let l = kv.Key.ToLower()
+ if l <> "id" && l <> "schematype" && l <> "additionaltype" && l <> "@context" && (l.StartsWith("init@") |> not) && (l.StartsWith("init_") |> not)then
+ yield kv.Key, genericEncoder kv.Value
]
|> Encode.object
@@ -67,20 +85,23 @@ module rec LDObject =
/// If expectObject is set to true, decoder fails if top-level value is not an ROCrate object
let rec getDecoder (expectObject : bool) : Decoder =
let rec decode(expectObject) =
- let decodeObject : Decoder =
- { new Decoder with
+ let decodeObject : Decoder =
+ { new Decoder with
member _.Decode(helpers, value) =
if helpers.isObject value then
let getters = Decode.Getters(helpers, value)
let properties = helpers.getProperties value
let builder =
fun (get : Decode.IGetters) ->
- let t = get.Required.Field "@type" Decode.string
+ let t = get.Required.Field "@type" (Decode.resizeArrayOrSingleton Decode.string)
let id = get.Required.Field "@id" Decode.string
- let o = LDObject(id,t)
+ let context = get.Optional.Field "@context" LDContext.decoder
+ let at = get.Optional.Field "additionalType" (Decode.resizeArrayOrSingleton Decode.string)
+ let o = LDNode(id, t, ?additionalType = at)
for property in properties do
- if property <> "@id" && property <> "@type" then
+ if property <> "@id" && property <> "@type" && property <> "@context" then
o.SetProperty(property,get.Required.Field property (decode(false)))
+ if context.IsSome then o.SetContext context.Value
o
let result = builder getters
match getters.Errors with
@@ -127,15 +148,16 @@ module rec LDObject =
Decode.map box (decodeObject)
else
Decode.oneOf [
+ Decode.map box (LDValue.decoder)
Decode.map box (decodeObject)
+ Decode.map box (LDRef.decoder)
Decode.map box (resizeArray)
Decode.map box (Decode.string)
Decode.map box (Decode.int)
Decode.map box (Decode.decimal)
-
]
decode(expectObject)
- let decoder : Decoder = Decode.map unbox (getDecoder(true))
+ let decoder : Decoder = Decode.map unbox (getDecoder(true))
let genericDecoder : Decoder = getDecoder(false)
diff --git a/src/Json/ROCrate/LDRef.fs b/src/Json/ROCrate/LDRef.fs
new file mode 100644
index 00000000..db849adf
--- /dev/null
+++ b/src/Json/ROCrate/LDRef.fs
@@ -0,0 +1,22 @@
+namespace ARCtrl.Json
+
+open ARCtrl
+open System
+open ARCtrl.ROCrate
+open Thoth.Json.Core
+open DynamicObj
+
+module LDRef =
+
+ let decoder : Decoder =
+ Decode.object (fun decoders ->
+ let id = decoders.Required.Field "@id" Decode.string
+ LDRef(id)
+ )
+
+ let encoder (r: LDRef) =
+ [
+ "@id", Encode.string r.Id
+ ]
+ |> Encode.object
+
diff --git a/src/Json/ROCrate/LDValue.fs b/src/Json/ROCrate/LDValue.fs
new file mode 100644
index 00000000..b0026092
--- /dev/null
+++ b/src/Json/ROCrate/LDValue.fs
@@ -0,0 +1,39 @@
+namespace ARCtrl.Json
+
+open ARCtrl
+open System
+open ARCtrl.ROCrate
+open Thoth.Json.Core
+open DynamicObj
+
+module LDValue =
+
+ let genericDecoder =
+ Decode.oneOf [
+ Decode.map box Decode.string
+ Decode.map box Decode.int
+ Decode.map box Decode.decimal
+ ]
+
+ let genericEncoder (value : obj) =
+ match value with
+ | :? string as s -> Encode.string s
+ | :? int as i -> Encode.int i
+ | :? bool as b -> Encode.bool b
+ | :? float as f -> Encode.float f
+ | :? DateTime as d -> Encode.dateTime d
+ | _ -> failwith "Unknown type"
+
+ let decoder : Decoder =
+ Decode.object (fun decoders ->
+ let value = decoders.Required.Field "@value" genericDecoder
+ let valueType = decoders.Optional.Field "@type" Decode.string
+ LDValue(value, ?valueType = valueType)
+ )
+
+ let encoder (v: LDValue) =
+ [
+ "@value", genericEncoder v.Value
+ "@type", Encode.string v.ValueType
+ ]
+ |> Encode.object
diff --git a/src/ROCrate/ARCtrl.ROCrate.fsproj b/src/ROCrate/ARCtrl.ROCrate.fsproj
index e5dfc51f..60833990 100644
--- a/src/ROCrate/ARCtrl.ROCrate.fsproj
+++ b/src/ROCrate/ARCtrl.ROCrate.fsproj
@@ -7,19 +7,23 @@
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -27,4 +31,7 @@
+
+
+
diff --git a/src/ROCrate/ArcROCrateMetadata.fs b/src/ROCrate/ArcROCrateMetadata.fs
index 2fd930b1..23abcb0d 100644
--- a/src/ROCrate/ArcROCrateMetadata.fs
+++ b/src/ROCrate/ArcROCrateMetadata.fs
@@ -2,9 +2,9 @@ namespace ARCtrl.ROCrate
open DynamicObj
-type ArcROCrateMetadata(?about : LDObject) as this =
+type ArcROCrateMetadata(?about : LDNode) as this =
- inherit LDObject(id = "ro-crate-metadata",schemaType = "CreativeWork")
+ inherit LDNode(id = "ro-crate-metadata",schemaType = ResizeArray([|"CreativeWork"|]))
do DynObj.setOptionalProperty (nameof about) about this
@@ -15,10 +15,10 @@ type ArcROCrateMetadata(?about : LDObject) as this =
do
let context = LDContext()
- context.SetProperty("sdo", "http://schema.org/")
- context.SetProperty("arc", "http://purl.org/nfdi4plants/ontology/")
- context.SetProperty("CreativeWork", "sdo:CreativeWork")
- context.SetProperty("about", "sdo:about")
- context.SetProperty("conformsTo", "sdo:conformsTo")
+ context.AddMapping("sdo", "http://schema.org/")
+ context.AddMapping("arc", "http://purl.org/nfdi4plants/ontology/")
+ context.AddMapping("CreativeWork", "sdo:CreativeWork")
+ context.AddMapping("about", "sdo:about")
+ context.AddMapping("conformsTo", "sdo:conformsTo")
this.SetProperty("@context", context)
diff --git a/src/ROCrate/DynObjExtensions.fs b/src/ROCrate/DynObjExtensions.fs
index 1cb8fe70..a84d676f 100644
--- a/src/ROCrate/DynObjExtensions.fs
+++ b/src/ROCrate/DynObjExtensions.fs
@@ -12,4 +12,10 @@ module DynObj =
| Some value -> value
| None -> raise (System.InvalidCastException($"Property '{propertyName}' is set on this '{className}' object but cannot be cast to '{(typeof<'TPropertyValue>).Name}'"))
else
- raise (System.MissingMemberException($"No property '{propertyName}' set on this '{className}' object although it is mandatory. Was it created correctly?"))
\ No newline at end of file
+ raise (System.MissingMemberException($"No property '{propertyName}' set on this '{className}' object although it is mandatory. Was it created correctly?"))
+
+ let inline tryGetTypedPropertyValueAsResizeArray<'T> (name : string) (obj : DynamicObj) =
+ match obj.TryGetPropertyValue(name) with
+ | Some (:? ResizeArray<'T> as ra) -> Some ra
+ | Some (:? 'T as singleton) -> Some (ResizeArray [singleton])
+ | _ -> None
\ No newline at end of file
diff --git a/src/ROCrate/Generic/Comment.fs b/src/ROCrate/Generic/Comment.fs
new file mode 100644
index 00000000..818834bb
--- /dev/null
+++ b/src/ROCrate/Generic/Comment.fs
@@ -0,0 +1,62 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+///
+[]
+type Comment =
+
+ static member schemaType = "http://schema.org/Comment"
+
+ static member text = "http://schema.org/text"
+
+ static member name = "http://schema.org/name"
+
+ static member tryGetTextAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(Comment.text, ?context = context) with
+ | Some (:? string as tc) -> Some tc
+ | _ -> None
+
+ static member getTextAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(Comment.text, ?context = context) with
+ | Some (:? string as tc) -> tc
+ | Some _ -> failwith $"Property of `text` of object with @id `{dt.Id}` was not a string"
+ | _ -> failwith $"Could not access property `text` of object with @id `{dt.Id}`"
+
+ static member setTextAsString(dt : LDNode, text : string, ?context : LDContext) =
+ dt.SetProperty(Comment.text, text, ?context = context)
+
+ static member tryGetNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(Comment.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(Comment.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `name` of object with @id `{dt.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{dt.Id}`"
+
+ static member setNameAsString(dt : LDNode, name : string, ?context : LDContext) =
+ dt.SetProperty(Comment.name, name, ?context = context)
+
+ static member genID(name : string, ?text : string) =
+ match text with
+ | Some t -> $"#Comment_{name}_{t}"
+ | None -> $"#Comment_{name}"
+ |> Helper.ID.clean
+
+ static member validate(dt : LDNode, ?context : LDContext) =
+ dt.HasType(Comment.schemaType, ?context = context)
+ && dt.HasProperty(Comment.name, ?context = context)
+
+ static member create(name : string, ?id : string, ?text : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Comment.genID(name, ?text = text)
+ let dt = LDNode(id, ResizeArray [Comment.schemaType], ?context = context)
+ dt.SetProperty(Comment.name, name, ?context = context)
+ dt.SetOptionalProperty(Comment.text, text, ?context = context)
+ dt
\ No newline at end of file
diff --git a/src/ROCrate/Generic/Dataset.fs b/src/ROCrate/Generic/Dataset.fs
new file mode 100644
index 00000000..e86629f1
--- /dev/null
+++ b/src/ROCrate/Generic/Dataset.fs
@@ -0,0 +1,333 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+open ARCtrl.Helper
+
+[]
+type Dataset =
+
+ static member schemaType = "http://schema.org/Dataset"
+
+ static member additionalType = "http://schema.org/Dataset"
+
+ static member identifier = "http://schema.org/identifier"
+
+ static member creator = "http://schema.org/creator"
+
+ static member dateCreated = "http://schema.org/dateCreated"
+
+ static member datePublished = "http://schema.org/datePublished"
+
+ static member sdDatePublished = "http://schema.org/datePublished"
+
+ static member license = "http://schema.org/license"
+
+ static member dateModified = "http://schema.org/dateModified"
+
+ static member description = "http://schema.org/description"
+
+ static member hasPart = "http://schema.org/hasPart"
+
+ static member headline = "http://schema.org/headline"
+
+ static member name = "http://schema.org/name"
+
+ static member citation = "http://schema.org/citation"
+
+ static member comment = "http://schema.org/comment"
+
+ static member mentions = "http://schema.org/mentions"
+
+ static member url = "http://schema.org/url"
+
+ static member about = "http://schema.org/about"
+
+ static member measurementMethod = "http://schema.org/measurementMethod"
+
+ static member measurementTechnique = "http://schema.org/measurementTechnique"
+
+ static member variableMeasured = "http://schema.org/variableMeasured"
+
+ static member tryGetIdentifierAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.identifier, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getIdentifierAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.identifier, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"property `identifier` of object with @id `{lp.Id}` was not a string"
+ | _ -> failwith $"Could not access property `identifier` of object with @id `{lp.Id}`"
+
+ static member setIdentifierAsString(lp : LDNode, identifier : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.identifier, identifier, ?context = context)
+
+ static member getCreators(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter (p : LDNode) (ctx : LDContext option) = Person.validate(p, ?context = ctx)
+ lp.GetPropertyNodes(Dataset.creator, filter = filter, ?graph = graph, ?context = context)
+
+ //static member getCreatorsAsPerson(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ // let filter (p : LDNode) (ctx : LDContext option) = Person.validate(p, ?context = ctx)
+ // lp.GetPropertyNodes(Dataset.creator, filter = filter, ?graph = graph, ?context = context)
+
+ static member setCreators(lp : LDNode, creators : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(Dataset.creator, creators, ?context = context)
+
+ static member tryGetDateCreatedAsDateTime(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.dateCreated, ?context = context) with
+ | Some (:? System.DateTime as n) -> Some n
+ | _ -> None
+
+ static member setDateCreatedAsDateTime(lp : LDNode, dateCreated : System.DateTime, ?context : LDContext) =
+ lp.SetProperty(Dataset.dateCreated, dateCreated, ?context = context)
+
+ static member tryGetDatePublishedAsDateTime(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.datePublished, ?context = context) with
+ | Some (:? System.DateTime as n) -> Some n
+ | _ -> None
+
+ static member setDatePublishedAsDateTime(lp : LDNode, datePublished : System.DateTime, ?context : LDContext) =
+ lp.SetProperty(Dataset.datePublished, datePublished, ?context = context)
+
+ static member tryGetSDDatePublishedAsDateTime(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.sdDatePublished, ?context = context) with
+ | Some (:? System.DateTime as n) -> Some n
+ | _ -> None
+
+ static member setSDDatePublishedAsDateTime(lp : LDNode, sdDatePublished : System.DateTime, ?context : LDContext) =
+ lp.SetProperty(Dataset.sdDatePublished, sdDatePublished, ?context = context)
+
+ static member tryGetLicenseAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.license, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ //static member tryGetLicenseAsCreativeWork(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ // match lp.TryGetPropertyAsSingleNode(Dataset.license, ?graph = graph, ?context = context) with
+ // | Some n when CreativeWork.validate(n, ?context = context) -> Some n
+ // | _ -> None
+
+ static member setLicenseAsString(lp : LDNode, license : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.license, license, ?context = context)
+
+ static member setLicenseAsCreativeWork(lp : LDNode, license : obj, ?context : LDContext) =
+ lp.SetProperty(Dataset.license, license, ?context = context)
+
+ static member tryGetDateModifiedAsDateTime(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.dateModified, ?context = context) with
+ | Some (:? System.DateTime as n) -> Some n
+ | _ -> None
+
+ static member setDateModifiedAsDateTime(lp : LDNode, dateModified : System.DateTime, ?context : LDContext) =
+ lp.SetProperty(Dataset.dateModified, dateModified, ?context = context)
+
+ static member tryGetDescriptionAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.description, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getDescriptionAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.description, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"property `description` of object with @id `{lp.Id}` was not a string"
+ | _ -> failwith $"Could not access property `description` of object with @id `{lp.Id}`"
+
+ static member setDescriptionAsString(lp : LDNode, description : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.description, description, ?context = context)
+
+ static member getHasParts(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(Dataset.hasPart, ?graph = graph, ?context = context)
+
+ static member getHasPartsAsDataset(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Dataset.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(Dataset.hasPart, filter = filter, ?graph = graph, ?context = context)
+
+ static member getHasPartsAsFile(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = File.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(Dataset.hasPart, filter = filter, ?graph = graph, ?context = context)
+
+ static member setHasParts(lp : LDNode, hasParts : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(Dataset.hasPart, hasParts, ?context = context)
+
+ static member tryGetHeadlineAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.headline, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member tryGetNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"property `name` of object with @id `{lp.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{lp.Id}`"
+
+ static member setNameAsString(lp : LDNode, name : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.name, name, ?context = context)
+
+ static member getCitations(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = ScholarlyArticle.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(Dataset.citation, filter = filter, ?graph = graph, ?context = context)
+
+ static member setCitations(lp : LDNode, citations : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(Dataset.citation, citations, ?context = context)
+
+ static member getComments(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Comment.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(Dataset.comment, filter = filter, ?graph = graph, ?context = context)
+
+ static member setComments(lp : LDNode, comments : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(Dataset.comment, comments, ?context = context)
+
+ //static member getMentions(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ // let filter ldObject context = DefinedTermSet.validate(ldObject, ?context = context)
+ // lp.GetPropertyNodes(Dataset.mentions, filter = filter, ?graph = graph, ?context = context)
+
+ //static member setMentions(lp : LDNode, mentions : ResizeArray, ?context : LDContext) =
+ // lp.SetProperty(Dataset.mentions, mentions, ?context = context)
+
+ static member tryGetUrlAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.url, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getUrlAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.url, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"property `url` of object with @id `{lp.Id}` was not a string"
+ | _ -> failwith $"Could not access property `url` of object with @id `{lp.Id}`"
+
+ static member setUrlAsString(lp : LDNode, url : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.url, url, ?context = context)
+
+ static member getAbouts(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(Dataset.about, ?graph = graph, ?context = context)
+
+ static member getAboutsAsLabProcess(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = LabProcess.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(Dataset.about, filter = filter, ?graph = graph, ?context = context)
+
+ static member setAbouts(lp : LDNode, abouts : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(Dataset.about, abouts, ?context = context)
+
+ static member tryGetMeasurementMethodAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.measurementMethod, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member tryGetMeasurementMethodAsDefinedTerm(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleNode(Dataset.measurementMethod, ?graph = graph, ?context = context) with
+ | Some n when DefinedTerm.validate(n, ?context = context) -> Some n
+ | _ -> None
+
+ static member setMeasurementMethodAsString(lp : LDNode, measurementMethod : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.measurementMethod, measurementMethod, ?context = context)
+
+ static member setMeasurementMethodAsDefinedTerm(lp : LDNode, measurementMethod : LDNode, ?context : LDContext) =
+ lp.SetProperty(Dataset.measurementMethod, measurementMethod, ?context = context)
+
+ static member tryGetMeasurementTechniqueAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.measurementTechnique, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member tryGetMeasurementTechniqueAsDefinedTerm(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleNode(Dataset.measurementTechnique, ?graph = graph, ?context = context) with
+ | Some n when DefinedTerm.validate(n, ?context = context) -> Some n
+ | _ -> None
+
+
+ static member setMeasurementTechniqueAsString(lp : LDNode, measurementTechnique : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.measurementTechnique, measurementTechnique, ?context = context)
+
+ static member setMeasurementTechniqueAsDefinedTerm(lp : LDNode, measurementTechnique : LDNode, ?context : LDContext) =
+ lp.SetProperty(Dataset.measurementTechnique, measurementTechnique, ?context = context)
+
+ static member tryGetVariableMeasuredAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(Dataset.variableMeasured, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member tryGetVariableMeasuredAsPropertyValue(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleNode(Dataset.variableMeasured, ?graph = graph, ?context = context) with
+ | Some n when PropertyValue.validate(n, ?context = context) -> Some n
+ | _ -> None
+
+ static member setVariableMeasuredAsString(lp : LDNode, variableMeasured : string, ?context : LDContext) =
+ lp.SetProperty(Dataset.variableMeasured, variableMeasured, ?context = context)
+
+ static member setVariableMeasuredAsPropertyValue(lp : LDNode, variableMeasured : LDNode, ?context : LDContext) =
+ lp.SetProperty(Dataset.variableMeasured, variableMeasured, ?context = context)
+
+ static member genIDInvesigation() =
+ "./"
+
+ static member genIDStudy(identifier : string) =
+ $"studies/{identifier}/"
+
+ static member genIDAssay(identifier : string) =
+ $"assay/{identifier}/"
+
+ static member validate(lp : LDNode, ?context : LDContext) =
+ lp.HasType(Dataset.schemaType, ?context = context)
+
+ static member validateInvestigation(lp : LDNode, ?context : LDContext) =
+ Dataset.validate(lp, ?context = context)
+ && lp.AdditionalType.Contains("Investigation")
+
+ static member validateStudy (lp : LDNode, ?context : LDContext) =
+ Dataset.validate(lp, ?context = context)
+ && lp.AdditionalType.Contains("Study")
+
+ static member validateAssay (lp : LDNode, ?context : LDContext) =
+ Dataset.validate(lp, ?context = context)
+ && lp.AdditionalType.Contains("Assay")
+
+ static member create(id : string, ?identier : string, ?creators : ResizeArray, ?dateCreated : System.DateTime, ?datePublished : System.DateTime, ?dateModified : System.DateTime, ?description : string, ?hasParts : ResizeArray, ?name : string, ?citations : ResizeArray, ?comments : ResizeArray, ?mentions : ResizeArray, ?url : string, ?abouts : ResizeArray, ?measurementMethod : LDNode, ?measurementTechnique : LDNode, ?variableMeasured : LDNode, ?context : LDContext) =
+ let s = LDNode(id, ResizeArray [Dataset.schemaType], ?context = context)
+ s.SetOptionalProperty(Dataset.identifier, identier, ?context = context)
+ s.SetOptionalProperty(Dataset.creator, creators, ?context = context)
+ s.SetOptionalProperty(Dataset.dateCreated, dateCreated, ?context = context)
+ s.SetOptionalProperty(Dataset.datePublished, datePublished, ?context = context)
+ s.SetOptionalProperty(Dataset.dateModified, dateModified, ?context = context)
+ s.SetOptionalProperty(Dataset.description, description, ?context = context)
+ s.SetOptionalProperty(Dataset.hasPart, hasParts, ?context = context)
+ s.SetOptionalProperty(Dataset.name, name, ?context = context)
+ s.SetOptionalProperty(Dataset.citation, citations, ?context = context)
+ s.SetOptionalProperty(Dataset.comment, comments, ?context = context)
+ s.SetOptionalProperty(Dataset.mentions, mentions, ?context = context)
+ s.SetOptionalProperty(Dataset.url, url, ?context = context)
+ s.SetOptionalProperty(Dataset.about, abouts, ?context = context)
+ s.SetOptionalProperty(Dataset.measurementMethod, measurementMethod, ?context = context)
+ s.SetOptionalProperty(Dataset.measurementTechnique, measurementTechnique, ?context = context)
+ s.SetOptionalProperty(Dataset.variableMeasured, variableMeasured, ?context = context)
+ s
+
+ static member createInvestigation(identifier : string, name : string, ?id : string, ?creators : ResizeArray, ?dateCreated : System.DateTime, ?datePublished : System.DateTime, ?dateModified : System.DateTime, ?description : string, ?hasParts : ResizeArray, ?citations : ResizeArray, ?comments : ResizeArray, ?mentions : ResizeArray, ?url : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Dataset.genIDInvesigation()
+ let s = Dataset.create(id, identier = identifier, ?creators = creators, ?dateCreated = dateCreated, ?datePublished = datePublished, ?dateModified = dateModified, ?description = description, ?hasParts = hasParts, name = name, ?citations = citations, ?comments = comments, ?mentions = mentions, ?url = url, ?context = context)
+ s.AdditionalType <- ResizeArray ["Investigation"]
+ s
+
+ static member createStudy(identifier : string, ?id : string, ?creators : ResizeArray, ?dateCreated : System.DateTime, ?datePublished : System.DateTime, ?dateModified : System.DateTime, ?description : string, ?hasParts : ResizeArray, ?name : string, ?citations : ResizeArray, ?comments : ResizeArray, ?url : string, ?abouts : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Dataset.genIDStudy(identifier)
+ let s = Dataset.create(id, identier = identifier, ?creators = creators, ?dateCreated = dateCreated, ?datePublished = datePublished, ?dateModified = dateModified, ?description = description, ?hasParts = hasParts, ?name = name, ?citations = citations, ?comments = comments, ?url = url, ?abouts = abouts, ?context = context)
+ s.AdditionalType <- ResizeArray ["Study"]
+ s
+
+ static member createAssay(identifier : string, ?id : string, ?description : string, ?creators : ResizeArray, ?hasParts : ResizeArray, ?measurementMethod : LDNode, ?measurementTechnique : LDNode, ?variableMeasured : LDNode, ?abouts : ResizeArray, ?comments : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Dataset.genIDAssay(identifier)
+ let s = Dataset.create(id, identier = identifier, ?description = description, ?creators = creators, ?hasParts = hasParts, ?measurementMethod = measurementMethod, ?measurementTechnique = measurementTechnique, ?variableMeasured = variableMeasured, ?abouts = abouts, ?comments = comments, ?context = context)
+ s.AdditionalType <- ResizeArray ["Assay"]
+ s
diff --git a/src/ROCrate/Generic/DefinedTerm.fs b/src/ROCrate/Generic/DefinedTerm.fs
new file mode 100644
index 00000000..7071e124
--- /dev/null
+++ b/src/ROCrate/Generic/DefinedTerm.fs
@@ -0,0 +1,62 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+///
+[]
+type DefinedTerm =
+
+ static member schemaType = "http://schema.org/DefinedTerm"
+
+ static member termCode = "http://schema.org/termCode"
+
+ static member name = "http://schema.org/name"
+
+ static member tryGetTermCodeAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(DefinedTerm.termCode, ?context = context) with
+ | Some (:? string as tc) -> Some tc
+ | _ -> None
+
+ static member getTermCodeAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(DefinedTerm.termCode, ?context = context) with
+ | Some (:? string as tc) -> tc
+ | Some _ -> failwith $"Property of `termCode` of object with @id `{dt.Id}` was not a string"
+ | _ -> failwith $"Could not access property `termCode` of object with @id `{dt.Id}`"
+
+ static member setTermCodeAsString(dt : LDNode, termCode : string, ?context : LDContext) =
+ dt.SetProperty(DefinedTerm.termCode, termCode, ?context = context)
+
+ static member tryGetNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(DefinedTerm.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(DefinedTerm.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `name` of object with @id `{dt.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{dt.Id}`"
+
+ static member setNameAsString(dt : LDNode, name : string, ?context : LDContext) =
+ dt.SetProperty(DefinedTerm.name, name, ?context = context)
+
+ static member genID(name : string, ?termCode : string) =
+ match termCode with
+ | Some tc -> $"{tc}"
+ | None -> $"#OA_{name}" |> Helper.ID.clean
+
+
+ static member validate(dt : LDNode, ?context : LDContext) =
+ dt.HasType(DefinedTerm.schemaType, ?context = context)
+ && dt.HasProperty(DefinedTerm.name, ?context = context)
+
+ static member create(name : string, ?id : string, ?termCode : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> DefinedTerm.genID(name, ?termCode = termCode)
+ let dt = LDNode(id, ResizeArray [DefinedTerm.schemaType], ?context = context)
+ dt.SetProperty(DefinedTerm.name, name, ?context = context)
+ dt.SetOptionalProperty(DefinedTerm.termCode, termCode, ?context = context)
+ dt
\ No newline at end of file
diff --git a/src/ROCrate/Generic/File.fs b/src/ROCrate/Generic/File.fs
new file mode 100644
index 00000000..6b5ec7d8
--- /dev/null
+++ b/src/ROCrate/Generic/File.fs
@@ -0,0 +1,123 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+///
+[]
+type File =
+
+ static member schemaType = "http://schema.org/MediaObject"
+
+ static member name = "http://schema.org/name"
+
+ static member comment = "http://schema.org/comment"
+
+ static member disambiguatingDescription = "http://schema.org/disambiguatingDescription"
+
+ static member usageInfo = "http://schema.org/usageInfo"
+
+ static member encodingFormat = "http://schema.org/encodingFormat"
+
+ static member tryGetNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(File.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(File.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `name` of object with @id `{dt.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{dt.Id}`"
+
+ static member setNameAsString(dt : LDNode, name : string, ?context : LDContext) =
+ dt.SetProperty(File.name, name, ?context = context)
+
+ static member getComments(dt : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Comment.validate(ldObject, ?context = context)
+ dt.GetPropertyNodes(File.comment, filter = filter, ?graph = graph, ?context = context)
+
+ static member setComments(dt : LDNode, comment : ResizeArray, ?context : LDContext) =
+ dt.SetProperty(File.comment, comment, ?context = context)
+
+ static member tryGetDisambiguatingDescriptionAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(File.disambiguatingDescription, ?context = context) with
+ | Some (:? string as dd) -> Some dd
+ | _ -> None
+
+ static member setDisambiguatingDescriptionAsString(dt : LDNode, disambiguatingDescription : string, ?context : LDContext) =
+ dt.SetProperty(File.disambiguatingDescription, disambiguatingDescription, ?context = context)
+
+ static member tryGetEncodingFormatAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(File.encodingFormat, ?context = context) with
+ | Some (:? string as ef) -> Some ef
+ | _ -> None
+
+ static member setEncodingFormatAsString(dt : LDNode, encodingFormat : string, ?context : LDContext) =
+ dt.SetProperty(File.encodingFormat, encodingFormat, ?context = context)
+
+ static member tryGetUsageInfoAsString(dt : LDNode, ?context : LDContext) =
+ match dt.TryGetPropertyAsSingleton(File.usageInfo, ?context = context) with
+ | Some (:? string as ui) -> Some ui
+ | _ -> None
+
+ static member setUsageInfoAsString(dt : LDNode, usageInfo : string, ?context : LDContext) =
+ dt.SetProperty(File.usageInfo, usageInfo, ?context = context)
+
+ static member genId(name : string) =
+ $"{name}"
+
+ static member validate(dt : LDNode, ?context : LDContext) =
+ dt.HasType(File.schemaType, ?context = context)
+ && dt.HasProperty(File.name, ?context = context)
+
+ static member create(name : string, ?id : string, ?comments : ResizeArray, ?disambiguatingDescription : string, ?encodingFormat : string, ?usageInfo : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> File.genId(name)
+ let dt = LDNode(id, ResizeArray [File.schemaType], ?context = context)
+ dt.SetProperty(File.name, name, ?context = context)
+ dt.SetOptionalProperty(File.comment, comments, ?context = context)
+ dt.SetOptionalProperty(File.disambiguatingDescription, disambiguatingDescription, ?context = context)
+ dt.SetOptionalProperty(File.encodingFormat, encodingFormat, ?context = context)
+ dt.SetOptionalProperty(File.usageInfo, usageInfo, ?context = context)
+ dt
+
+ //static member tryGetTermCodeAsString(dt : LDNode, ?context : LDContext) =
+ // match dt.TryGetProperty(DefinedTerm.termCode, ?context = context) with
+ // | Some (:? string as tc) -> Some tc
+ // | _ -> None
+
+ //static member getTermCodeAsString(dt : LDNode, ?context : LDContext) =
+ // match dt.TryGetProperty(DefinedTerm.termCode, ?context = context) with
+ // | Some (:? string as tc) -> tc
+ // | Some _ -> failwith $"Property of `termCode` of object with @id `{dt.Id}` was not a string"
+ // | _ -> failwith $"Could not access property `termCode` of object with @id `{dt.Id}`"
+
+ //static member setTermCodeAsString(dt : LDNode, termCode : string, ?context : LDContext) =
+ // dt.SetProperty(DefinedTerm.termCode, termCode, ?context = context)
+
+ //static member tryGetNameAsString(dt : LDNode, ?context : LDContext) =
+ // match dt.TryGetProperty(DefinedTerm.name, ?context = context) with
+ // | Some (:? string as n) -> Some n
+ // | _ -> None
+
+ //static member getNameAsString(dt : LDNode, ?context : LDContext) =
+ // match dt.TryGetProperty(DefinedTerm.name, ?context = context) with
+ // | Some (:? string as n) -> n
+ // | Some _ -> failwith $"Property of `name` of object with @id `{dt.Id}` was not a string"
+ // | _ -> failwith $"Could not access property `name` of object with @id `{dt.Id}`"
+
+ //static member setNameAsString(dt : LDNode, name : string, ?context : LDContext) =
+ // dt.SetProperty(DefinedTerm.name, name, ?context = context)
+
+ //static member validate(dt : LDNode, ?context : LDContext) =
+ // dt.HasType(DefinedTerm.schemaType, ?context = context)
+ // && dt.HasProperty(DefinedTerm.name, ?context = context)
+
+ //static member create(id : string, name : string, ?termCode : string, ?context : LDContext) =
+ // let dt = LDNode(id, ResizeArray [DefinedTerm.schemaType], ?context = context)
+ // dt.SetProperty(DefinedTerm.name, name, ?context = context)
+ // dt.SetOptionalProperty(DefinedTerm.termCode, termCode, ?context = context)
+ // dt
\ No newline at end of file
diff --git a/src/ROCrate/Generic/LabProcess.fs b/src/ROCrate/Generic/LabProcess.fs
new file mode 100644
index 00000000..1c0e5662
--- /dev/null
+++ b/src/ROCrate/Generic/LabProcess.fs
@@ -0,0 +1,151 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+open ARCtrl.Helper
+
+[]
+type LabProcess =
+
+ static member schemaType = "https://bioschemas.org/LabProcess"
+
+ static member name = "http://schema.org/name"
+
+ static member agent = "http://schema.org/agent"
+
+ static member object_ = "http://schema.org/object"
+
+ static member result = "http://schema.org/result"
+
+ static member executesLabProtocol = "https://bioschemas.org/executesLabProtocol"
+
+ static member parameterValue = "https://bioschemas.org/parameterValue"
+
+ static member endTime = "http://schema.org/endTime"
+
+ static member disambiguatingDescription = "http://schema.org/disambiguatingDescription"
+
+ static member tryGetNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProcess.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProcess.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"property `name` of object with @id `{lp.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{lp.Id}`"
+
+ static member setNameAsString(lp : LDNode, name : string, ?context : LDContext) =
+ lp.SetProperty(LabProcess.name, name, ?context = context)
+
+ static member tryGetAgent(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Person.validate(ldObject, ?context = context)
+ match lp.TryGetPropertyAsSingleNode(LabProcess.agent, ?graph = graph, ?context = context) with
+ | Some a when filter a context -> Some a
+ | _ -> None
+
+ static member getAgent(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Person.validate(ldObject, ?context = context)
+ match lp.TryGetPropertyAsSingleNode(LabProcess.agent, ?graph = graph, ?context = context) with
+ | Some a when filter a context -> a
+ | Some _ -> failwith $"Property of `agent` of object with @id `{lp.Id}` was not a valid Person"
+ | _ -> failwith $"Could not access property `agent` of object with @id `{lp.Id}`"
+
+ static member setAgent(lp : LDNode, agent : LDNode, ?context : LDContext) =
+ lp.SetProperty(LabProcess.agent, agent, ?context = context)
+
+ static member getObjects(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(LabProcess.object_, ?graph = graph, ?context = context)
+
+ static member getObjectsAsSample(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Sample.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProcess.object_, filter = filter, ?graph = graph, ?context = context)
+
+ static member getObjectsAsData(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = File.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProcess.object_, filter = filter, ?graph = graph, ?context = context)
+
+ static member setObjects(lp : LDNode, objects : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProcess.object_, objects, ?context = context)
+
+ static member getResults(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(LabProcess.result, ?graph = graph, ?context = context)
+
+ static member getResultsAsSample(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Sample.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProcess.result, filter = filter, ?graph = graph, ?context = context)
+
+ static member getResultsAsData(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = File.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProcess.result, filter = filter, ?graph = graph, ?context = context)
+
+ static member setResults(lp : LDNode, results : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProcess.result, results, ?context = context)
+
+ static member tryGetExecutesLabProtocol(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = LabProtocol.validate(ldObject, ?context = context)
+ match lp.TryGetPropertyAsSingleNode(LabProcess.executesLabProtocol, ?graph = graph, ?context = context) with
+ | Some l when filter l context -> Some l
+ | _ -> None
+
+ static member setExecutesLabProtocol(lp : LDNode, executesLabProtocol : LDNode, ?context : LDContext) =
+ lp.SetProperty(LabProcess.executesLabProtocol, executesLabProtocol, ?context = context)
+
+ static member getParameterValues(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = PropertyValue.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProcess.parameterValue, filter = filter, ?graph = graph, ?context = context)
+
+ static member setParameterValues(lp : LDNode, parameterValues : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProcess.parameterValue, parameterValues, ?context = context)
+
+ static member tryGetEndTime(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProcess.endTime, ?context = context) with
+ | Some (:? System.DateTime as et) -> Some et
+ | _ -> None
+
+ static member setEndTime(lp : LDNode, endTime : System.DateTime, ?context : LDContext) =
+ lp.SetProperty(LabProcess.endTime, endTime, ?context = context)
+
+
+ static member getDisambiguatingDescriptionsAsString(lp : LDNode, ?context : LDContext) =
+ let filter = fun (o : obj) context -> o :? string
+ lp.GetPropertyValues(LabProcess.disambiguatingDescription, filter = filter, ?context = context)
+ |> ResizeArray.map (fun (o : obj) -> o :?> string)
+
+ static member setDisambiguatingDescriptionsAsString(lp : LDNode, disambiguatingDescriptions : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProcess.disambiguatingDescription, disambiguatingDescriptions, ?context = context)
+
+ static member validate(lp : LDNode, ?context : LDContext) =
+ lp.HasType(LabProcess.schemaType, ?context = context)
+ && lp.HasProperty(LabProcess.name, ?context = context)
+ //&& lp.HasProperty(LabProcess.agent, ?context = context)
+ //&& lp.HasProperty(LabProcess.object_, ?context = context)
+ //&& lp.HasProperty(LabProcess.result, ?context = context)
+
+ static member genId(name, ?assayName, ?studyName) =
+ match assayName, studyName with
+ | Some assay, Some study -> $"#Process_{study}_{assay}_{name}"
+ | Some assay, None -> $"#Process_{assay}_{name}"
+ | None, Some study -> $"#Process_{study}_{name}"
+ | _ -> $"#Process_{name}"
+ |> Helper.ID.clean
+
+
+ static member create(name : string, ?objects : ResizeArray, ?results : ResizeArray, ?id : string, ?agent : LDNode, ?executesLabProtocol : LDNode, ?parameterValues : ResizeArray, ?endTime : System.DateTime, ?disambiguatingDescriptions : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> LabProcess.genId(name)
+ let objects = Option.defaultValue (ResizeArray []) objects
+ let results = Option.defaultValue (ResizeArray []) results
+ let lp = LDNode(id, ResizeArray [LabProcess.schemaType], ?context = context)
+ lp.SetProperty(LabProcess.name, name, ?context = context)
+ lp.SetOptionalProperty(LabProcess.agent, agent, ?context = context) // Optional?
+ lp.SetProperty(LabProcess.object_, objects, ?context = context)
+ lp.SetProperty(LabProcess.result, results, ?context = context)
+ lp.SetOptionalProperty(LabProcess.executesLabProtocol, executesLabProtocol, ?context = context)
+ lp.SetOptionalProperty(LabProcess.parameterValue, parameterValues, ?context = context)
+ lp.SetOptionalProperty(LabProcess.endTime, endTime, ?context = context)
+ lp.SetOptionalProperty(LabProcess.disambiguatingDescription, disambiguatingDescriptions, ?context = context)
+ lp
\ No newline at end of file
diff --git a/src/ROCrate/Generic/LabProtocol.fs b/src/ROCrate/Generic/LabProtocol.fs
new file mode 100644
index 00000000..6a7a5576
--- /dev/null
+++ b/src/ROCrate/Generic/LabProtocol.fs
@@ -0,0 +1,145 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+[]
+type LabProtocol =
+
+ static member schemaType = "https://bioschemas.org/LabProtocol"
+
+ static member description = "http://schema.org/description"
+
+ static member intendedUse = "https://bioschemas.org/intendedUse"
+
+ static member name = "http://schema.org/name"
+
+ static member comment = "http://schema.org/comment"
+
+ static member computationalTool = "https://bioschemas.org/computationalTool"
+
+ static member labEquipment = "https://bioschemas.org/labEquipment"
+
+ static member reagent = "https://bioschemas.org/reagent"
+
+ static member url = "http://schema.org/url"
+
+ static member version = "http://schema.org/version"
+
+
+ static member tryGetDescriptionAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.description, ?context = context) with
+ | Some (:? string as d) -> Some d
+ | _ -> None
+
+ static member setDescriptionAsString(lp : LDNode, description : string, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.description, description, ?context = context)
+
+ static member tryGetIntendedUseAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.intendedUse, ?context = context) with
+ | Some (:? string as iu) -> Some iu
+ | _ -> None
+
+ static member setIntendedUseAsString(lp : LDNode, intendedUse : string, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.intendedUse, intendedUse, ?context = context)
+
+ static member tryGetIntendedUseAsDefinedTerm(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = DefinedTerm.validate(ldObject, ?context = context)
+ match lp.TryGetPropertyAsSingleNode(LabProtocol.intendedUse, ?graph = graph, ?context = context) with
+ | Some iu when filter iu context -> Some iu
+ | _ -> None
+
+ static member setIntendedUseAsDefinedTerm(lp : LDNode, intendedUse : LDNode, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.intendedUse, intendedUse, ?context = context)
+
+ static member tryGetNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | _ -> failwith $"Could not access property `name` of object with @id `{lp.Id}`"
+
+ static member setNameAsString(lp : LDNode, name : string, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.name, name, ?context = context)
+
+ static member getComments(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Comment.validate(ldObject, ?context = context)
+ lp.GetPropertyNodes(LabProtocol.comment, filter = filter, ?graph = graph, ?context = context)
+
+ static member setComments(lp : LDNode, comments : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.comment, comments, ?context = context)
+
+ static member getComputationalTools(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(LabProtocol.computationalTool, ?graph = graph, ?context = context)
+
+ static member setComputationalTools(lp : LDNode, computationalTools : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.computationalTool, computationalTools, ?context = context)
+
+ static member getLabEquipments(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(LabProtocol.labEquipment, ?graph = graph, ?context = context)
+
+ static member setLabEquipments(lp : LDNode, labEquipments : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.labEquipment, labEquipments, ?context = context)
+
+ static member getReagents(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ lp.GetPropertyNodes(LabProtocol.reagent, ?graph = graph, ?context = context)
+
+ static member setReagents(lp : LDNode, reagents : ResizeArray, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.reagent, reagents, ?context = context)
+
+ static member getComponents(lp : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ LabProtocol.getLabEquipments(lp, ?graph = graph, ?context = context)
+ |> Seq.append (LabProtocol.getReagents(lp, ?graph = graph, ?context = context))
+ |> Seq.append (LabProtocol.getComputationalTools(lp, ?graph = graph, ?context = context))
+ |> ResizeArray
+
+ static member tryGetUrl(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.url, ?context = context) with
+ | Some (:? string as u) -> Some u
+ | _ -> None
+
+ static member setUrl(lp : LDNode, url : string, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.url, url, ?context = context)
+
+ static member tryGetVersionAsString(lp : LDNode, ?context : LDContext) =
+ match lp.TryGetPropertyAsSingleton(LabProtocol.version, ?context = context) with
+ | Some (:? string as v) -> Some v
+ | _ -> None
+
+ static member setVersionAsString(lp : LDNode, version : string, ?context : LDContext) =
+ lp.SetProperty(LabProtocol.version, version, ?context = context)
+
+ static member validate(lp : LDNode, ?context : LDContext) =
+ lp.HasType(LabProtocol.schemaType, ?context = context)
+ //&& lp.HasProperty(LabProtocol.name, ?context = context)
+
+ static member genId(?name : string, ?processName : string, ?assayName : string, ?studyName : string) =
+ [
+ if name.IsSome then name.Value
+ if processName.IsSome then processName.Value
+ if assayName.IsSome then assayName.Value
+ if studyName.IsSome then studyName.Value
+ ]
+ |> fun vals ->
+ if vals.IsEmpty then [ARCtrl.Helper.Identifier.createMissingIdentifier()]
+ else vals
+ |> List.append ["#Protocol"]
+ |> String.concat "_"
+ |> Helper.ID.clean
+
+ static member create(id : string, ?name : string, ?description : string, ?intendedUse : LDNode, ?comments : ResizeArray, ?computationalTools : ResizeArray, ?labEquipments : ResizeArray, ?reagents : ResizeArray, ?url : string, ?version : string, ?context : LDContext) =
+ let lp = LDNode(id, ResizeArray [LabProtocol.schemaType], ?context = context)
+ lp.SetOptionalProperty(LabProtocol.name, name, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.description, description, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.intendedUse, intendedUse, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.comment, comments, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.computationalTool, computationalTools, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.labEquipment, labEquipments, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.reagent, reagents, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.url, url, ?context = context)
+ lp.SetOptionalProperty(LabProtocol.version, version, ?context = context)
+ lp
\ No newline at end of file
diff --git a/src/ROCrate/Generic/Organization.fs b/src/ROCrate/Generic/Organization.fs
new file mode 100644
index 00000000..6ef0113d
--- /dev/null
+++ b/src/ROCrate/Generic/Organization.fs
@@ -0,0 +1,42 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+///
+[]
+type Organization =
+
+ static member schemaType = "http://schema.org/Organization"
+
+ static member name = "http://schema.org/name"
+
+ static member tryGetNameAsString(o : LDNode, ?context : LDContext) =
+ match o.TryGetPropertyAsSingleton(Organization.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(o : LDNode, ?context : LDContext) =
+ match o.TryGetPropertyAsSingleton(Organization.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `name` of object with @id `{o.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{o.Id}`"
+
+ static member setNameAsString(o : LDNode, n : string, ?context : LDContext) =
+ o.SetProperty(Organization.name, n, ?context = context)
+
+ static member genID(name : string) =
+ $"#Organization_{name}"
+ |> Helper.ID.clean
+
+ static member validate(o : LDNode, ?context : LDContext) =
+ o.HasType(Organization.schemaType, ?context = context)
+ && o.HasProperty(Organization.name, ?context = context)
+
+ static member create(name : string, ?id : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Organization.genID name
+ let o = LDNode(id, ResizeArray [Organization.schemaType], ?context = context)
+ o.SetProperty(Organization.name, name, ?context = context)
+ o
\ No newline at end of file
diff --git a/src/ROCrate/Generic/Person.fs b/src/ROCrate/Generic/Person.fs
new file mode 100644
index 00000000..9c69d19b
--- /dev/null
+++ b/src/ROCrate/Generic/Person.fs
@@ -0,0 +1,168 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+open ARCtrl.Helper
+
+[]
+type Person =
+
+ static member schemaType = "http://schema.org/Person"
+ static member givenName = "http://schema.org/givenName"
+ static member affiliation = "http://schema.org/affiliation"
+ static member email = "http://schema.org/email"
+ static member familyName = "http://schema.org/familyName"
+ static member identifier = "http://schema.org/identifier"
+ static member jobTitle = "http://schema.org/jobTitle"
+ static member additionalName = "http://schema.org/additionalName"
+ static member address = "http://schema.org/address"
+ static member disambiguatingDescription = "http://schema.org/disambiguatingDescription"
+ static member faxNumber = "http://schema.org/faxNumber"
+ static member telephone = "http://schema.org/telephone"
+
+ static member tryGetGivenNameAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.givenName, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getGivenNameAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.givenName, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `givenName` of object with @id `{p.Id}` was not a string"
+ | _ -> failwith $"Could not access property `givenName` of object with @id `{p.Id}`"
+
+ static member setGivenNameAsString(p : LDNode, n : string, ?context : LDContext) =
+ p.SetProperty(Person.givenName, n, ?context = context)
+
+ static member tryGetAffiliation(p : LDNode, ?graph :LDGraph, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleNode(Person.affiliation, ?graph = graph, ?context = context) with
+ | Some n when Organization.validate n -> Some n
+ | _ -> None
+
+ static member setAffiliation(p : LDNode, a : LDNode, ?context : LDContext) =
+ p.SetProperty(Person.affiliation, a, ?context = context)
+
+ static member tryGetEmailAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.email, ?context = context) with
+ | Some (:? string as e) -> Some e
+ | _ -> None
+
+ static member setEmailAsString(p : LDNode, e : string, ?context : LDContext) =
+ p.SetProperty(Person.email, e, ?context = context)
+
+ static member tryGetFamilyNameAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.familyName, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getFamilyNameAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.familyName, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `familyName` of object with @id `{p.Id}` was not a string"
+ | _ -> failwith $"Could not access property `familyName` of object with @id `{p.Id}`"
+
+ static member setFamilyNameAsString(p : LDNode, n : string, ?context : LDContext) =
+ p.SetProperty(Person.familyName, n, ?context = context)
+
+ static member tryGetIdentifier(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.identifier, ?context = context) with
+ | Some (:? LDNode as i) -> Some i
+ | _ -> None
+
+ static member setIdentifier(p : LDNode, i : LDNode, ?context : LDContext) =
+ p.SetProperty(Person.identifier, i, ?context = context)
+
+ static member getJobTitlesAsDefinedTerm(p : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = DefinedTerm.validate(ldObject, ?context = context)
+ p.GetPropertyNodes(Person.jobTitle, filter = filter, ?graph = graph, ?context = context)
+
+
+ static member setJobTitleAsDefinedTerm(p : LDNode, j : ResizeArray, ?context : LDContext) =
+ p.SetProperty(Person.jobTitle, j, ?context = context)
+
+ static member tryGetAdditionalNameAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.additionalName, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member setAdditionalNameAsString(p : LDNode, n : string, ?context : LDContext) =
+ p.SetProperty(Person.additionalName, n, ?context = context)
+
+ static member tryGetAddress(p : LDNode, ?graph : LDGraph, ?context : LDContext) : obj option =
+ match p.TryGetPropertyAsSingleton(Person.address, ?context = context) with
+ | Some (:? LDRef as r) when graph.IsSome -> graph.Value.TryGetNode(r.Id) |> Option.map box
+ | Some (:? LDNode as a) -> Some a
+ | Some (:? string as s) -> Some s
+ | _ -> None
+
+ static member tryGetAddressAsPostalAddress(p : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleNode(Person.address, ?graph = graph, ?context = context) with
+ | Some n when PostalAddress.validate n -> Some n
+ | _ -> None
+
+ static member tryGetAddressAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.address, ?context = context) with
+ | Some (:? string as a) -> Some a
+ | _ -> None
+
+ static member setAddressAsPostalAddress(p : LDNode, a : LDNode, ?context : LDContext) =
+ p.SetProperty(Person.address, a, ?context = context)
+
+ static member setAddressAsString(p : LDNode, a : string, ?context : LDContext) =
+ p.SetProperty(Person.address, a, ?context = context)
+
+ static member getDisambiguatingDescriptionsAsString(p : LDNode, ?context : LDContext) =
+ let filter (value : obj) context = value :? string
+ p.GetPropertyValues(Person.disambiguatingDescription, filter = filter, ?context = context)
+ |> ResizeArray.map (fun v -> v :?> string)
+
+ static member setDisambiguatingDescriptionsAsString(p : LDNode, d : ResizeArray, ?context : LDContext) =
+ p.SetProperty(Person.disambiguatingDescription, d, ?context = context)
+
+ static member tryGetFaxNumberAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.faxNumber, ?context = context) with
+ | Some (:? string as f) -> Some f
+ | _ -> None
+
+ static member setFaxNumberAsString(p : LDNode, f : string, ?context : LDContext) =
+ p.SetProperty(Person.faxNumber, f, ?context = context)
+
+ static member tryGetTelephoneAsString(p : LDNode, ?context : LDContext) =
+ match p.TryGetPropertyAsSingleton(Person.telephone, ?context = context) with
+ | Some (:? string as t) -> Some t
+ | _ -> None
+
+ static member setTelephoneAsString(p : LDNode, t : string, ?context : LDContext) =
+ p.SetProperty(Person.telephone, t, ?context = context)
+
+ static member genId(givenName, ?orcid, ?familyName) =
+ match orcid |> Option.bind ORCID.tryGetOrcidURL with
+ | Some orcid -> orcid
+ | None ->
+ match familyName with
+ | Some familyName -> $"#Person_{givenName}_{familyName}"
+ | None -> $"#Person_{givenName}"
+ |> Helper.ID.clean
+
+ static member validate(p : LDNode, ?context : LDContext) =
+ p.HasType(Person.schemaType, ?context = context)
+ && p.HasProperty(Person.givenName, ?context = context)
+
+ static member create(givenName : string, ?orcid : string, ?id : string, ?affiliation : #obj, ?email : string, ?familyName : string, ?identifier, ?jobTitles : ResizeArray, ?additionalName : string, ?address : #obj, ?disambiguatingDescriptions : ResizeArray, ?faxNumber : string, ?telephone : string, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> Person.genId(givenName, ?orcid = orcid, ?familyName = familyName)
+ let person = LDNode(id, ResizeArray [Person.schemaType], ?context = context)
+ person.SetProperty(Person.givenName, givenName, ?context = context)
+ person.SetOptionalProperty(Person.affiliation, affiliation, ?context = context)
+ person.SetOptionalProperty(Person.email, email, ?context = context)
+ person.SetOptionalProperty(Person.familyName, familyName, ?context = context)
+ person.SetOptionalProperty(Person.identifier, identifier, ?context = context)
+ person.SetOptionalProperty(Person.jobTitle, jobTitles, ?context = context)
+ person.SetOptionalProperty(Person.additionalName, additionalName, ?context = context)
+ person.SetOptionalProperty(Person.address, address, ?context = context)
+ person.SetOptionalProperty(Person.disambiguatingDescription, disambiguatingDescriptions, ?context = context)
+ person.SetOptionalProperty(Person.faxNumber, faxNumber, ?context = context)
+ person.SetOptionalProperty(Person.telephone, telephone, ?context = context)
+ person
\ No newline at end of file
diff --git a/src/ROCrate/Generic/PostalAddress.fs b/src/ROCrate/Generic/PostalAddress.fs
new file mode 100644
index 00000000..2a5833b2
--- /dev/null
+++ b/src/ROCrate/Generic/PostalAddress.fs
@@ -0,0 +1,87 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.Helper
+
+///
+[]
+type PostalAddress =
+
+ static member schemaType = "http://schema.org/PostalAddress"
+
+ static member addressCountry = "http://schema.org/addressCountry"
+
+ static member postalCode = "http://schema.org/postalCode"
+
+ static member streetAddress = "http://schema.org/streetAddress"
+
+ static member tryGetAddressCountryAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.addressCountry, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getAddressCountryAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.addressCountry, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Value of property `addressCountry` of object with @id `{s.Id}` should have been a string"
+ | None -> failwith $"Could not access property `addressCountry` of object with @id `{s.Id}`"
+
+ static member setAddressCountryAsString(s : LDNode, n : string) =
+ s.SetProperty(PostalAddress.addressCountry, n)
+
+ static member tryGetPostalCodeAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.postalCode, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getPostalCodeAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.postalCode, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Value of property `postalCode` of object with @id `{s.Id}` should have been a string"
+ | None -> failwith $"Could not access property `postalCode` of object with @id `{s.Id}`"
+
+ static member setPostalCodeAsString(s : LDNode, n : string) =
+ s.SetProperty(PostalAddress.postalCode, n)
+
+ static member tryGetStreetAddressAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.streetAddress, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getStreetAddressAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(PostalAddress.streetAddress, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Value of property `streetAddress` of object with @id `{s.Id}` should have been a string"
+ | None -> failwith $"Could not access property `streetAddress` of object with @id `{s.Id}`"
+
+ static member setStreetAddressAsString(s : LDNode, n : string) =
+ s.SetProperty(PostalAddress.streetAddress, n)
+
+ static member genID(?addressCountry : string, ?postalCode : string, ?streetAddress : string) =
+ let items =
+ [
+ if addressCountry.IsSome then yield "addressCountry"
+ if postalCode.IsSome then yield "postalCode"
+ if streetAddress.IsSome then yield "streetAddress"
+ ]
+ if items.IsEmpty then Identifier.createMissingIdentifier()
+ else
+ items
+ |> List.reduce (fun acc x -> $"{acc}_{x}")
+ |> sprintf "#%s"
+ |> Helper.ID.clean
+
+ static member validate(o : LDNode, ?context : LDContext) =
+ o.HasType(PostalAddress.schemaType, ?context = context)
+
+ static member create(?id : string, ?addressCountry : string, ?postalCode : string, ?streetAddress : string, ?context : LDContext) =
+ let id =
+ match id with
+ | Some x -> x
+ | None -> PostalAddress.genID(?addressCountry = addressCountry, ?postalCode = postalCode, ?streetAddress = streetAddress)
+ let s = LDNode(id, ResizeArray [PostalAddress.schemaType], ?context = context)
+ s.SetOptionalProperty(PostalAddress.addressCountry, addressCountry)
+ s.SetOptionalProperty(PostalAddress.postalCode, postalCode)
+ s.SetOptionalProperty(PostalAddress.streetAddress, streetAddress)
+ s
\ No newline at end of file
diff --git a/src/ROCrate/Generic/PropertyValue.fs b/src/ROCrate/Generic/PropertyValue.fs
new file mode 100644
index 00000000..b54af2a8
--- /dev/null
+++ b/src/ROCrate/Generic/PropertyValue.fs
@@ -0,0 +1,171 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+///
+[]
+type PropertyValue =
+
+ static member schemaType = "http://schema.org/PropertyValue"
+
+ static member name = "http://schema.org/name"
+
+ static member value = "http://schema.org/value"
+
+ static member propertyID = "http://schema.org/propertyID"
+
+ static member unitCode = "http://schema.org/unitCode"
+
+ static member unitText = "http://schema.org/unitText"
+
+ static member valueReference = "http://schema.org/valueReference"
+
+ static member tryGetNameAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Property of `name` of object with @id `{pv.Id}` was not a string"
+ | _ -> failwith $"Could not access property `name` of object with @id `{pv.Id}`"
+
+ static member setNameAsString(pv : LDNode, name : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.name, name, ?context = context)
+
+ static member tryGetValueAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.value, ?context = context) with
+ | Some (:? string as v) -> Some v
+ | _ -> None
+
+ static member getValueAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.value, ?context = context) with
+ | Some (:? string as v) -> v
+ | Some _ -> failwith $"Property of `value` of object with @id `{pv.Id}` was not a string"
+ | _ -> failwith $"Could not access property `value` of object with @id `{pv.Id}`"
+
+ static member setValueAsString(pv : LDNode, value : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.value, value, ?context = context)
+
+ static member tryGetPropertyIDAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.propertyID, ?context = context) with
+ | Some (:? string as pid) -> Some pid
+ | _ -> None
+
+ static member setPropertyIDAsString(pv : LDNode, propertyID : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.propertyID, propertyID, ?context = context)
+
+ static member tryGetUnitCodeAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.unitCode, ?context = context) with
+ | Some (:? string as uc) -> Some uc
+ | _ -> None
+
+ static member setUnitCodeAsString(pv : LDNode, unitCode : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.unitCode, unitCode, ?context = context)
+
+ static member tryGetUnitTextAsString(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.unitText, ?context = context) with
+ | Some (:? string as ut) -> Some ut
+ | _ -> None
+
+ static member setUnitTextAsString(pv : LDNode, unitText : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.unitText, unitText, ?context = context)
+
+ static member tryGetValueReference(pv : LDNode, ?context : LDContext) =
+ match pv.TryGetPropertyAsSingleton(PropertyValue.valueReference, ?context = context) with
+ | Some (:? string as vr) -> Some vr
+ | _ -> None
+
+ static member setValueReference(pv : LDNode, valueReference : string, ?context : LDContext) =
+ pv.SetProperty(PropertyValue.valueReference, valueReference, ?context = context)
+
+ static member validate(pv : LDNode, ?context : LDContext) =
+ pv.HasType(PropertyValue.schemaType, ?context = context)
+ && pv.HasProperty(PropertyValue.name, ?context = context)
+ //&& pv.HasProperty(PropertyValue.value, ?context = context)
+
+ static member validateComponent (pv : LDNode, ?context : LDContext) =
+ PropertyValue.validate(pv, ?context = context)
+ && pv.AdditionalType.Contains("Component")
+
+ static member validateParameterValue (pv : LDNode, ?context : LDContext) =
+ PropertyValue.validate(pv, ?context = context)
+ && pv.AdditionalType.Contains("ParameterValue")
+
+ static member validateCharacteristicValue (pv : LDNode, ?context : LDContext) =
+ PropertyValue.validate(pv, ?context = context)
+ && pv.AdditionalType.Contains("CharacteristicValue")
+
+ static member validateFactorValue (pv : LDNode, ?context : LDContext) =
+ PropertyValue.validate(pv, ?context = context)
+ && pv.AdditionalType.Contains("FactorValue")
+
+ static member genId(name : string, ?value : string, ?propertyID : string, ?prefix) =
+ let prefix = Option.defaultValue "PV" prefix
+ match value,propertyID with
+ | Some value, Some pid -> $"#{prefix}_{name}_{value}"(*_{pid}*)
+ | Some value, None -> $"#{prefix}_{name}_{value}"
+ | None, Some pid -> $"#{prefix}_{name}"(*_{pid}*)
+ | _ -> $"#{prefix}_{name}"
+ |> Helper.ID.clean
+
+ static member genIdComponent(name : string, ?value : string, ?propertyID : string) =
+ PropertyValue.genId(name, ?value = value, ?propertyID = propertyID, prefix = "Component")
+
+ static member genIdParameterValue(name : string, ?value : string, ?propertyID : string) =
+ PropertyValue.genId(name, ?value = value, ?propertyID = propertyID, prefix = "ParameterValue")
+
+ static member genIdCharacteristicValue(name : string, ?value : string, ?propertyID : string) =
+ PropertyValue.genId(name, ?value = value, ?propertyID = propertyID, prefix = "CharacteristicValue")
+
+ static member genIdFactorValue(name : string, ?value : string, ?propertyID : string) =
+ PropertyValue.genId(name, ?value = value, ?propertyID = propertyID, prefix = "FactorValue")
+
+ static member create(name, ?value, ?id : string, ?propertyID, ?unitCode, ?unitText, ?valueReference, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> PropertyValue.genId(name, ?value = value, ?propertyID = propertyID)
+ let pv = LDNode(id, schemaType = ResizeArray [PropertyValue.schemaType], ?context = context)
+ PropertyValue.setNameAsString(pv, name, ?context = context)
+ //PropertyValue.setValueAsString(pv, value, ?context = context)
+ pv.SetOptionalProperty(PropertyValue.value, value, ?context = context)
+ propertyID |> Option.iter (fun pid -> PropertyValue.setPropertyIDAsString(pv, pid, ?context = context))
+ unitCode |> Option.iter (fun uc -> PropertyValue.setUnitCodeAsString(pv, uc, ?context = context))
+ unitText |> Option.iter (fun ut -> PropertyValue.setUnitTextAsString(pv, ut, ?context = context))
+ valueReference |> Option.iter (fun vr -> PropertyValue.setValueReference(pv, vr, ?context = context))
+ pv
+
+ static member createComponent(name, ?value, ?id, ?propertyID, ?unitCode, ?unitText, ?valueReference, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> PropertyValue.genIdComponent(name, ?value = value, ?propertyID = propertyID)
+ let c = PropertyValue.create(name, id = id, ?value = value, ?propertyID = propertyID, ?unitCode = unitCode, ?unitText = unitText, ?valueReference = valueReference, ?context = context)
+ c.AdditionalType <- ResizeArray ["Component"]
+ c
+
+ static member createParameterValue(name, ?value, ?id, ?propertyID, ?unitCode, ?unitText, ?valueReference, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> PropertyValue.genIdParameterValue(name, ?value = value, ?propertyID = propertyID)
+ let pv = PropertyValue.create(name, id = id, ?value = value, ?propertyID = propertyID, ?unitCode = unitCode, ?unitText = unitText, ?valueReference = valueReference, ?context = context)
+ pv.AdditionalType <- ResizeArray ["ParameterValue"]
+ pv
+
+ static member createCharacteristicValue(name, ?value, ?id, ?propertyID, ?unitCode, ?unitText, ?valueReference, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> PropertyValue.genIdCharacteristicValue(name, ?value = value, ?propertyID = propertyID)
+ let cv = PropertyValue.create(name, id = id, ?value = value, ?propertyID = propertyID, ?unitCode = unitCode, ?unitText = unitText, ?valueReference = valueReference, ?context = context)
+ cv.AdditionalType <- ResizeArray ["CharacteristicValue"]
+ cv
+
+ static member createFactorValue(name, ?value, ?id, ?propertyID, ?unitCode, ?unitText, ?valueReference, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> PropertyValue.genIdFactorValue(name, ?value = value, ?propertyID = propertyID)
+ let fv = PropertyValue.create(name, id = id, ?value = value, ?propertyID = propertyID, ?unitCode = unitCode, ?unitText = unitText, ?valueReference = valueReference, ?context = context)
+ fv.AdditionalType <- ResizeArray ["FactorValue"]
+ fv
\ No newline at end of file
diff --git a/src/ROCrate/Generic/Sample.fs b/src/ROCrate/Generic/Sample.fs
new file mode 100644
index 00000000..53f748b2
--- /dev/null
+++ b/src/ROCrate/Generic/Sample.fs
@@ -0,0 +1,101 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+
+///
+[]
+type Sample =
+
+ static member schemaType = "https://bioschemas.org/Sample"
+
+ static member name = "http://schema.org/name"
+
+ static member additionalProperty = "http://schema.org/additionalProperty"
+
+ static member tryGetNameAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(Sample.name, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getNameAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(Sample.name, ?context = context) with
+ | Some (:? string as n) -> n
+ | _ -> failwith $"Could not access property `name` of object with @id `{s.Id}`"
+
+ static member setNameAsString(s : LDNode, n : string) =
+ s.SetProperty(Sample.name, n)
+
+ static member getAdditionalProperties(s : LDNode, ?graph : LDGraph, ?context : LDContext) : ResizeArray =
+ let filter ldObject context = PropertyValue.validate(ldObject, ?context = context)
+ s.GetPropertyNodes(Sample.additionalProperty, filter = filter, ?graph = graph, ?context = context)
+
+ static member setAdditionalProperties(s : LDNode, additionalProperties : ResizeArray, ?context : LDContext) =
+ s.SetProperty(Sample.additionalProperty, additionalProperties, ?context = context)
+
+ static member getCharacteristics(s : LDNode, ?graph : LDGraph, ?context : LDContext) : ResizeArray =
+ let filter ldObject context = PropertyValue.validateCharacteristicValue(ldObject, ?context = context)
+ s.GetPropertyNodes(Sample.additionalProperty, filter = filter, ?graph = graph, ?context = context)
+
+ static member getFactors(s : LDNode, ?graph : LDGraph, ?context : LDContext) : ResizeArray =
+ let filter ldObject context = PropertyValue.validateFactorValue(ldObject, ?context = context)
+ s.GetPropertyNodes(Sample.additionalProperty, filter = filter, ?graph = graph, ?context = context)
+
+ static member validate(s : LDNode, ?context : LDContext) =
+ s.HasType(Sample.schemaType, ?context = context)
+ && s.HasProperty(Sample.name, ?context = context)
+
+ static member genIDSample(name : string) =
+ $"#Sample_{name}"
+ |> Helper.ID.clean
+
+ static member genIDSource(name : string) =
+ $"#Source_{name}"
+ |> Helper.ID.clean
+
+ static member genIDMaterial(name : string) =
+ $"#Material_{name}"
+ |> Helper.ID.clean
+
+ static member validateSample (s : LDNode, ?context : LDContext) =
+ Sample.validate(s, ?context = context)
+ && s.AdditionalType.Contains("Sample")
+
+ static member validateSource (s : LDNode, ?context : LDContext) =
+ Sample.validate(s, ?context = context)
+ && s.AdditionalType.Contains("Source")
+
+ static member validateMaterial (s : LDNode, ?context : LDContext) =
+ Sample.validate(s, ?context = context)
+ && s.AdditionalType.Contains("Material")
+
+ static member create(id : string, name : string, ?additionalProperties : ResizeArray, ?context : LDContext) =
+ let s = LDNode(id, ResizeArray [Sample.schemaType], ?context = context)
+ s.SetProperty(Sample.name, name, ?context = context)
+ s.SetOptionalProperty(Sample.additionalProperty, additionalProperties, ?context = context)
+ s
+
+ static member createSample (name : string, ?id : string, ?additionalProperties : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some id -> id
+ | None -> Sample.genIDSample name
+ let s = Sample.create(id, name, ?additionalProperties = additionalProperties, ?context = context)
+ s.AdditionalType <- ResizeArray ["Sample"]
+ s
+
+ static member createSource (name : string, ?id : string, ?additionalProperties : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some id -> id
+ | None -> Sample.genIDSource name
+ let s = Sample.create(id, name, ?additionalProperties = additionalProperties, ?context = context)
+ s.AdditionalType <- ResizeArray ["Source"]
+ s
+
+ static member createMaterial (name : string, ?id, ?additionalProperties : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some id -> id
+ | None -> Sample.genIDMaterial name
+ let s = Sample.create(id, name, ?additionalProperties = additionalProperties, ?context = context)
+ s.AdditionalType <- ResizeArray ["Material"]
+ s
\ No newline at end of file
diff --git a/src/ROCrate/Generic/ScholarlyArticle.fs b/src/ROCrate/Generic/ScholarlyArticle.fs
new file mode 100644
index 00000000..a3985283
--- /dev/null
+++ b/src/ROCrate/Generic/ScholarlyArticle.fs
@@ -0,0 +1,103 @@
+namespace ARCtrl.ROCrate
+
+open DynamicObj
+open Fable.Core
+open ARCtrl.ROCrate
+///
+
+[]
+type ScholarlyArticle =
+
+ static member schemaType = "http://schema.org/ScholarlyArticle"
+
+ static member headline = "http://schema.org/headline"
+
+ static member identifier = "http://schema.org/identifier"
+
+ static member author = "http://schema.org/author"
+
+ static member url = "http://schema.org/url"
+
+ static member creativeWorkStatus = "http://schema.org/creativeWorkStatus"
+
+ static member comment = "http://schema.org/comment"
+
+ static member tryGetHeadlineAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(ScholarlyArticle.headline, ?context = context) with
+ | Some (:? string as n) -> Some n
+ | _ -> None
+
+ static member getHeadlineAsString(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(ScholarlyArticle.headline, ?context = context) with
+ | Some (:? string as n) -> n
+ | Some _ -> failwith $"Value of property `headline` of object with @id `{s.Id}` should have been a string"
+ | None -> failwith $"Could not access property `headline` of object with @id `{s.Id}`"
+
+ static member setHeadlineAsString(s : LDNode, n : string) =
+ s.SetProperty(ScholarlyArticle.headline, n)
+
+ static member getIdentifiers(s : LDNode, ?context : LDContext) =
+ s.GetPropertyValues(ScholarlyArticle.identifier, ?context = context)
+
+ static member getIdentifiersAsPropertyValue(s : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter = fun ldObject context -> PropertyValue.validate(ldObject, ?context = context)
+ s.GetPropertyNodes(ScholarlyArticle.identifier, filter = filter, ?graph = graph, ?context = context)
+
+ static member setIdentifiers(s : LDNode, identifiers : ResizeArray) =
+ s.SetProperty(ScholarlyArticle.identifier, identifiers)
+
+ static member getAuthors(s : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter ldObject context = Person.validate(ldObject, ?context = context)
+ s.GetPropertyNodes(ScholarlyArticle.author, filter = filter, ?graph = graph, ?context = context)
+
+ static member setAuthors(s : LDNode, authors : ResizeArray, ?context : LDContext) =
+ s.SetProperty(ScholarlyArticle.author, authors, ?context = context)
+
+ static member tryGetUrl(s : LDNode, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleton(ScholarlyArticle.url, ?context = context) with
+ | Some (:? string as u) -> Some u
+ | _ -> None
+
+ static member setUrl(s : LDNode, u : string, ?context : LDContext) =
+ s.SetProperty(ScholarlyArticle.url, u, ?context = context)
+
+ static member tryGetCreativeWorkStatus(s : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ match s.TryGetPropertyAsSingleNode(ScholarlyArticle.creativeWorkStatus, ?graph = graph, ?context = context) with
+ | Some cws when DefinedTerm.validate cws -> Some cws
+ | _ -> None
+
+ static member setCreativeWorkStatus(s : LDNode, cws : LDNode, ?context : LDContext) =
+ s.SetProperty(ScholarlyArticle.creativeWorkStatus, cws, ?context = context)
+
+ static member getComments(s : LDNode, ?graph : LDGraph, ?context : LDContext) =
+ let filter = fun ldObject context -> Comment.validate(ldObject, ?context = context)
+ s.GetPropertyNodes(ScholarlyArticle.comment, filter = filter, ?graph = graph, ?context = context)
+
+ static member setcomments(s : LDNode, comments : ResizeArray, ?context : LDContext) =
+ s.SetProperty(ScholarlyArticle.comment, comments, ?context = context)
+
+ static member genID(headline : string, ?url : string) =
+ match url with
+ | Some u -> u
+ | None -> $"#{headline}"
+ |> Helper.ID.clean
+
+ static member validate(s : LDNode, ?context : LDContext) =
+ s.HasType(ScholarlyArticle.schemaType, ?context = context)
+ && s.HasProperty(ScholarlyArticle.headline, ?context = context)
+ //&& s.HasProperty(ScholarlyArticle.identifier, ?context = context)
+
+ static member create(headline : string, identifiers : ResizeArray<#obj>, ?id : string, ?authors : ResizeArray, ?url : string, ?creativeWorkStatus : LDNode, ?comments : ResizeArray, ?context : LDContext) =
+ let id = match id with
+ | Some i -> i
+ | None -> ScholarlyArticle.genID(headline, ?url = url)
+ let s = LDNode(id, ResizeArray [ScholarlyArticle.schemaType], ?context = context)
+ s.SetProperty(ScholarlyArticle.headline, headline, ?context = context)
+ s.SetProperty(ScholarlyArticle.identifier, identifiers, ?context = context)
+ s.SetOptionalProperty(ScholarlyArticle.author, authors, ?context = context)
+ s.SetOptionalProperty(ScholarlyArticle.url, url, ?context = context)
+ s.SetOptionalProperty(ScholarlyArticle.creativeWorkStatus, creativeWorkStatus, ?context = context)
+ s.SetOptionalProperty(ScholarlyArticle.comment, comments, ?context = context)
+ s
+
+
\ No newline at end of file
diff --git a/src/ROCrate/Helper.fs b/src/ROCrate/Helper.fs
new file mode 100644
index 00000000..6ff35b06
--- /dev/null
+++ b/src/ROCrate/Helper.fs
@@ -0,0 +1,6 @@
+module ARCtrl.ROCrate.Helper
+
+module ID =
+
+ let clean (id : string) =
+ id.Replace(" ", "_")
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/Assay.fs b/src/ROCrate/ISAProfile/Assay.fs
deleted file mode 100644
index 5532ada4..00000000
--- a/src/ROCrate/ISAProfile/Assay.fs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Assay(
- id: string,
- identifier: string,
- ?about,
- ?comment,
- ?creator,
- ?hasPart,
- ?measurementMethod,
- ?measurementTechnique,
- ?url,
- ?variableMeasured
-) as this =
- inherit Dataset(id, "Assay")
- do
- DynObj.setProperty (nameof identifier) identifier this
-
- DynObj.setOptionalProperty (nameof measurementMethod) measurementMethod this
- DynObj.setOptionalProperty (nameof measurementTechnique) measurementTechnique this
- DynObj.setOptionalProperty (nameof variableMeasured) variableMeasured this
- DynObj.setOptionalProperty (nameof about) about this
- DynObj.setOptionalProperty (nameof comment) comment this
- DynObj.setOptionalProperty (nameof creator) creator this
- DynObj.setOptionalProperty (nameof hasPart) hasPart this
- DynObj.setOptionalProperty (nameof url) url this
-
- member this.GetIdentifier() = DynObj.getMandatoryDynamicPropertyOrThrow "Assay" (nameof identifier) this
- static member getIdentifier = fun (ass: Assay) -> ass.GetIdentifier()
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/Data.fs b/src/ROCrate/ISAProfile/Data.fs
deleted file mode 100644
index 1f4e30ec..00000000
--- a/src/ROCrate/ISAProfile/Data.fs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Data(
- id,
- name,
- ?additionalType,
- ?comment,
- ?encodingFormat,
- ?disambiguatingDescription
-) as this =
- inherit LDObject(id = id, schemaType = "schema.org/MediaObject", ?additionalType = additionalType)
- do
- DynObj.setProperty (nameof name) name this
-
- DynObj.setOptionalProperty (nameof comment) comment this
- DynObj.setOptionalProperty (nameof encodingFormat) encodingFormat this
- DynObj.setOptionalProperty (nameof disambiguatingDescription) disambiguatingDescription this
-
- member this.GetName() = DynObj.getMandatoryDynamicPropertyOrThrow "Data" (nameof name) this
- static member getName = fun (d: Data) -> d.GetName()
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/Dataset.fs b/src/ROCrate/ISAProfile/Dataset.fs
deleted file mode 100644
index 52ce4779..00000000
--- a/src/ROCrate/ISAProfile/Dataset.fs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Dataset (id: string, ?additionalType: string) =
- inherit LDObject(id = id, schemaType = "schema.org/Dataset", ?additionalType = additionalType)
diff --git a/src/ROCrate/ISAProfile/Investigation.fs b/src/ROCrate/ISAProfile/Investigation.fs
deleted file mode 100644
index 8e08aff7..00000000
--- a/src/ROCrate/ISAProfile/Investigation.fs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Investigation(
- id: string,
- identifier: string,
- ?citation,
- ?comment,
- ?creator,
- ?dateCreated,
- ?dateModified,
- ?datePublished,
- ?hasPart,
- ?headline,
- ?mentions,
- ?url,
- ?description
-) as this =
- inherit Dataset(id, "Investigation")
- do
- DynObj.setProperty (nameof identifier) identifier this
-
- DynObj.setOptionalProperty (nameof citation) citation this
- DynObj.setOptionalProperty (nameof comment) comment this
- DynObj.setOptionalProperty (nameof creator) creator this
- DynObj.setOptionalProperty (nameof dateCreated) dateCreated this
- DynObj.setOptionalProperty (nameof dateModified) dateModified this
- DynObj.setOptionalProperty (nameof datePublished) datePublished this
- DynObj.setOptionalProperty (nameof hasPart) hasPart this
- DynObj.setOptionalProperty (nameof headline) headline this
- DynObj.setOptionalProperty (nameof mentions) mentions this
- DynObj.setOptionalProperty (nameof url) url this
- DynObj.setOptionalProperty (nameof description) description this
-
- member this.GetIdentifier() = DynObj.getMandatoryDynamicPropertyOrThrow "Investigation" (nameof identifier) this
- static member getIdentifier = fun (inv: Investigation) -> inv.GetIdentifier()
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/LabProcess.fs b/src/ROCrate/ISAProfile/LabProcess.fs
deleted file mode 100644
index 9abb4fdc..00000000
--- a/src/ROCrate/ISAProfile/LabProcess.fs
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type LabProcess(
- id: string,
- name,
- agent,
- object,
- result,
- ?additionalType,
- ?executesLabProtocol,
- ?parameterValue,
- ?endTime,
- ?disambiguatingDescription
-) as this =
- inherit LDObject(id = id, schemaType = "bioschemas.org/LabProcess", ?additionalType = additionalType)
- do
- DynObj.setProperty (nameof name) name this
- DynObj.setProperty (nameof agent) agent this
- DynObj.setProperty (nameof object) object this
- DynObj.setProperty (nameof result) result this
-
- DynObj.setOptionalProperty (nameof executesLabProtocol) executesLabProtocol this
- DynObj.setOptionalProperty (nameof parameterValue) parameterValue this
- DynObj.setOptionalProperty (nameof endTime) endTime this
- DynObj.setOptionalProperty (nameof disambiguatingDescription) disambiguatingDescription this
-
- member this.GetName() = DynObj.getMandatoryDynamicPropertyOrThrow "LabProcess" (nameof name) this
- static member getName = fun (lp: LabProcess) -> lp.GetName()
-
- member this.GetAgent() = DynObj.getMandatoryDynamicPropertyOrThrow "LabProcess" (nameof agent) this
- static member getAgent = fun (lp: LabProcess) -> lp.GetAgent()
-
- member this.GetObject() = DynObj.getMandatoryDynamicPropertyOrThrow "LabProcess" (nameof object) this
- static member getObject = fun (lp: LabProcess) -> lp.GetObject()
-
- member this.GetResult() = DynObj.getMandatoryDynamicPropertyOrThrow "LabProcess" (nameof result) this
- static member getResult = fun (lp: LabProcess) -> lp.GetResult()
diff --git a/src/ROCrate/ISAProfile/LabProtocol.fs b/src/ROCrate/ISAProfile/LabProtocol.fs
deleted file mode 100644
index db6a66d4..00000000
--- a/src/ROCrate/ISAProfile/LabProtocol.fs
+++ /dev/null
@@ -1,31 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type LabProtocol(
- id,
- ?additionalType,
- ?name,
- ?intendedUse,
- ?description,
- ?url,
- ?comment,
- ?version,
- ?labEquipment,
- ?reagent,
- ?computationalTool
-) as this =
- inherit LDObject(id = id, schemaType = "bioschemas.org/LabProtocol", ?additionalType = additionalType)
- do
- DynObj.setOptionalProperty (nameof name) name this
- DynObj.setOptionalProperty (nameof intendedUse) intendedUse this
- DynObj.setOptionalProperty (nameof description) description this
- DynObj.setOptionalProperty (nameof url) url this
- DynObj.setOptionalProperty (nameof comment) comment this
- DynObj.setOptionalProperty (nameof version) version this
- DynObj.setOptionalProperty (nameof labEquipment) labEquipment this
- DynObj.setOptionalProperty (nameof reagent) reagent this
- DynObj.setOptionalProperty (nameof computationalTool) computationalTool this
diff --git a/src/ROCrate/ISAProfile/Person.fs b/src/ROCrate/ISAProfile/Person.fs
deleted file mode 100644
index b3076813..00000000
--- a/src/ROCrate/ISAProfile/Person.fs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Person(
- id,
- givenName,
- ?additionalType,
- ?familyName,
- ?email,
- ?identifier,
- ?affiliation,
- ?jobTitle,
- ?additionalName,
- ?address,
- ?telephone,
- ?faxNumber,
- ?disambiguatingDescription
-) as this=
- inherit LDObject(id = id, schemaType = "schema.org/Person", ?additionalType = additionalType)
- do
-
- DynObj.setProperty (nameof givenName) givenName this
-
- DynObj.setOptionalProperty (nameof familyName) familyName this
- DynObj.setOptionalProperty (nameof email) email this
- DynObj.setOptionalProperty (nameof identifier) identifier this
- DynObj.setOptionalProperty (nameof affiliation) affiliation this
- DynObj.setOptionalProperty (nameof jobTitle) jobTitle this
- DynObj.setOptionalProperty (nameof additionalName) additionalName this
- DynObj.setOptionalProperty (nameof address) address this
- DynObj.setOptionalProperty (nameof telephone) telephone this
- DynObj.setOptionalProperty (nameof faxNumber) faxNumber this
- DynObj.setOptionalProperty (nameof disambiguatingDescription) disambiguatingDescription this
-
- member this.GetGivenName() = DynObj.getMandatoryDynamicPropertyOrThrow "Person" (nameof givenName) this
- static member getGivenName = fun (p: Person) -> p.GetGivenName()
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/PropertyValue.fs b/src/ROCrate/ISAProfile/PropertyValue.fs
deleted file mode 100644
index 730ad7a7..00000000
--- a/src/ROCrate/ISAProfile/PropertyValue.fs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type PropertyValue(
- id,
- name,
- value,
- ?propertyID,
- ?unitCode,
- ?unitText,
- ?valueReference,
- ?additionalType
-) as this =
- inherit LDObject(id = id, schemaType = "schema.org/PropertyValue", ?additionalType = additionalType)
- do
-
- DynObj.setProperty (nameof name) name this
- DynObj.setProperty (nameof value) value this
-
- DynObj.setOptionalProperty (nameof propertyID) propertyID this
- DynObj.setOptionalProperty (nameof unitCode) unitCode this
- DynObj.setOptionalProperty (nameof unitText) unitText this
- DynObj.setOptionalProperty (nameof valueReference) valueReference this
-
- member this.GetName() = DynObj.getMandatoryDynamicPropertyOrThrow "PropertyValue" (nameof name) this
- static member getName = fun (lp: PropertyValue) -> lp.GetName()
-
- member this.GetValue() = DynObj.getMandatoryDynamicPropertyOrThrow "PropertyValue" (nameof name) this
- static member getValue = fun (lp: PropertyValue) -> lp.GetValue()
diff --git a/src/ROCrate/ISAProfile/Sample.fs b/src/ROCrate/ISAProfile/Sample.fs
deleted file mode 100644
index a607ce56..00000000
--- a/src/ROCrate/ISAProfile/Sample.fs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Sample(
- id,
- name,
- ?additionalType,
- ?additionalProperty,
- ?derivesFrom
-) as this =
- inherit LDObject(id = id, schemaType = "bioschemas.org/Sample", ?additionalType = additionalType)
- do
- DynObj.setProperty (nameof name) name this
-
- DynObj.setOptionalProperty (nameof additionalProperty) additionalProperty this
- DynObj.setOptionalProperty (nameof derivesFrom) derivesFrom this
-
- member this.GetName() = DynObj.getMandatoryDynamicPropertyOrThrow "Sample" (nameof name) this
- static member getName = fun (s: Sample) -> s.GetName()
diff --git a/src/ROCrate/ISAProfile/ScholarlyArticle.fs b/src/ROCrate/ISAProfile/ScholarlyArticle.fs
deleted file mode 100644
index cb4905a5..00000000
--- a/src/ROCrate/ISAProfile/ScholarlyArticle.fs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type ScholarlyArticle(
- id,
- headline,
- identifier,
- ?additionalType,
- ?author,
- ?url,
- ?creativeWorkStatus,
- ?disambiguatingDescription
-
-) as this =
- inherit LDObject(id = id, schemaType = "schema.org/ScholarlyArticle", ?additionalType = additionalType)
- do
-
- DynObj.setProperty (nameof headline) headline this
- DynObj.setProperty (nameof identifier) identifier this
-
- DynObj.setOptionalProperty (nameof author) author this
- DynObj.setOptionalProperty (nameof url) url this
- DynObj.setOptionalProperty (nameof creativeWorkStatus) creativeWorkStatus this
- DynObj.setOptionalProperty (nameof disambiguatingDescription) disambiguatingDescription this
-
- member this.GetHeadline() = DynObj.getMandatoryDynamicPropertyOrThrow "ScholarlyArticle" (nameof headline) this
- static member getHeadline = fun (s: ScholarlyArticle) -> s.GetHeadline()
-
- member this.GetIdentifier() = DynObj.getMandatoryDynamicPropertyOrThrow "ScholarlyArticle" (nameof identifier) this
- static member getIdentifier = fun (s: ScholarlyArticle) -> s.GetIdentifier()
\ No newline at end of file
diff --git a/src/ROCrate/ISAProfile/Study.fs b/src/ROCrate/ISAProfile/Study.fs
deleted file mode 100644
index 41bbd82e..00000000
--- a/src/ROCrate/ISAProfile/Study.fs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace ARCtrl.ROCrate
-
-open DynamicObj
-open Fable.Core
-
-///
-[]
-type Study(
- id: string,
- identifier: string,
- ?about,
- ?citation,
- ?comment,
- ?creator,
- ?dateCreated,
- ?dateModified,
- ?datePublished,
- ?description,
- ?hasPart,
- ?headline,
- ?url
-) as this =
- inherit Dataset(id, "Study")
- do
- DynObj.setProperty (nameof identifier) identifier this
- DynObj.setOptionalProperty (nameof about) about this
- DynObj.setOptionalProperty (nameof citation) citation this
- DynObj.setOptionalProperty (nameof comment) comment this
- DynObj.setOptionalProperty (nameof creator) creator this
- DynObj.setOptionalProperty (nameof dateCreated) dateCreated this
- DynObj.setOptionalProperty (nameof dateModified) dateModified this
- DynObj.setOptionalProperty (nameof datePublished) datePublished this
- DynObj.setOptionalProperty (nameof description) description this
- DynObj.setOptionalProperty (nameof hasPart) hasPart this
- DynObj.setOptionalProperty (nameof headline) headline this
- DynObj.setOptionalProperty (nameof url) url this
-
- member this.GetIdentifier() = DynObj.getMandatoryDynamicPropertyOrThrow "Study" (nameof identifier) this
- static member getIdentifier = fun (inv: Investigation) -> inv.GetIdentifier()
diff --git a/src/ROCrate/LDContext.fs b/src/ROCrate/LDContext.fs
new file mode 100644
index 00000000..98284efc
--- /dev/null
+++ b/src/ROCrate/LDContext.fs
@@ -0,0 +1,219 @@
+namespace ARCtrl.ROCrate
+
+open System.Collections.Generic
+open ARCtrl.Helper
+open Fable.Core
+
+module Dictionary =
+
+ let ofSeq (s : seq<'Key*'T>) =
+ let dict = Dictionary()
+ s
+ |> Seq.iter dict.Add
+ dict
+
+ let tryFind (key : 'Key) (dict : Dictionary<'Key,'T>) =
+ let b,v = dict.TryGetValue key
+ if b then Some v
+ else None
+
+module IRIHelper =
+
+ open ARCtrl.Helper.Regex
+
+ let compactIRIRegex = """(?.*):(?[^\/][^\/].*)"""
+
+ let (|CompactIri|_|) (termDefition : string) =
+ match termDefition with
+ | ActivePatterns.Regex compactIRIRegex result ->
+ let prefix = result.Groups.["prefix"].Value
+ let suffix = result.Groups.["suffix"].Value
+ Some(prefix,suffix)
+ | _ -> None
+
+ let combine (baseIRI : string) (relative : string) =
+ if relative.StartsWith("http://") || relative.StartsWith("https://") then
+ relative
+ else
+ let baseUri = new System.Uri(baseIRI)
+ let relativeUri = new System.Uri(baseUri,relative)
+ relativeUri.ToString()
+
+ let combineOptional (baseIRI : string option) (relative : string option) =
+ match baseIRI, relative with
+ | Some b, Some r -> Some (combine b r)
+ | Some b, None -> Some b
+ | None, Some r -> Some r
+ | _ -> None
+
+// Add second dictionary which maps from definition to term?
+// Make LDContext to be nested hierarchical tree? Like this you can iterate through the tree and stop at the first match, kind of like a shadowing mechanism
+[]
+type LDContext(?mappings : Dictionary, ?baseContexts : ResizeArray) =
+
+ let mutable baseContexts = Option.defaultValue (ResizeArray []) baseContexts
+ let mutable name : string option = None
+
+ /// Dictionary
+ let mappings : Dictionary =
+ match mappings with
+ | Some m -> m
+ | None -> Dictionary()
+
+ /// Dictionary
+ let reverseMappings : Dictionary = Dictionary()
+
+ /// Dictionary
+ let compactReverseMappings : Dictionary = Dictionary()
+
+ let addReverseMapping (key : string) (value : string) =
+ Dictionary.addOrUpdate value key reverseMappings
+ match value with
+ | IRIHelper.CompactIri (prefix,suffix) ->
+ Dictionary.addOrUpdate prefix (suffix,key) compactReverseMappings
+ match Dictionary.tryFind prefix mappings with
+ | Some prefix ->
+ let iri = IRIHelper.combine prefix suffix
+ Dictionary.addOrUpdate iri key reverseMappings
+ | None -> ()
+ | _ ->
+ match Dictionary.tryFind key compactReverseMappings with
+ | Some (suffix,term) ->
+ let iri = IRIHelper.combine value suffix
+ Dictionary.addOrUpdate iri term reverseMappings
+ | None -> ()
+
+ do for kvp in mappings do
+ addReverseMapping kvp.Key kvp.Value
+
+ let rec tryFindTerm (term : string) : string option =
+ let definition =
+ match Dictionary.tryFind term mappings with
+ | Some v -> Some v
+ | None ->
+ baseContexts
+ |> Seq.tryPick (fun ctx -> ctx.TryResolveTerm term)
+ match definition with
+ | Some (IRIHelper.CompactIri (prefix,suffix)) ->
+ let prefix = if prefix = term then prefix else tryFindTerm prefix |> Option.defaultValue prefix
+ let suffix = if suffix = term then suffix else tryFindTerm suffix |> Option.defaultValue suffix
+ IRIHelper.combine prefix suffix
+ |> Some
+ | Some d -> Some d
+ | None -> None
+
+ let tryFindIri (iri : string) =
+ match Dictionary.tryFind iri reverseMappings with
+ | Some v -> Some v
+ | None ->
+ baseContexts
+ |> Seq.tryPick (fun ctx -> ctx.TryGetTerm iri)
+
+ let tryCompactIRI (iri : string) =
+ failwith "TryCompactIRI is Not implemented yet"
+
+ member this.Mappings
+ with get() = mappings
+
+ member this.BaseContexts
+ with get() = baseContexts
+ and internal set(value) = baseContexts <- value
+
+ member this.Name
+ with get() = name
+ and set(value) = name <- value
+
+ member this.AddMapping(term,definition) =
+ Dictionary.addOrUpdate term definition mappings
+ addReverseMapping term definition
+
+ member this.TryResolveTerm(term : string) =
+ // Handle compact IRI
+ if term.Contains(":") then
+ term.Split(':')
+ |> Seq.map tryFindTerm
+ |> Seq.reduce IRIHelper.combineOptional
+ else
+ tryFindTerm term
+
+ member this.TryGetTerm(iri : string) =
+ tryFindIri iri
+
+ member this.PropertyNamesMatch(p1 : string,p2 : string) =
+ if p1 = p2 then true
+ else
+ let p1Def = this.TryResolveTerm p1
+ let p2Def = this.TryResolveTerm p2
+ match p1Def,p2Def with
+ | Some p1Def, Some p2Def -> p1Def = p2Def
+ | Some p1Def, None -> p1Def = p2
+ | None, Some p2Def -> p1 = p2Def
+ | _ -> false
+
+ static member fromMappingSeq(mappings : seq) =
+ LDContext(Dictionary.ofSeq mappings)
+
+ // Append second context to the first one inplace
+ static member combine_InPlace (first : LDContext) (second : LDContext) : LDContext =
+ first.BaseContexts.Add second
+ first
+
+ // Create new context with the the two given contexts as baseContexts
+ static member combine (first : LDContext) (second : LDContext) : LDContext =
+ LDContext(baseContexts = ResizeArray([first;second]))
+
+ static member tryCombineOptional (first : LDContext option) (second : LDContext option) : LDContext option =
+ match first,second with
+ | Some f, Some s -> Some (LDContext.combine f s)
+ | Some f, None -> Some f
+ | None, Some s -> Some s
+ | _ -> None
+
+ member this.ShallowCopy() =
+ let newMappings = Dictionary()
+ for kvp in mappings do
+ newMappings.Add(kvp.Key,kvp.Value)
+ LDContext(mappings = newMappings, baseContexts = baseContexts)
+
+ member this.DeepCopy() =
+ let newMappings = Dictionary()
+ for kvp in mappings do
+ newMappings.Add(kvp.Key,kvp.Value)
+ let newBaseContexts = ResizeArray()
+ for ctx in baseContexts do
+ newBaseContexts.Add(ctx.DeepCopy())
+ LDContext(mappings = newMappings, baseContexts = newBaseContexts)
+
+ interface System.ICloneable with
+ member this.Clone() =
+ this.DeepCopy()
+
+ member this.StructurallyEquals(other : LDContext) =
+ this.GetHashCode() = other.GetHashCode()
+
+ member this.ReferenceEquals(other : LDContext) =
+ System.Object.ReferenceEquals(this,other)
+
+ override this.Equals(other : obj) =
+ match other with
+ | :? LDContext as other -> this.StructurallyEquals(other)
+ | _ -> false
+
+ override this.GetHashCode() =
+ let mappingsHash =
+ this.Mappings.Keys
+ |> Seq.sort
+ |> Seq.map (fun k -> HashCodes.mergeHashes (HashCodes.hash k) (HashCodes.hash this.Mappings.[k]))
+ |> HashCodes.boxHashSeq
+ |> fun v -> v :?> int
+ let nameHash =
+ match this.Name with
+ | Some n -> n.GetHashCode()
+ | None -> 0
+ let baseContextsHash =
+ if baseContexts.Count = 0 then 0
+ else
+ baseContexts
+ |> Seq.map (fun ctx -> ctx.GetHashCode())
+ |> Seq.reduce HashCodes.mergeHashes
+ HashCodes.mergeHashes (HashCodes.mergeHashes mappingsHash nameHash) baseContextsHash
\ No newline at end of file
diff --git a/src/ROCrate/LDObject.fs b/src/ROCrate/LDObject.fs
index bf001ba7..31708c05 100644
--- a/src/ROCrate/LDObject.fs
+++ b/src/ROCrate/LDObject.fs
@@ -5,22 +5,183 @@ open Thoth.Json.Core
open Fable.Core
open System
-type LDContext() = inherit DynamicObj()
+module ActivePattern =
+ #if !FABLE_COMPILER
+ let (|SomeObj|_|) =
+ // create generalized option type
+ let ty = typedefof