Skip to content

Commit 8c82636

Browse files
committed
add tests and fix empty cell parsing behaviour
1 parent a9b4fa1 commit 8c82636

7 files changed

Lines changed: 156 additions & 54 deletions

File tree

src/Core/Conversion.fs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,32 +68,36 @@ module JsonTypes =
6868
/// Convert a CompositeCell to a ISA Value and Unit tuple.
6969
let valueOfCell (value : CompositeCell) =
7070
match value with
71-
| CompositeCell.FreeText (text) -> Value.fromString text, None
72-
| CompositeCell.Term (term) -> Value.Ontology term, None
73-
| CompositeCell.Unitized (text,unit) -> Value.fromString text, Some unit
71+
| CompositeCell.FreeText ("") -> None, None
72+
| CompositeCell.FreeText (text) -> Some (Value.fromString text), None
73+
| CompositeCell.Term (term) when term.isEmpty() -> None, None
74+
| CompositeCell.Term (term) -> Some (Value.Ontology term), None
75+
| CompositeCell.Unitized (text,unit) ->
76+
(if text = "" then None else Value.fromString text |> Some),
77+
if unit.isEmpty() then None else unit |> Some
7478

7579
/// Convert a CompositeHeader and Cell tuple to a ISA Component
7680
let composeComponent (header : CompositeHeader) (value : CompositeCell) : Component =
7781
let v,u = valueOfCell value
78-
Component.create (v,?unit = u,componentType = header.ToTerm())
82+
Component.create (?value = v,?unit = u,componentType = header.ToTerm())
7983

8084
/// Convert a CompositeHeader and Cell tuple to a ISA ProcessParameterValue
8185
let composeParameterValue (header : CompositeHeader) (value : CompositeCell) : ProcessParameterValue =
8286
let v,u = valueOfCell value
8387
let p = ProtocolParameter.create(ParameterName = header.ToTerm())
84-
ProcessParameterValue.create(p,v,?Unit = u)
88+
ProcessParameterValue.create(p,?Value = v,?Unit = u)
8589

8690
/// Convert a CompositeHeader and Cell tuple to a ISA FactorValue
8791
let composeFactorValue (header : CompositeHeader) (value : CompositeCell) : FactorValue =
8892
let v,u = valueOfCell value
8993
let f = Factor.create(Name = header.ToString(),FactorType = header.ToTerm())
90-
FactorValue.create(Category = f,Value = v,?Unit = u)
94+
FactorValue.create(Category = f,?Value = v,?Unit = u)
9195

9296
/// Convert a CompositeHeader and Cell tuple to a ISA MaterialAttributeValue
9397
let composeCharacteristicValue (header : CompositeHeader) (value : CompositeCell) : MaterialAttributeValue =
9498
let v,u = valueOfCell value
9599
let c = MaterialAttribute.create(CharacteristicType = header.ToTerm())
96-
MaterialAttributeValue.create(Category = c,Value = v,?Unit = u)
100+
MaterialAttributeValue.create(Category = c,?Value = v,?Unit = u)
97101

98102
/// Convert a CompositeHeader and Cell tuple to a ISA ProcessInput
99103
let composeProcessInput (header : CompositeHeader) (value : CompositeCell) : ProcessInput =
@@ -124,12 +128,13 @@ module JsonTypes =
124128
let value = value |> Option.defaultValue (Value.Name "")
125129
match value,unit with
126130
| Value.Ontology oa, None -> CompositeCell.Term oa
127-
| Value.Name text, None -> CompositeCell.FreeText text
131+
| Value.Name "", None -> CompositeCell.Term (OntologyAnnotation())
132+
| Value.Name text, None -> CompositeCell.Term (OntologyAnnotation(text))
128133
| Value.Name name, Some u -> CompositeCell.Unitized (name,u)
129134
| Value.Float f, Some u -> CompositeCell.Unitized (f.ToString(),u)
130-
| Value.Float f, None -> CompositeCell.FreeText (f.ToString())
135+
| Value.Float f, None -> CompositeCell.Unitized (f.ToString(),OntologyAnnotation())
131136
| Value.Int i, Some u -> CompositeCell.Unitized (i.ToString(),u)
132-
| Value.Int i, None -> CompositeCell.FreeText (i.ToString())
137+
| Value.Int i, None -> CompositeCell.Unitized (i.ToString(),OntologyAnnotation())
133138
| _ -> failwithf "Could not parse value %O with unit %O" value unit
134139

135140
/// Convert an ISA Component to a CompositeHeader and Cell tuple

src/Core/OntologyAnnotation.fs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,12 @@ type OntologyAnnotation(?name,?tsr,?tan, ?comments) =
5454
static member createUriAnnotation (termSourceRef : string) (localTAN : string) =
5555
$"{Url.OntobeeOboPurl}{termSourceRef}_{localTAN}"
5656

57-
/// Will always be created without `OntologyAnnotion.Name`
5857
static member fromTermAnnotation (tan : string, ?name) =
59-
let name = Option.defaultValue "" name
60-
tan
61-
|> Regex.tryParseTermAnnotation
62-
|> Option.get
63-
|> fun r ->
58+
match Regex.tryParseTermAnnotation tan with
59+
| Some r ->
6460
let accession = r.IDSpace + ":" + r.LocalID
65-
OntologyAnnotation.create (name, r.IDSpace, accession)
61+
OntologyAnnotation.create (?name = name, tsr = r.IDSpace, tan = accession)
62+
| None -> OntologyAnnotation.create (?name = name, tan = tan)
6663

6764
/// Parses any value in `TermAccessionString` to term accession format "termsourceref:localtan". Exmp.: "MS:000001".
6865
///
@@ -131,6 +128,9 @@ type OntologyAnnotation(?name,?tsr,?tan, ?comments) =
131128
sb.Append("}") |> ignore
132129
sb.ToString()
133130

131+
member this.isEmpty() =
132+
this.Name.IsNone && this.TermSourceREF.IsNone && this.TermAccessionNumber.IsNone && this.Comments.Count = 0
133+
134134
override this.GetHashCode() =
135135
[|
136136
HashCodes.boxHashOption this.Name

src/Core/Process/ColumnIndex.fs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ let createOrderComment (index : int) =
1313
Comment.create(orderName,(string index))
1414

1515
let tryGetIndex (comments : ResizeArray<Comment>) =
16-
comments
17-
|> CommentArray.tryItem orderName
18-
|> Option.bind tryInt
16+
match comments |> CommentArray.tryItem orderName with
17+
| Some ci ->
18+
let i = comments |> Seq.findIndex (fun c -> c.Name = Some orderName)
19+
comments.RemoveAt(i)
20+
tryInt ci
21+
| _ -> None
1922

2023
let setOntologyAnnotationIndexInplace i (oa : OntologyAnnotation) =
2124
oa.Comments.Add(createOrderComment i)

src/Core/Process/Value.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type Value =
2626
#endif
2727
match parser value with
2828
| (true, f) -> Value.Float f
29-
| _ -> Value.Ontology <| OntologyAnnotation.create value
29+
| _ -> Value.Name value
3030

3131
static member fromOptions (value : string Option) (termSource: string Option) (termAccesssion: string Option) =
3232
match value, termSource, termAccesssion with

src/Json/Process/PropertyValue.fs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ module PropertyValue =
1313

1414
let genID (p : IPropertyValue<'T>) =
1515
match p.GetCategory() with
16-
| Some t -> $"{p.GetAdditionalType}/{t}{p.GetValue()}{p.GetUnit()}"
17-
| None -> $"#Empty{p.GetAdditionalType}"
16+
| Some t -> $"{p.GetAdditionalType()}/{t}{p.GetValue()}{p.GetUnit()}"
17+
| None -> $"#Empty{p.GetAdditionalType()}"
1818

1919
let encoder<'T> (pv : IPropertyValue<'T>) =
2020
let categoryName, categoryURL =
@@ -54,24 +54,48 @@ module PropertyValue =
5454

5555
let decoder<'T> (create : createPVFunction<'T>) : Decoder<'T>=
5656
Decode.object (fun get ->
57-
let category =
58-
let name = get.Optional.Field "category" Decode.string
59-
let code = get.Optional.Field "categoryCode" Decode.string
60-
match name, code with
61-
| None, None -> None
62-
| _, None -> Some(OntologyAnnotation.create(?name = name))
63-
| _, Some code -> Some(OntologyAnnotation.fromTermAnnotation(code, ?name = name))
64-
let value =
65-
let value = get.Optional.Field "value" AnnotationValue.decoder
66-
let code = get.Optional.Field "valueCode" Decode.string
67-
if value.IsNone && code.IsNone then None
68-
else Value.fromOptions value None code
69-
let unit =
70-
let name = get.Optional.Field "unit" Decode.string
71-
let code = get.Optional.Field "unitCode" Decode.string
72-
match name, code with
73-
| None, None -> None
74-
| _, None -> Some(OntologyAnnotation.create(?name = name))
75-
| _, Some code -> Some(OntologyAnnotation.fromTermAnnotation(code, ?name = name))
76-
create category value unit
57+
58+
let category =
59+
let name = get.Optional.Field "category" Decode.string
60+
let code = get.Optional.Field "categoryCode" Decode.string
61+
match name, code with
62+
| None, None | None, Some "" -> None
63+
| _, None | _, Some "" ->
64+
try Some(OntologyAnnotation.create(?name = name))
65+
with
66+
| err ->
67+
failwith $"Error while decoding category (name:{name}): {err}"
68+
| _, Some code ->
69+
try Some(OntologyAnnotation.fromTermAnnotation(code, ?name = name))
70+
with
71+
| err -> failwith $"Error while decoding category (name:{name}, code:{code}): {err}"
72+
let unit =
73+
let name = get.Optional.Field "unit" Decode.string
74+
let code = get.Optional.Field "unitCode" Decode.string
75+
76+
match name, code with
77+
| None, None | None, Some "" -> None
78+
| _, None | _, Some "" ->
79+
try Some(OntologyAnnotation.create(?name = name))
80+
with
81+
| err ->
82+
failwith $"Error while decoding unit (name:{name}): {err}"
83+
| _, Some code ->
84+
try Some(OntologyAnnotation.fromTermAnnotation(code, ?name = name))
85+
with
86+
| err ->
87+
failwith $"Error while decoding unit (name:{name}, code:{code}): {err}"
88+
89+
let value =
90+
let value = get.Optional.Field "value" AnnotationValue.decoder
91+
let code = get.Optional.Field "valueCode" Decode.string
92+
if value.IsNone && code.IsNone then None
93+
else
94+
try Value.fromOptions value None code
95+
with
96+
| err ->
97+
failwith $"Error while decoding value {value},{code}: {err}"
98+
99+
create category value unit
100+
77101
)

tests/Core/ArcJsonConversion.Tests.fs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ let private tests_ArcTableProcess =
302302
let table = ArcTable.fromProcesses tableName1 processes
303303
Expect.arcTableEqual table t "Table should be equal"
304304
)
305-
ftestCase "ParamValueUnitizedNoValue" (fun () ->
305+
ptestCase "ParamValueUnitizedNoValueNoInputOutput" (fun () ->
306306
let t = ArcTable.init tableName1
307307
let header = CompositeHeader.Parameter oa_temperature
308308
let unit = oa_degreeCel
@@ -321,13 +321,38 @@ let private tests_ArcTableProcess =
321321
Expect.equal resultUnit unit "Unit should match"
322322

323323
let t' = ArcTable.fromProcesses tableName1 processes
324-
Expect.arcTableEqual t t' "Table should be equal"
324+
Expect.arcTableEqual t' t "Table should be equal"
325325
)
326-
ftestCase "ParamValueUnitizedEmpty" (fun () ->
326+
testCase "ParamValueUnitizedNoValue" (fun () ->
327+
let t = ArcTable.init tableName1
328+
let header = CompositeHeader.Parameter oa_temperature
329+
let unit = oa_degreeCel
330+
let cell = CompositeCell.createUnitized ("",unit)
331+
t.AddColumn(CompositeHeader.Input(IOType.Source),[|CompositeCell.createFreeText "Source"|])
332+
t.AddColumn(header,[|cell|])
333+
t.AddColumn(CompositeHeader.Output(IOType.Sample),[|CompositeCell.createFreeText "Sample"|])
334+
335+
let processes = t.GetProcesses()
336+
Expect.equal 1 processes.Length "Should have 1 process"
337+
let p = processes.[0]
338+
Expect.equal 1 p.ParameterValues.Value.Length "Should have 1 parameter value"
339+
let pv = p.ParameterValues.Value.[0]
340+
Expect.isNone pv.Value "Should have no value"
341+
let resultCategory = Expect.wantSome pv.Category "Should have category"
342+
Expect.equal resultCategory (ProtocolParameter.create(ParameterName = oa_temperature)) "Category should match"
343+
let resultUnit = Expect.wantSome pv.Unit "Should have unit"
344+
Expect.equal resultUnit unit "Unit should match"
345+
346+
let t' = ArcTable.fromProcesses tableName1 processes
347+
Expect.arcTableEqual t' t "Table should be equal"
348+
)
349+
ptestCase "ParamValueUnitizedEmpty" (fun () ->
327350
let t = ArcTable.init tableName1
328351
let header = CompositeHeader.Parameter oa_temperature
329352
let cell = CompositeCell.createUnitized ("")
353+
t.AddColumn(CompositeHeader.Input(IOType.Source),[|CompositeCell.createFreeText "Source"|])
330354
t.AddColumn(header,[|cell|])
355+
t.AddColumn(CompositeHeader.Output(IOType.Sample),[|CompositeCell.createFreeText "Sample"|])
331356

332357
let processes = t.GetProcesses()
333358
Expect.equal 1 processes.Length "Should have 1 process"
@@ -340,13 +365,15 @@ let private tests_ArcTableProcess =
340365
Expect.isNone pv.Unit "Should have no unit"
341366

342367
let t' = ArcTable.fromProcesses tableName1 processes
343-
Expect.arcTableEqual t t' "Table should be equal"
368+
Expect.arcTableEqual t' t "Table should be equal"
344369
)
345-
ftestCase "ParamValueUnitizedNoUnit" (fun () ->
370+
testCase "ParamValueUnitizedNoUnit" (fun () ->
346371
let t = ArcTable.init tableName1
347372
let header = CompositeHeader.Parameter oa_temperature
348373
let cell = CompositeCell.createUnitized ("5")
374+
t.AddColumn(CompositeHeader.Input(IOType.Source),[|CompositeCell.createFreeText "Source"|])
349375
t.AddColumn(header,[|cell|])
376+
t.AddColumn(CompositeHeader.Output(IOType.Sample),[|CompositeCell.createFreeText "Sample"|])
350377

351378
let processes = t.GetProcesses()
352379
Expect.equal 1 processes.Length "Should have 1 process"
@@ -360,7 +387,28 @@ let private tests_ArcTableProcess =
360387
Expect.isNone pv.Unit "Should have no unit"
361388

362389
let t' = ArcTable.fromProcesses tableName1 processes
363-
Expect.arcTableEqual t t' "Table should be equal"
390+
Expect.arcTableEqual t' t "Table should be equal"
391+
)
392+
testCase "ParamValueTermEmpty" (fun () ->
393+
let t = ArcTable.init tableName1
394+
let header = CompositeHeader.Parameter oa_species
395+
let cell = CompositeCell.createTerm (OntologyAnnotation.create())
396+
t.AddColumn(CompositeHeader.Input(IOType.Source),[|CompositeCell.createFreeText "Source"|])
397+
t.AddColumn(header,[|cell|])
398+
t.AddColumn(CompositeHeader.Output(IOType.Sample),[|CompositeCell.createFreeText "Sample"|])
399+
400+
let processes = t.GetProcesses()
401+
Expect.equal 1 processes.Length "Should have 1 process"
402+
let p = processes.[0]
403+
Expect.equal 1 p.ParameterValues.Value.Length "Should have 1 parameter value"
404+
let pv = p.ParameterValues.Value.[0]
405+
Expect.isNone pv.Value "Should have no value"
406+
let resultCategory = Expect.wantSome pv.Category "Should have category"
407+
Expect.equal resultCategory (ProtocolParameter.create(ParameterName = oa_species)) "Category should match"
408+
Expect.isNone pv.Unit "Should have no unit"
409+
410+
let t' = ArcTable.fromProcesses tableName1 processes
411+
Expect.arcTableEqual t' t "Table should be equal"
364412
)
365413
]
366414

tests/Json/Process/ProcessParameterValue.fs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ open TestObjects.Json
88

99
let private tests_roCrate =
1010
testList "RO-Crate" [
11-
ftestCase "ReadWriteIntegerValue" (fun () ->
11+
testCase "ReadWriteIntegerValue" (fun () ->
1212
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("temperature","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
1313
let value = Value.Int 25
1414
let unit = OntologyAnnotation("degree Celsius","UO","http://purl.obolibrary.org/obo/UO_0000185")
@@ -19,7 +19,29 @@ let private tests_roCrate =
1919

2020
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
2121
)
22-
ftestCase "ReadWriteUnitNoValue" (fun () ->
22+
testCase "ReadWriteUnitEmptyUnit" (fun () ->
23+
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("temperature","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
24+
let unit = OntologyAnnotation("","","")
25+
let value = Value.Int 25
26+
let ppv = ProcessParameterValue.create(pp,value,unit)
27+
28+
let roCrate = ProcessParameterValue.toROCrateJsonString() ppv
29+
let ppv2 = ProcessParameterValue.fromROCrateJsonString roCrate
30+
31+
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
32+
)
33+
testCase "ReadWriteUnitEmptyValue" (fun () ->
34+
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("temperature","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
35+
let unit = OntologyAnnotation("degree Celsius","UO","http://purl.obolibrary.org/obo/UO_0000185")
36+
let value = Value.Name ""
37+
let ppv = ProcessParameterValue.create(pp,value,unit)
38+
39+
let roCrate = ProcessParameterValue.toROCrateJsonString() ppv
40+
let ppv2 = ProcessParameterValue.fromROCrateJsonString roCrate
41+
42+
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
43+
)
44+
testCase "ReadWriteUnitNoValue" (fun () ->
2345
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("temperature","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
2446
let unit = OntologyAnnotation("degree Celsius","UO","http://purl.obolibrary.org/obo/UO_0000185")
2547
let ppv = ProcessParameterValue.create(pp,Unit = unit)
@@ -29,7 +51,7 @@ let private tests_roCrate =
2951

3052
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
3153
)
32-
ftestCase "ReadWriteNoUnitNoValue" (fun () ->
54+
testCase "ReadWriteNoUnitNoValue" (fun () ->
3355
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("temperature","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
3456
let ppv = ProcessParameterValue.create(pp)
3557

@@ -38,7 +60,7 @@ let private tests_roCrate =
3860

3961
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
4062
)
41-
ftestCase "ReadWriteOntologyValue" (fun () ->
63+
testCase "ReadWriteOntologyValue" (fun () ->
4264
let pp = ProtocolParameter.create(ParameterName = OntologyAnnotation("organism","NCIT","http://purl.obolibrary.org/obo/NCIT_0000029"))
4365
let value = Value.Ontology (OntologyAnnotation(
4466
"chlamydomonas reinhardtii",
@@ -52,7 +74,7 @@ let private tests_roCrate =
5274

5375
Expect.equal ppv ppv2 "RO-Crate roundtrip failed"
5476
)
55-
ftestCase "ReadWriteEmpty" (fun () ->
77+
testCase "ReadWriteEmpty" (fun () ->
5678
let ppv = ProcessParameterValue.create()
5779

5880
let roCrate = ProcessParameterValue.toROCrateJsonString() ppv

0 commit comments

Comments
 (0)