diff --git a/src/ISADotNet.XLSX/AssayFile/AnnotationNode.fs b/src/ISADotNet.XLSX/AssayFile/AnnotationNode.fs index 8f904fa8..99db4266 100644 --- a/src/ISADotNet.XLSX/AssayFile/AnnotationNode.fs +++ b/src/ISADotNet.XLSX/AssayFile/AnnotationNode.fs @@ -105,7 +105,7 @@ module AnnotationNode = (termAccessionGetter matrix i) (termSourceGetter matrix i) - let category = mergeOntology category1 category2 |> Option.map (Some >> ProtocolParameter.create None) + let category = mergeOntology h.Term category1 |> mergeOntology category2 |> Option.map (Some >> ProtocolParameter.create None) category, fun (matrix : System.Collections.Generic.Dictionary<(string * int),string>) i -> @@ -157,7 +157,7 @@ module AnnotationNode = (termSourceGetter matrix i) let factor = - mergeOntology category1 category2 + mergeOntology h.Term category1 |> mergeOntology category2 |> Option.map (fun oa -> Factor.create None (oa.Name |> Option.map AnnotationValue.toString) (Some oa) None) factor, @@ -186,6 +186,7 @@ module AnnotationNode = | Some v -> Some v | _ -> None | None -> None, fun _ _ -> None + let category2, termSourceGetter = match Seq.tryPick (tryParseTermSourceReferenceHeader h) headers with | Some h -> @@ -210,7 +211,7 @@ module AnnotationNode = (termAccessionGetter matrix i) (termSourceGetter matrix i) - let characteristic = mergeOntology category1 category2 |> Option.map (Some >> MaterialAttribute.create None) + let characteristic = mergeOntology h.Term category1 |> mergeOntology category2 |> Option.map (Some >> MaterialAttribute.create None) characteristic, fun (matrix : System.Collections.Generic.Dictionary<(string * int),string>) i -> diff --git a/src/ISADotNet.XLSX/Conversions.fs b/src/ISADotNet.XLSX/Conversions.fs index 2ccf06cb..b3577d18 100644 --- a/src/ISADotNet.XLSX/Conversions.fs +++ b/src/ISADotNet.XLSX/Conversions.fs @@ -153,6 +153,17 @@ module ProtocolParameter = sprintf "%s%c%s" sources separator source ) +module MaterialAttribute = + + /// Create a ISAJson MaterialAttribute from ISATab string entries + let fromString (term:string) (accession:string) (source:string) = + OntologyAnnotation.fromString term accession source + |> Option.fromValueWithDefault OntologyAnnotation.empty + |> MaterialAttribute.create None + + /// Get ISATab string entries from an ISAJson MaterialAttribute object + let toString (ma : MaterialAttribute) = + ma.CharacteristicType |> Option.map OntologyAnnotation.toString |> Option.defaultValue ("","","") module Value = diff --git a/src/ISADotNet.XLSX/ISADotNet.XLSX.fsproj b/src/ISADotNet.XLSX/ISADotNet.XLSX.fsproj index 6b596cbd..fa4459bb 100644 --- a/src/ISADotNet.XLSX/ISADotNet.XLSX.fsproj +++ b/src/ISADotNet.XLSX/ISADotNet.XLSX.fsproj @@ -29,12 +29,16 @@ - + + + + + nfdi4plants, Lukas Weil, Kevin Frey diff --git a/src/ISADotnet/ISADotNet.fsproj b/src/ISADotnet/ISADotNet.fsproj index d4f3aa89..a80b7397 100644 --- a/src/ISADotnet/ISADotNet.fsproj +++ b/src/ISADotnet/ISADotNet.fsproj @@ -50,6 +50,10 @@ + + + + diff --git a/src/ISADotnet/JsonIO/IO.fs b/src/ISADotnet/JsonIO/IO.fs index b02334ec..784f25ff 100644 --- a/src/ISADotnet/JsonIO/IO.fs +++ b/src/ISADotnet/JsonIO/IO.fs @@ -5,6 +5,21 @@ open System.Text.Json open System.IO open FSharp.SystemTextJson +module OntologyAnnotation = + + let fromString (s:string) = + JsonSerializer.Deserialize(s,JsonExtensions.options) + + let toString (oa:OntologyAnnotation) = + JsonSerializer.Serialize(oa,JsonExtensions.options) + + let fromFile (path : string) = + File.ReadAllText path + |> fromString + + let toFile (path : string) (oa:OntologyAnnotation) = + File.WriteAllText(path,toString oa) + module Protocol = let fromString (s:string) = diff --git a/tests/ISADotNet.Tests/ISADotNet.XLSX/AssayFileTests.fs b/tests/ISADotNet.Tests/ISADotNet.XLSX/AssayFileTests.fs index 1591ab8c..b59fdc8b 100644 --- a/tests/ISADotNet.Tests/ISADotNet.XLSX/AssayFileTests.fs +++ b/tests/ISADotNet.Tests/ISADotNet.XLSX/AssayFileTests.fs @@ -7,6 +7,7 @@ open ISADotNet open Expecto open TestingUtils +open ISADotNet open ISADotNet.XLSX open ISADotNet.XLSX.AssayFile @@ -142,6 +143,88 @@ let testHeaderSplittingFunctions = ) ] +[] +let testNodeGetterFunctions = + + let sourceDirectory = __SOURCE_DIRECTORY__ + @"/TestFiles/" + + let assayFilePath = System.IO.Path.Combine(sourceDirectory,"AssayTableTestFile.xlsx") + + let doc = Spreadsheet.fromFile assayFilePath false + let sst = Spreadsheet.tryGetSharedStringTable doc + let wsp = Spreadsheet.tryGetWorksheetPartBySheetIndex 0u doc |> Option.get + let table = Table.tryGetByNameBy (fun s -> s.Contains "annotationTable") wsp |> Option.get + + let m = Table.toSparseValueMatrix sst (Worksheet.getSheetData wsp.Worksheet) table + + testList "NodGetterTests" [ + testCase "RetreiveValueFromMatrix" (fun () -> + let tryGetValue k (dict:System.Collections.Generic.Dictionary<'K,'V>) = + let b,v = dict.TryGetValue(k) + if b then Some v + else None + + let v = tryGetValue ("Unit [square centimeter] (#h; #tUO:0000081; #u)",0) m + + Expect.isSome v "Value could not be retrieved from matrix" + + let expectedValue = "square centimeter" + + Expect.equal v.Value expectedValue "Value retrieved from matrix is not correct" + + ) + + testCase "GetUnitGetter" (fun () -> + + let headers = ["Unit [square centimeter] (#h; #tUO:0000081; #u)";"Term Source REF [square centimeter] (#h; #tUO:0000081; #u)";"Term Accession Number [square centimeter] (#h; #tUO:0000081; #u)"] + + let unitGetterOption = AnnotationNode.tryGetUnitGetterFunction headers + + Expect.isSome unitGetterOption "Unit Getter was not returned even though headers should have matched" + + let unitGetter = unitGetterOption.Value + + let unit = unitGetter m 0 + + let expectedUnit = OntologyAnnotation.fromString "square centimeter" "http://purl.obolibrary.org/obo/UO_0000081" "UO" + + Expect.equal unit expectedUnit "Retrieved Unit is wrong" + ) + testCase "GetUnitGetterWrongHeaders" (fun () -> + + let headers = ["Parameter [square centimeter] (#h; #tUO:0000081; #u)";"Term Source REF [square centimeter] (#h; #tUO:0000081; #u)";"Term Accession Number [square centimeter] (#h; #tUO:0000081; #u)"] + + let unitGetterOption = AnnotationNode.tryGetUnitGetterFunction headers + + Expect.isNone unitGetterOption "Unit Getter was returned even though headers should not have matched" + ) + testCase "GetCharacteristicsGetter" (fun () -> + + let headers = ["Characteristics [leaf size]";"Term Source REF [leaf size] (#h; #tTO:0002637)";"Term Accession Number [leaf size] (#h; #tTO:0002637)";"Unit [square centimeter] (#h; #tUO:0000081; #u)";"Term Source REF [square centimeter] (#h; #tUO:0000081; #u)";"Term Accession Number [square centimeter] (#h; #tUO:0000081; #u)"] + + let characteristicGetterOption = AnnotationNode.tryGetCharacteristicGetterFunction headers + + Expect.isSome characteristicGetterOption "Characteristic Getter was not returned even though headers should have matched" + + let characteristic,characteristicValue = characteristicGetterOption.Value + + //let unit = unitGetter m 0 + + let expectedCharacteristic = MaterialAttribute.fromString "leaf size" "0002637" "TO" + + Expect.equal characteristic.Value expectedCharacteristic "Retrieved Characteristic is wrong" + ) + testCase "GetCharacteristicsGetterWrongHeaders" (fun () -> + + let headers = ["Parameter [square centimeter] (#h; #tUO:0000081; #u)";"Term Source REF [square centimeter] (#h; #tUO:0000081; #u)";"Term Accession Number [square centimeter] (#h; #tUO:0000081; #u)"] + + let unitGetterOption = AnnotationNode.tryGetCharacteristicGetterFunction headers + + Expect.isNone unitGetterOption "Characteristic Getter was returned even though headers should not have matched" + ) + ] + + [] let testProcessComparisonFunctions = @@ -250,6 +333,21 @@ let testProcessComparisonFunctions = Expect.sequenceEqual mergedProcess2.Outputs.Value [ProcessOutput.Sample sample2;ProcessOutput.Sample sample4] "Inputs were not merged correctly for mergedProcess2" ) + testCase "IndexIdenticalProcessesByProtocolName" (fun () -> + + let process1 = Process.create None None (Some protocol1) (Some parameterValues1) None None None None (Some [Source source1]) (Some [Sample sample1]) None + let process2 = Process.create None None (Some protocol1) (Some parameterValues2) None None None None (Some [Source source2]) (Some [Sample sample2]) None + let process3 = Process.create None None (Some protocol2) (Some parameterValues1) None None None None (Some [Source source3]) (Some [Sample sample3]) None + let process4 = Process.create None None (Some protocol2) (Some parameterValues2) None None None None (Some [Source source4]) (Some [Sample sample4]) None + + let indexedProcesses = AnnotationTable.indexRelatedProcessesByProtocolName [process1;process2;process3;process4] + + let names = indexedProcesses |> Seq.map (fun p -> Option.defaultValue "" p.Name) + + let expectedNames = ["Protocol1_0";"Protocol1_1";"Protocol2_0";"Protocol2_1"] + + Expect.sequenceEqual names expectedNames "Processes were not indexed correctly" + ) ] @@ -257,8 +355,7 @@ let testProcessComparisonFunctions = let testMetaDataFunctions = let sourceDirectory = __SOURCE_DIRECTORY__ + @"/TestFiles/" - let sinkDirectory = System.IO.Directory.CreateDirectory(__SOURCE_DIRECTORY__ + @"/TestResult/").FullName - let referenceAssayFilePath = System.IO.Path.Combine(sourceDirectory,"AssayTestFile.xlsx") + let referenceAssayFilePath = System.IO.Path.Combine(sourceDirectory,"AssayMetadataTestFile.xlsx") testList "MetaDataTests" [ testCase "CanReadMetaData" (fun () -> diff --git a/tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayTestFile.xlsx b/tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayMetadataTestFile.xlsx similarity index 100% rename from tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayTestFile.xlsx rename to tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayMetadataTestFile.xlsx diff --git a/tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayTableTestFile.xlsx b/tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayTableTestFile.xlsx new file mode 100644 index 00000000..f05c86d0 Binary files /dev/null and b/tests/ISADotNet.Tests/ISADotNet.XLSX/TestFiles/AssayTableTestFile.xlsx differ