Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve LDObject, add ISA model API #478

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageVersion Include="FsSpreadsheet.Js" Version="6.3.1" />
<PackageVersion Include="FsSpreadsheet.Py" Version="6.3.1" />
<PackageVersion Include="YAMLicious" Version="0.0.3" />
<PackageVersion Include="DynamicObj" Version="4.0.3" />
<PackageVersion Include="DynamicObj" Version="7.0.0" />
<PackageVersion Include="Fable.SimpleHttp" Version="3.5.0" />
<PackageVersion Include="Fable.Fetch" Version="2.6.0" />
<PackageVersion Include="Fable.Node" Version="1.2.0" />
Expand Down
21 changes: 12 additions & 9 deletions build/ProjectInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 16 additions & 19 deletions build/TestTasks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,14 @@ module RunTests =
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"
)
Expand All @@ -65,13 +63,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"

Expand All @@ -81,8 +78,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"
)
Expand All @@ -105,6 +101,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.
Expand Down
6 changes: 2 additions & 4 deletions src/Json/Decode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ module Decode =
decoder.Decode(helpers,value)
}



let resizeArray (decoder: Decoder<'value>) : Decoder<ResizeArray<'value>> =
let resizeArrayOrSingleton (decoder: Decoder<'value>) : Decoder<ResizeArray<'value>> =
{ new Decoder<ResizeArray<'value>> with
member _.Decode(helpers, value) =
if helpers.isArray value then
Expand Down Expand Up @@ -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<System.DateTime> =
Expand Down
10 changes: 9 additions & 1 deletion src/Json/Encode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
| _ -> 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
84 changes: 67 additions & 17 deletions src/Json/LDObject.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,39 @@ open ARCtrl.ROCrate
open Thoth.Json.Core
open DynamicObj

module rec LDObject =
module LDContext =

let decoder : Decoder<LDContext> =
{ new Decoder<LDContext> 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 o = LDContext()
for property in properties do
if property <> "@id" && property <> "@type" then
o.SetProperty(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
else
("", BadPrimitive("an object", value)) |> Error
}

let encoder (ctx: LDContext) =
ctx.GetProperties true
|> Seq.map (fun kv -> kv.Key, kv.Value |> string |> Encode.string )
|> Encode.object

module rec LDObject =
#if !FABLE_COMPILER
let (|SomeObj|_|) =
// create generalized option type
Expand Down Expand Up @@ -45,20 +76,36 @@ module rec LDObject =
| _ -> 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
//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" then
yield kv.Key, genericEncoder kv.Value
]
|> Encode.object

Expand All @@ -75,12 +122,15 @@ module rec LDObject =
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 = LDObject(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
Expand Down
2 changes: 1 addition & 1 deletion src/ROCrate/ArcROCrateMetadata.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open DynamicObj

type ArcROCrateMetadata(?about : LDObject) as this =

inherit LDObject(id = "ro-crate-metadata",schemaType = "CreativeWork")
inherit LDObject(id = "ro-crate-metadata",schemaType = ResizeArray([|"CreativeWork"|]))

do DynObj.setOptionalProperty (nameof about) about this

Expand Down
8 changes: 7 additions & 1 deletion src/ROCrate/DynObjExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@

let inline hasProperty (propertyName: string) (obj: #DynamicObj) = DynObj.tryGetPropertyValue propertyName obj |> Option.isSome

let inline getMandatoryDynamicPropertyOrThrow<'TPropertyValue> (className:string) (propertyName: string) (obj: #DynamicObj) =

Check warning on line 9 in src/ROCrate/DynObjExtensions.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest)

This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near 'D:\a\ARCtrl\ARCtrl\src\ROCrate\DynObjExtensions.fs(9,115)-(9,126)' has been constrained to be type 'DynamicObj'.
if hasProperty propertyName obj then
match DynObj.tryGetTypedPropertyValue<'TPropertyValue> propertyName obj with
| 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?"))
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
2 changes: 1 addition & 1 deletion src/ROCrate/ISAProfile/Assay.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Assay(
?url,
?variableMeasured
) as this =
inherit Dataset(id, "Assay")
inherit Dataset(id = id, additionalType = ResizeArray[|"Assay"|])
do
DynObj.setProperty (nameof identifier) identifier this

Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/Data.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ type Data(
?encodingFormat,
?disambiguatingDescription
) as this =
inherit LDObject(id = id, schemaType = "schema.org/MediaObject", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"schema.org/MediaObject"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do
DynObj.setProperty (nameof name) name this

Expand Down
10 changes: 7 additions & 3 deletions src/ROCrate/ISAProfile/Dataset.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ namespace ARCtrl.ROCrate

open DynamicObj
open Fable.Core

///
[<AttachMembers>]
type Dataset (id: string, ?additionalType: string) =
inherit LDObject(id = id, schemaType = "schema.org/Dataset", ?additionalType = additionalType)
type Dataset (id: string, ?additionalType: ResizeArray<string>) =
inherit LDObject(
id = id,
schemaType = ResizeArray[|"schema.org/Dataset"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
2 changes: 1 addition & 1 deletion src/ROCrate/ISAProfile/Investigation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Investigation(
?url,
?description
) as this =
inherit Dataset(id, "Investigation")
inherit Dataset(id = id, additionalType = ResizeArray[|"Investigation"|])
do
DynObj.setProperty (nameof identifier) identifier this

Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/LabProcess.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ type LabProcess(
?endTime,
?disambiguatingDescription
) as this =
inherit LDObject(id = id, schemaType = "bioschemas.org/LabProcess", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"bioschemas.org/LabProcess"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do
DynObj.setProperty (nameof name) name this
DynObj.setProperty (nameof agent) agent this
Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/LabProtocol.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ type LabProtocol(
?reagent,
?computationalTool
) as this =
inherit LDObject(id = id, schemaType = "bioschemas.org/LabProtocol", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"bioschemas.org/LabProtocol"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do
DynObj.setOptionalProperty (nameof name) name this
DynObj.setOptionalProperty (nameof intendedUse) intendedUse this
Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/Person.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ type Person(
?faxNumber,
?disambiguatingDescription
) as this=
inherit LDObject(id = id, schemaType = "schema.org/Person", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"schema.org/Person"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do

DynObj.setProperty (nameof givenName) givenName this
Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/PropertyValue.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ type PropertyValue(
?valueReference,
?additionalType
) as this =
inherit LDObject(id = id, schemaType = "schema.org/PropertyValue", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"schema.org/PropertyValue"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do

DynObj.setProperty (nameof name) name this
Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/Sample.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ type Sample(
?additionalProperty,
?derivesFrom
) as this =
inherit LDObject(id = id, schemaType = "bioschemas.org/Sample", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"bioschemas.org/Sample"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do
DynObj.setProperty (nameof name) name this

Expand Down
6 changes: 5 additions & 1 deletion src/ROCrate/ISAProfile/ScholarlyArticle.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ type ScholarlyArticle(
?disambiguatingDescription

) as this =
inherit LDObject(id = id, schemaType = "schema.org/ScholarlyArticle", ?additionalType = additionalType)
inherit LDObject(
id = id,
schemaType = ResizeArray[|"schema.org/ScholarlyArticle"|],
additionalType = defaultArg additionalType (ResizeArray[||])
)
do

DynObj.setProperty (nameof headline) headline this
Expand Down
Loading
Loading