From 03e97843a39400c5fd33f63fadfc37aa27d32e3a Mon Sep 17 00:00:00 2001 From: Kevin F Date: Wed, 6 Sep 2023 16:12:59 +0200 Subject: [PATCH 01/17] Update ArcTypes to ref parent :construction: --- src/ISA/ISA/ARCtrl.ISA.fsproj | 4 +- src/ISA/ISA/ArcTypes/ArcAssay.fs | 444 ------- src/ISA/ISA/ArcTypes/ArcInvestigation.fs | 323 ----- src/ISA/ISA/ArcTypes/ArcStudy.fs | 561 --------- src/ISA/ISA/ArcTypes/ArcTypes.fs | 1403 ++++++++++++++++++++++ 5 files changed, 1404 insertions(+), 1331 deletions(-) delete mode 100644 src/ISA/ISA/ArcTypes/ArcAssay.fs delete mode 100644 src/ISA/ISA/ArcTypes/ArcInvestigation.fs delete mode 100644 src/ISA/ISA/ArcTypes/ArcStudy.fs create mode 100644 src/ISA/ISA/ArcTypes/ArcTypes.fs diff --git a/src/ISA/ISA/ARCtrl.ISA.fsproj b/src/ISA/ISA/ARCtrl.ISA.fsproj index b64060b1..6362514d 100644 --- a/src/ISA/ISA/ARCtrl.ISA.fsproj +++ b/src/ISA/ISA/ARCtrl.ISA.fsproj @@ -54,9 +54,7 @@ - - - + diff --git a/src/ISA/ISA/ArcTypes/ArcAssay.fs b/src/ISA/ISA/ArcTypes/ArcAssay.fs deleted file mode 100644 index 85dcfb13..00000000 --- a/src/ISA/ISA/ArcTypes/ArcAssay.fs +++ /dev/null @@ -1,444 +0,0 @@ -namespace ARCtrl.ISA - -open Fable.Core -open ARCtrl.ISA.Aux - -// "MyAssay"; "assays/MyAssay/isa.assay.xlsx" - -[] -type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray, ?performers : Person [], ?comments : Comment []) = - let tables = defaultArg tables <| ResizeArray() - let performers = defaultArg performers [||] - let comments = defaultArg comments [||] - let mutable identifier : string = identifier - - /// Must be unique in one study - member this.Identifier - with get() = identifier - and internal set(i) = identifier <- i - - static member FileName = ARCtrl.Path.AssayFileName - - member val MeasurementType : OntologyAnnotation option = measurementType with get, set - member val TechnologyType : OntologyAnnotation option = technologyType with get, set - member val TechnologyPlatform : OntologyAnnotation option = technologyPlatform with get, set - member val Tables : ResizeArray = tables with get, set - member val Performers : Person [] = performers with get, set - member val Comments : Comment [] = comments with get, set - - static member init (identifier : string) = ArcAssay(identifier) - static member create (identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray, ?performers : Person [], ?comments : Comment []) = - ArcAssay(identifier = identifier, ?measurementType = measurementType, ?technologyType = technologyType, ?technologyPlatform = technologyPlatform, ?tables =tables, ?performers = performers, ?comments = comments) - - static member make - (identifier : string) - (measurementType : OntologyAnnotation option) - (technologyType : OntologyAnnotation option) - (technologyPlatform : OntologyAnnotation option) - (tables : ResizeArray) - (performers : Person []) - (comments : Comment []) = - ArcAssay(identifier = identifier, ?measurementType = measurementType, ?technologyType = technologyType, ?technologyPlatform = technologyPlatform, tables =tables, performers = performers, comments = comments) - - member this.TableCount - with get() = ArcTables(this.Tables).Count - - member this.TableNames - with get() = ArcTables(this.Tables).TableNames - - // - Table API - // - // remark should this return ArcTable? - member this.AddTable(table:ArcTable, ?index: int) : unit = ArcTables(this.Tables).AddTable(table, ?index = index) - - static member addTable(table:ArcTable, ?index: int) = - fun (assay:ArcAssay) -> - let c = assay.Copy() - c.AddTable(table, ?index = index) - c - - // - Table API - // - member this.AddTables(tables:seq, ?index: int) = ArcTables(this.Tables).AddTables(tables, ?index = index) - - static member addTables(tables:seq, ?index: int) = - fun (assay:ArcAssay) -> - let c = assay.Copy() - c.AddTables(tables, ?index = index) - c - - // - Table API - // - member this.InitTable(tableName:string, ?index: int) = ArcTables(this.Tables).InitTable(tableName, ?index = index) - - static member initTable(tableName: string, ?index: int) = - fun (assay:ArcAssay) -> - let c = assay.Copy() - c.InitTable(tableName, ?index=index) - - - // - Table API - // - member this.InitTables(tableNames:seq, ?index: int) = ArcTables(this.Tables).InitTables(tableNames, ?index = index) - - static member initTables(tableNames:seq, ?index: int) = - fun (assay:ArcAssay) -> - let c = assay.Copy() - c.InitTables(tableNames, ?index=index) - c - - // - Table API - // - member this.GetTableAt(index:int) : ArcTable = ArcTables(this.Tables).GetTableAt(index) - - /// Receive **copy** of table at `index` - static member getTableAt(index:int) : ArcAssay -> ArcTable = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetTableAt(index) - - // - Table API - // - member this.GetTable(name: string) : ArcTable = ArcTables(this.Tables).GetTable(name) - - /// Receive **copy** of table with `name` = `ArcTable.Name` - static member getTable(name: string) : ArcAssay -> ArcTable = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetTable(name) - - // - Table API - // - member this.UpdateTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).UpdateTableAt(index, table) - - static member updateTableAt(index:int, table:ArcTable) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateTableAt(index, table) - newAssay - - // - Table API - // - member this.UpdateTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).UpdateTable(name, table) - - static member updateTable(name: string, table:ArcTable) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateTable(name, table) - newAssay - - // - Table API - // - member this.SetTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).SetTableAt(index, table) - - static member setTableAt(index:int, table:ArcTable) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.SetTableAt(index, table) - newAssay - - // - Table API - // - member this.SetTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).SetTable(name, table) - - static member setTable(name: string, table:ArcTable) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.SetTable(name, table) - newAssay - - // - Table API - // - member this.RemoveTableAt(index:int) : unit = ArcTables(this.Tables).RemoveTableAt(index) - - static member removeTableAt(index:int) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveTableAt(index) - newAssay - - // - Table API - // - member this.RemoveTable(name: string) : unit = ArcTables(this.Tables).RemoveTable(name) - - static member removeTable(name: string) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveTable(name) - newAssay - - // - Table API - // - // Remark: This must stay `ArcTable -> unit` so name cannot be changed here. - member this.MapTableAt(index: int, updateFun: ArcTable -> unit) = ArcTables(this.Tables).MapTableAt(index, updateFun) - - static member mapTableAt(index:int, updateFun: ArcTable -> unit) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.MapTableAt(index, updateFun) - newAssay - - // - Table API - // - member this.MapTable(name: string, updateFun: ArcTable -> unit) : unit = ArcTables(this.Tables).MapTable(name, updateFun) - - static member updateTable(name: string, updateFun: ArcTable -> unit) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.MapTable(name, updateFun) - newAssay - - // - Table API - // - member this.RenameTableAt(index: int, newName: string) : unit = ArcTables(this.Tables).RenameTableAt(index, newName) - - static member renameTableAt(index: int, newName: string) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RenameTableAt(index, newName) - newAssay - - // - Table API - // - member this.RenameTable(name: string, newName: string) : unit = ArcTables(this.Tables).RenameTable(name, newName) - - static member renameTable(name: string, newName: string) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RenameTable(name, newName) - newAssay - - // - Column CRUD API - // - member this.AddColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = - ArcTables(this.Tables).AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) - - static member addColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcAssay -> ArcAssay = - fun (assay: ArcAssay) -> - let newAssay = assay.Copy() - newAssay.AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) - newAssay - - // - Column CRUD API - // - member this.AddColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = - ArcTables(this.Tables).AddColumn(tableName, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) - - static member addColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.AddColumn(tableName, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) - newAssay - - // - Column CRUD API - // - member this.RemoveColumnAt(tableIndex: int, columnIndex: int) = - ArcTables(this.Tables).RemoveColumnAt(tableIndex, columnIndex) - - static member removeColumnAt(tableIndex: int, columnIndex: int) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveColumnAt(tableIndex, columnIndex) - newAssay - - // - Column CRUD API - // - member this.RemoveColumn(tableName: string, columnIndex: int) : unit = - ArcTables(this.Tables).RemoveColumn(tableName, columnIndex) - - static member removeColumn(tableName: string, columnIndex: int) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveColumn(tableName, columnIndex) - newAssay - - // - Column CRUD API - // - member this.UpdateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - ArcTables(this.Tables).UpdateColumnAt(tableIndex, columnIndex, header, ?cells = cells) - - static member updateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateColumnAt(tableIndex, columnIndex, header, ?cells=cells) - newAssay - - // - Column CRUD API - // - member this.UpdateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - ArcTables(this.Tables).UpdateColumn(tableName, columnIndex, header, ?cells=cells) - - static member updateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateColumn(tableName, columnIndex, header, ?cells=cells) - newAssay - - // - Column CRUD API - // - member this.GetColumnAt(tableIndex: int, columnIndex: int) = - ArcTables(this.Tables).GetColumnAt(tableIndex, columnIndex) - - static member getColumnAt(tableIndex: int, columnIndex: int) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetColumnAt(tableIndex, columnIndex) - - // - Column CRUD API - // - member this.GetColumn(tableName: string, columnIndex: int) = - ArcTables(this.Tables).GetColumn(tableName, columnIndex) - - static member getColumn(tableName: string, columnIndex: int) = - fun (assay: ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetColumn(tableName, columnIndex) - - // - Row CRUD API - // - member this.AddRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) = - ArcTables(this.Tables).AddRowAt(tableIndex, ?cells=cells, ?rowIndex = rowIndex) - - static member addRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) : ArcAssay -> ArcAssay = - fun (assay: ArcAssay) -> - let newAssay = assay.Copy() - newAssay.AddRowAt(tableIndex, ?cells=cells, ?rowIndex=rowIndex) - newAssay - - // - Row CRUD API - // - member this.AddRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) = - ArcTables(this.Tables).AddRow(tableName, ?cells=cells, ?rowIndex = rowIndex) - - static member addRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.AddRow(tableName, ?cells=cells, ?rowIndex=rowIndex) - newAssay - - // - Row CRUD API - // - member this.RemoveRowAt(tableIndex: int, rowIndex: int) = - ArcTables(this.Tables).RemoveRowAt(tableIndex, rowIndex) - - static member removeRowAt(tableIndex: int, rowIndex: int) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveColumnAt(tableIndex, rowIndex) - newAssay - - // - Row CRUD API - // - member this.RemoveRow(tableName: string, rowIndex: int) : unit = - ArcTables(this.Tables).RemoveRow(tableName, rowIndex) - - static member removeRow(tableName: string, rowIndex: int) : ArcAssay -> ArcAssay = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.RemoveRow(tableName, rowIndex) - newAssay - - // - Row CRUD API - // - member this.UpdateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = - ArcTables(this.Tables).UpdateRowAt(tableIndex, rowIndex, cells) - - static member updateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateRowAt(tableIndex, rowIndex, cells) - newAssay - - // - Row CRUD API - // - member this.UpdateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = - ArcTables(this.Tables).UpdateRow(tableName, rowIndex, cells) - - static member updateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.UpdateRow(tableName, rowIndex, cells) - newAssay - - // - Row CRUD API - // - member this.GetRowAt(tableIndex: int, rowIndex: int) = - ArcTables(this.Tables).GetRowAt(tableIndex, rowIndex) - - static member getRowAt(tableIndex: int, rowIndex: int) = - fun (assay:ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetRowAt(tableIndex, rowIndex) - - // - Row CRUD API - // - member this.GetRow(tableName: string, rowIndex: int) = - ArcTables(this.Tables).GetRow(tableName, rowIndex) - - static member getRow(tableName: string, rowIndex: int) = - fun (assay: ArcAssay) -> - let newAssay = assay.Copy() - newAssay.GetRow(tableName, rowIndex) - - // - Mutable properties API - // - static member setPerformers performers (assay: ArcAssay) = - assay.Performers <- performers - assay - - member this.Copy() : ArcAssay = - let nextTables = ResizeArray() - for table in this.Tables do - let copy = table.Copy() - nextTables.Add(copy) - let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) - let nextPerformers = this.Performers |> Array.map (fun c -> c.Copy()) - ArcAssay( - this.Identifier, - ?measurementType = this.MeasurementType, - ?technologyType = this.TechnologyType, - ?technologyPlatform = this.TechnologyPlatform, - tables=nextTables, - performers=nextPerformers, - comments=nextComments - ) - - /// 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 = """(?[^\(]+) \((?[^(]*:[^)]*)\)""" - - let r = System.Text.RegularExpressions.Regex.Match(name,pattern) - - - if r.Success then - let oa = (r.Groups.Item "ontology").Value |> OntologyAnnotation.fromTermAnnotation - let v = (r.Groups.Item "value").Value |> Value.fromString - {oa with Name = (Some (AnnotationValue.Text v.Text))} - else - OntologyAnnotation.fromString(termName = name) - - /// Transform an ArcAssay to an ISA Json Assay. - member this.ToAssay() : Assay = - let processSeq = ArcTables(this.Tables).GetProcesses() - let assayMaterials = - AssayMaterials.create( - ?Samples = (ProcessSequence.getSamples processSeq |> Option.fromValueWithDefault []), - ?OtherMaterials = (ProcessSequence.getMaterials processSeq |> Option.fromValueWithDefault []) - ) - |> Option.fromValueWithDefault AssayMaterials.empty - let fileName = - if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then - None - else - Some (ARCtrl.ISA.Identifier.Assay.fileNameFromIdentifier this.Identifier) - Assay.create( - ?FileName = fileName, - ?MeasurementType = this.MeasurementType, - ?TechnologyType = this.TechnologyType, - ?TechnologyPlatform = (this.TechnologyPlatform |> Option.map ArcAssay.composeTechnologyPlatform), - ?DataFiles = (ProcessSequence.getData processSeq |> Option.fromValueWithDefault []), - ?Materials = assayMaterials, - ?CharacteristicCategories = (ProcessSequence.getCharacteristics processSeq |> Option.fromValueWithDefault []), - ?UnitCategories = (ProcessSequence.getUnits processSeq |> Option.fromValueWithDefault []), - ?ProcessSequence = (processSeq |> Option.fromValueWithDefault []), - ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) - ) - - // Create an ArcAssay from an ISA Json Assay. - static member fromAssay (a : Assay) : ArcAssay = - let tables = (a.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) - let identifer = - match a.FileName with - | Some fn -> Identifier.Assay.identifierFromFileName fn - | None -> Identifier.createMissingIdentifier() - ArcAssay.create( - identifer, - ?measurementType = (a.MeasurementType |> Option.map (fun x -> x.Copy())), - ?technologyType = (a.TechnologyType |> Option.map (fun x -> x.Copy())), - ?technologyPlatform = (a.TechnologyPlatform |> Option.map ArcAssay.decomposeTechnologyPlatform), - ?tables = tables, - ?comments = (a.Comments |> Option.map Array.ofList) - ) \ No newline at end of file diff --git a/src/ISA/ISA/ArcTypes/ArcInvestigation.fs b/src/ISA/ISA/ArcTypes/ArcInvestigation.fs deleted file mode 100644 index 37793700..00000000 --- a/src/ISA/ISA/ArcTypes/ArcInvestigation.fs +++ /dev/null @@ -1,323 +0,0 @@ -namespace ARCtrl.ISA - -open Fable.Core -open ARCtrl.ISA.Aux - -module ArcInvestigationAux = - module SanityChecks = - let inline validateUniqueStudyIdentifier (study: ArcStudy) (existingStudies: seq) = - match existingStudies |> Seq.tryFindIndex (fun x -> x.Identifier = study.Identifier) with - | Some i -> - failwith $"Cannot create study with name {study.Identifier}, as study names must be unique and study at index {i} has the same name." - | None -> - () - -[] -type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = - - let ontologySourceReferences = defaultArg ontologySourceReferences [||] - let publications = defaultArg publications [||] - let contacts = defaultArg contacts [||] - let studies = defaultArg studies (ResizeArray()) - let comments = defaultArg comments [||] - let remarks = defaultArg remarks [||] - - let mutable identifier = identifier - /// Must be unique in one investigation - member this.Identifier - with get() = identifier - and internal set(i) = identifier <- i - - member val Title : string option = title with get, set - member val Description : string option = description with get, set - member val SubmissionDate : string option = submissionDate with get, set - member val PublicReleaseDate : string option = publicReleaseDate with get, set - member val OntologySourceReferences : OntologySourceReference [] = ontologySourceReferences with get, set - member val Publications : Publication [] = publications with get, set - member val Contacts : Person [] = contacts with get, set - member val Studies : ResizeArray = studies with get, set - member val Comments : Comment [] = comments with get, set - member val Remarks : Remark [] = remarks with get, set - - static member FileName = ARCtrl.Path.InvestigationFileName - - static member init(identifier: string) = ArcInvestigation identifier - static member create(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = - ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?ontologySourceReferences = ontologySourceReferences, ?publications = publications, ?contacts = contacts, ?studies = studies, ?comments = comments, ?remarks = remarks) - - static member make (identifier : string) (title : string option) (description : string option) (submissionDate : string option) (publicReleaseDate : string option) (ontologySourceReferences : OntologySourceReference []) (publications : Publication []) (contacts : Person []) (studies : ResizeArray) (comments : Comment []) (remarks : Remark []) : ArcInvestigation = - ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, studies = studies, comments = comments, remarks = remarks) - - member this.StudyCount - with get() = this.Studies.Count - - member this.StudyIdentifiers - with get() = this.Studies |> Seq.map (fun (x:ArcStudy) -> x.Identifier) - - // - Study API - CRUD // - member this.AddStudy(study: ArcStudy) = - ArcInvestigationAux.SanityChecks.validateUniqueStudyIdentifier study this.Studies - this.Studies.Add(study) - - static member addStudy(study: ArcStudy) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.AddStudy(study) - copy - - // - Study API - CRUD // - member this.InitStudy (studyName: string) = - let study = ArcStudy.init(studyName) - this.AddStudy(study) - study - - static member initStudy(studyName: string) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.InitStudy(studyName) - - - // - Study API - CRUD // - member this.RemoveStudyAt(index: int) = - this.Studies.RemoveAt(index) - - static member removeStudyAt(index: int) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.RemoveStudyAt(index) - newInv - - // - Study API - CRUD // - member this.RemoveStudy(studyIdentifier: string) = - this.GetStudy(studyIdentifier) - |> this.Studies.Remove - |> ignore - - static member removeStudy(studyIdentifier: string) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.RemoveStudy(studyIdentifier) - copy - - // - Study API - CRUD // - member this.SetStudyAt(index: int, study: ArcStudy) = - ArcInvestigationAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) - this.Studies.[index] <- study - - static member setStudyAt(index: int, study: ArcStudy) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.SetStudyAt(index, study) - newInv - - // - Study API - CRUD // - member this.SetStudy(studyIdentifier: string, study: ArcStudy) = - let index = this.GetStudyIndex studyIdentifier - ArcInvestigationAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) - this.Studies.[index] <- study - - static member setStudy(studyIdentifier: string, study: ArcStudy) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.SetStudy(studyIdentifier, study) - newInv - - // - Study API - CRUD // - member this.GetStudyIndex(studyIdentifier: string) : int = - let index = this.Studies.FindIndex (fun s -> s.Identifier = studyIdentifier) - if index = -1 then failwith $"Unable to find study with specified identifier '{studyIdentifier}'!" - index - - // - Study API - CRUD // - static member getStudyIndex(studyIdentifier: string) : ArcInvestigation -> int = - fun (inv: ArcInvestigation) -> inv.GetStudyIndex (studyIdentifier) - - // - Study API - CRUD // - member this.GetStudyAt(index: int) : ArcStudy = - this.Studies.[index] - - static member getStudyAt(index: int) : ArcInvestigation -> ArcStudy = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.GetStudyAt(index) - - // - Study API - CRUD // - member this.GetStudy(studyIdentifier: string) : ArcStudy = - this.Studies.Find (fun s -> s.Identifier = studyIdentifier) - - static member getStudy(studyIdentifier: string) : ArcInvestigation -> ArcStudy = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.GetStudy(studyIdentifier) - - // - Study API - CRUD // - member this.AddAssay(studyIdentifier: string, assay: ArcAssay) = - let study = this.GetStudy(studyIdentifier) - ArcStudyAux.SanityChecks.validateUniqueAssayIdentifier assay study.Assays - study.AddAssay(assay) - - static member addAssay(studyIdentifier: string, assay: ArcAssay) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.AddAssay(studyIdentifier, assay) - copy - - // - Study API - CRUD // - member this.AddAssayAt(studyIndex: int, assay: ArcAssay) = - let study = this.GetStudyAt(studyIndex) - ArcStudyAux.SanityChecks.validateUniqueAssayIdentifier assay study.Assays - study.AddAssay(assay) - - static member addAssayAt(studyIndex: int, assay: ArcAssay) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.AddAssayAt(studyIndex, assay) - copy - - // - Study API - CRUD // - member this.InitAssay(studyIdentifier: string, assayName: string) = - let assay = ArcAssay.init(assayName) - this.AddAssay(studyIdentifier, assay) - assay - - static member initAssay(studyIdentifier: string, assayName: string) = - fun (inv: ArcInvestigation) -> - let copy = inv.Copy() - copy.InitAssay(studyIdentifier, assayName) - - // - Study API - CRUD // - member this.RemoveAssayAt(studyIdentifier: string, index: int) = - let study = this.GetStudy(studyIdentifier) - study.Assays.RemoveAt(index) - - static member removeAssayAt(studyIdentifier: string, index: int) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.RemoveAssayAt(studyIdentifier, index) - newInv - - // - Study API - CRUD // - member this.SetAssayAt(studyIdentifier: string, index: int, assay: ArcAssay) = - let study = this.GetStudy(studyIdentifier) - study.SetAssayAt(index, assay) - this.Studies.[index] <- study - - static member setAssayAt(studyIdentifier: string, index: int, assay: ArcAssay) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.SetAssayAt(studyIdentifier, index, assay) - newInv - - // - Study API - CRUD // - member this.SetAssay(studyIdentifier: string, assayIdentifier: string, assay: ArcAssay) = - let study = this.GetStudy(studyIdentifier) - let index = study.GetAssayIndex assayIdentifier - study.SetAssayAt (index, assay) - - static member setAssay(studyIdentifier: string, assayIdentifier: string, assay: ArcAssay) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.SetAssay(studyIdentifier, assayIdentifier, assay) - - // - Study API - CRUD // - member this.GetAssayAt(studyIdentifier: string, index: int) : ArcAssay = - let study = this.GetStudy(studyIdentifier) - study.GetAssayAt index - - static member getAssayAt(studyIdentifier: string, index: int) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.GetAssayAt(studyIdentifier, index) - - // - Study API - CRUD // - member this.GetAssay(studyIdentifier: string, assayIdentifier: string) : ArcAssay = - let study = this.GetStudy(studyIdentifier) - let index = study.GetAssayIndex assayIdentifier - study.GetAssayAt index - - static member getAssay(studyIdentifier: string, assayIdentifier: string) = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.GetAssay(studyIdentifier, assayIdentifier) - - member this.TryFindAssay(assayIdentifier: string) : ArcAssay option = - let assays = this.Studies |> Seq.collect (fun s -> s.Assays) |> Array.ofSeq - assays |> Array.tryFind (fun a -> a.Identifier = assayIdentifier) - - member this.FindAssay(assayIdentifier: string) : ArcAssay = - match this.TryFindAssay(assayIdentifier) with - | Some a -> a - | None -> failwith $"Unable to find assay with identifier '{assayIdentifier}'." - - static member tryFindAssay(assayIdentifier: string) : ArcInvestigation -> ArcAssay option = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.TryFindAssay(assayIdentifier) - - static member findAssay(assayIdentifier: string) : ArcInvestigation -> ArcAssay = - fun (inv: ArcInvestigation) -> - let newInv = inv.Copy() - newInv.FindAssay(assayIdentifier) - - member this.Copy() : ArcInvestigation = - let nextStudies = ResizeArray() - for study in this.Studies do - let copy = study.Copy() - nextStudies.Add(copy) - let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) - let nextRemarks = this.Remarks |> Array.map (fun c -> c.Copy()) - let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) - let nextPublications = this.Publications |> Array.map (fun c -> c.Copy()) - let nextOntologySourceReferences = this.OntologySourceReferences |> Array.map (fun c -> c.Copy()) - ArcInvestigation( - this.Identifier, - ?title = this.Title, - ?description = this.Description, - ?submissionDate = this.SubmissionDate, - ?publicReleaseDate = this.PublicReleaseDate, - ontologySourceReferences = nextOntologySourceReferences, - publications = nextPublications, - contacts = nextContacts, - studies = nextStudies, // correct mutable behaviour is tested on this field - comments = nextComments, - remarks = nextRemarks - ) - - - /// Transform an ArcInvestigation to an ISA Json Investigation. - member this.ToInvestigation() : Investigation = - let studies = this.Studies |> Seq.toList |> List.map (fun a -> a.ToStudy()) |> Option.fromValueWithDefault [] - let identifier = - if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then None - else Some this.Identifier - Investigation.create( - FileName = ARCtrl.Path.InvestigationFileName, - ?Identifier = identifier, - ?Title = this.Title, - ?Description = this.Description, - ?SubmissionDate = this.SubmissionDate, - ?PublicReleaseDate = this.PublicReleaseDate, - ?Publications = (this.Publications |> List.ofArray |> Option.fromValueWithDefault []), - ?Contacts = (this.Contacts |> List.ofArray |> Option.fromValueWithDefault []), - ?Studies = studies, - ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) - ) - - // Create an ArcInvestigation from an ISA Json Investigation. - static member fromInvestigation (i : Investigation) : ArcInvestigation = - let identifer = - match i.Identifier with - | Some i -> i - | None -> Identifier.createMissingIdentifier() - let studies = i.Studies |> Option.map (List.map ArcStudy.fromStudy >> ResizeArray) - ArcInvestigation.create( - identifer, - ?title = i.Title, - ?description = i.Description, - ?submissionDate = i.SubmissionDate, - ?publicReleaseDate = i.PublicReleaseDate, - ?publications = (i.Publications |> Option.map Array.ofList), - ?contacts = (i.Contacts |> Option.map Array.ofList), - ?studies = studies, - ?comments = (i.Comments |> Option.map Array.ofList) - ) diff --git a/src/ISA/ISA/ArcTypes/ArcStudy.fs b/src/ISA/ISA/ArcTypes/ArcStudy.fs deleted file mode 100644 index 33b5d075..00000000 --- a/src/ISA/ISA/ArcTypes/ArcStudy.fs +++ /dev/null @@ -1,561 +0,0 @@ -namespace ARCtrl.ISA - -open Fable.Core -open ARCtrl.ISA.Aux - -module ArcStudyAux = - module SanityChecks = - let inline validateUniqueAssayIdentifier (assay: ArcAssay) (existingAssays: seq) = - match existingAssays |> Seq.tryFindIndex (fun x -> x.Identifier = assay.Identifier) with - | Some i -> - failwith $"Cannot create assay with name {assay.Identifier}, as assay names must be unique and assay at index {i} has the same name." - | None -> - () - -[] -type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays, ?factors, ?comments) = - let publications = defaultArg publications [||] - let contacts = defaultArg contacts [||] - let studyDesignDescriptors = defaultArg studyDesignDescriptors [||] - let tables = defaultArg tables <| ResizeArray() - let assays = defaultArg assays <| ResizeArray() - let factors = defaultArg factors [||] - let comments = defaultArg comments [||] - - let mutable identifier = identifier - /// Must be unique in one investigation - member this.Identifier - with get() = identifier - and internal set(i) = identifier <- i - - member val Title : string option = title with get, set - member val Description : string option = description with get, set - member val SubmissionDate : string option = submissionDate with get, set - member val PublicReleaseDate : string option = publicReleaseDate with get, set - member val Publications : Publication [] = publications with get, set - member val Contacts : Person [] = contacts with get, set - member val StudyDesignDescriptors : OntologyAnnotation [] = studyDesignDescriptors with get, set - member val Tables : ResizeArray = tables with get, set - member val Assays : ResizeArray = assays with get, set - member val Factors : Factor [] = factors with get, set - member val Comments : Comment []= comments with get, set - - static member init(identifier : string) = ArcStudy identifier - - static member create(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays, ?factors, ?comments) = - ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?publications = publications, ?contacts = contacts, ?studyDesignDescriptors = studyDesignDescriptors, ?tables = tables, ?assays = assays, ?factors = factors, ?comments = comments) - - static member make identifier title description submissionDate publicReleaseDate publications contacts studyDesignDescriptors tables assays factors comments = - ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, publications = publications, contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, assays = assays, factors = factors, comments = comments) - - /// - /// Returns true if all fields are None/ empty sequences **except** Identifier. - /// - member this.isEmpty - with get() = - (this.Title = None) && - (this.Description = None) && - (this.SubmissionDate = None) && - (this.PublicReleaseDate = None) && - (this.Publications = [||]) && - (this.Contacts = [||]) && - (this.StudyDesignDescriptors = [||]) && - (this.Tables.Count = 0) && - (this.Assays.Count = 0) && - (this.Factors = [||]) && - (this.Comments = [||]) - - // Not sure how to handle this best case. - static member FileName = ARCtrl.Path.StudyFileName - //member this.FileName = ArcStudy.FileName - - member this.AssayCount - with get() = this.Assays.Count - - member this.AssayIdentifiers - with get() = this.Assays |> Seq.map (fun (x:ArcAssay) -> x.Identifier) - - // - Assay API - CRUD // - member this.AddAssay(assay: ArcAssay) = - ArcStudyAux.SanityChecks.validateUniqueAssayIdentifier assay this.Assays - this.Assays.Add(assay) - - static member addAssay(assay: ArcAssay) = - fun (study:ArcStudy) -> - let newStudy = study.Copy() - newStudy.AddAssay(assay) - newStudy - - // - Assay API - CRUD // - member this.InitAssay(assayName: string) = - let assay = ArcAssay(assayName) - this.AddAssay(assay) - assay - - static member initAssay(assayName: string) = - fun (study:ArcStudy) -> - let newStudy = study.Copy() - newStudy.InitAssay(assayName) - - // - Assay API - CRUD // - member this.RemoveAssayAt(index: int) = - this.Assays.RemoveAt(index) - - static member removeAssayAt(index: int) = - fun (study: ArcStudy) -> - let newStudy = study.Copy() - newStudy.RemoveAssayAt(index) - newStudy - - // - Assay API - CRUD // - member this.SetAssayAt(index: int, assay: ArcAssay) = - ArcStudyAux.SanityChecks.validateUniqueAssayIdentifier assay (this.Assays |> Seq.removeAt index) - this.Assays.[index] <- assay - - static member setAssayAt(index: int, assay: ArcAssay) = - fun (study:ArcStudy) -> - let newStudy = study.Copy() - newStudy.SetAssayAt(index, assay) - newStudy - - // - Assay API - CRUD // - member this.SetAssay(assayIdentifier: string, assay: ArcAssay) = - let index = this.GetAssayIndex(assayIdentifier) - this.Assays.[index] <- assay - - static member setAssay(assayIdentifier: string, assay: ArcAssay) = - fun (study:ArcStudy) -> - let newStudy = study.Copy() - newStudy.SetAssay(assayIdentifier, assay) - newStudy - - // - Assay API - CRUD // - member this.GetAssayIndex(assayIdentifier: string) = - let index = this.Assays.FindIndex (fun a -> a.Identifier = assayIdentifier) - if index = -1 then failwith $"Unable to find assay with specified identifier '{assayIdentifier}'!" - index - - static member GetAssayIndex(assayIdentifier: string) : ArcStudy -> int = - fun (study: ArcStudy) -> study.GetAssayIndex(assayIdentifier) - - // - Assay API - CRUD // - member this.GetAssayAt(index: int) : ArcAssay = - this.Assays.[index] - - static member getAssayAt(index: int) : ArcStudy -> ArcAssay = - fun (study: ArcStudy) -> - let newStudy = study.Copy() - newStudy.GetAssayAt(index) - - // - Assay API - CRUD // - member this.GetAssay(assayIdentifier: string) : ArcAssay = - let index = this.GetAssayIndex(assayIdentifier) - this.GetAssayAt index - - static member getAssay(assayIdentifier: string) : ArcStudy -> ArcAssay = - fun (study: ArcStudy) -> - let newStudy = study.Copy() - newStudy.GetAssay(assayIdentifier) - - //////////////////////////////////// - // - Copy & Paste from ArcAssay - // - //////////////////////////////////// - - member this.TableCount - with get() = ArcTables(this.Tables).Count - - member this.TableNames - with get() = ArcTables(this.Tables).TableNames - - // - Table API - // - // remark should this return ArcTable? - member this.AddTable(table:ArcTable, ?index: int) = ArcTables(this.Tables).AddTable(table, ?index = index) - - static member addTable(table:ArcTable, ?index: int) = - fun (study:ArcStudy) -> - let c = study.Copy() - c.AddTable(table, ?index = index) - c - - // - Table API - // - member this.AddTables(tables:seq, ?index: int) = ArcTables(this.Tables).AddTables(tables, ?index = index) - - static member addTables(tables:seq, ?index: int) = - fun (study:ArcStudy) -> - let c = study.Copy() - c.AddTables(tables, ?index = index) - c - - // - Table API - // - member this.InitTable(tableName:string, ?index: int) = ArcTables(this.Tables).InitTable(tableName, ?index = index) - - static member initTable(tableName: string, ?index: int) = - fun (study:ArcStudy) -> - let c = study.Copy() - c.InitTable(tableName, ?index=index) - - - // - Table API - // - member this.InitTables(tableNames:seq, ?index: int) = ArcTables(this.Tables).InitTables(tableNames, ?index = index) - - static member initTables(tableNames:seq, ?index: int) = - fun (study:ArcStudy) -> - let c = study.Copy() - c.InitTables(tableNames, ?index=index) - c - - // - Table API - // - member this.GetTableAt(index:int) : ArcTable = ArcTables(this.Tables).GetTableAt(index) - - /// Receive **copy** of table at `index` - static member getTableAt(index:int) : ArcStudy -> ArcTable = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetTableAt(index) - - // - Table API - // - member this.GetTable(name: string) : ArcTable = ArcTables(this.Tables).GetTable(name) - - /// Receive **copy** of table with `name` = `ArcTable.Name` - static member getTable(name: string) : ArcStudy -> ArcTable = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetTable(name) - - // - Table API - // - member this.UpdateTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).UpdateTableAt(index, table) - - static member updateTableAt(index:int, table:ArcTable) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateTableAt(index, table) - newAssay - - // - Table API - // - member this.UpdateTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).UpdateTable(name, table) - - static member updateTable(name: string, table:ArcTable) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateTable(name, table) - newAssay - - - // - Table API - // - member this.SetTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).SetTableAt(index, table) - - static member setTableAt(index:int, table:ArcTable) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.SetTableAt(index, table) - newAssay - - // - Table API - // - member this.SetTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).SetTable(name, table) - - static member setTable(name: string, table:ArcTable) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.SetTable(name, table) - newAssay - - // - Table API - // - member this.RemoveTableAt(index:int) : unit = ArcTables(this.Tables).RemoveTableAt(index) - - static member removeTableAt(index:int) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveTableAt(index) - newAssay - - // - Table API - // - member this.RemoveTable(name: string) : unit = ArcTables(this.Tables).RemoveTable(name) - - static member removeTable(name: string) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveTable(name) - newAssay - - // - Table API - // - // Remark: This must stay `ArcTable -> unit` so name cannot be changed here. - member this.MapTableAt(index: int, updateFun: ArcTable -> unit) = ArcTables(this.Tables).MapTableAt(index, updateFun) - - static member mapTableAt(index:int, updateFun: ArcTable -> unit) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.MapTableAt(index, updateFun) - newAssay - - // - Table API - // - member this.MapTable(name: string, updateFun: ArcTable -> unit) : unit = ArcTables(this.Tables).MapTable(name, updateFun) - - static member mapTable(name: string, updateFun: ArcTable -> unit) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.MapTable(name, updateFun) - newAssay - - // - Table API - // - member this.RenameTableAt(index: int, newName: string) : unit = ArcTables(this.Tables).RenameTableAt(index, newName) - - static member renameTableAt(index: int, newName: string) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RenameTableAt(index, newName) - newAssay - - // - Table API - // - member this.RenameTable(name: string, newName: string) : unit = ArcTables(this.Tables).RenameTable(name, newName) - - static member renameTable(name: string, newName: string) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RenameTable(name, newName) - newAssay - - // - Column CRUD API - // - member this.AddColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = - ArcTables(this.Tables).AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) - - static member addColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcStudy -> ArcStudy = - fun (study: ArcStudy) -> - let newAssay = study.Copy() - newAssay.AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) - newAssay - - // - Column CRUD API - // - member this.AddColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = - ArcTables(this.Tables).AddColumn(tableName, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) - - static member addColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.AddColumn(tableName, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) - newAssay - - // - Column CRUD API - // - member this.RemoveColumnAt(tableIndex: int, columnIndex: int) = - ArcTables(this.Tables).RemoveColumnAt(tableIndex, columnIndex) - - static member removeColumnAt(tableIndex: int, columnIndex: int) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveColumnAt(tableIndex, columnIndex) - newAssay - - // - Column CRUD API - // - member this.RemoveColumn(tableName: string, columnIndex: int) : unit = - ArcTables(this.Tables).RemoveColumn(tableName, columnIndex) - - static member removeColumn(tableName: string, columnIndex: int) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveColumn(tableName, columnIndex) - newAssay - - // - Column CRUD API - // - member this.UpdateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - ArcTables(this.Tables).UpdateColumnAt(tableIndex, columnIndex, header, ?cells = cells) - - static member updateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateColumnAt(tableIndex, columnIndex, header, ?cells=cells) - newAssay - - // - Column CRUD API - // - member this.UpdateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - ArcTables(this.Tables).UpdateColumn(tableName, columnIndex, header, ?cells=cells) - - static member updateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateColumn(tableName, columnIndex, header, ?cells=cells) - newAssay - - // - Column CRUD API - // - member this.GetColumnAt(tableIndex: int, columnIndex: int) = - ArcTables(this.Tables).GetColumnAt(tableIndex, columnIndex) - - static member getColumnAt(tableIndex: int, columnIndex: int) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetColumnAt(tableIndex, columnIndex) - - // - Column CRUD API - // - member this.GetColumn(tableName: string, columnIndex: int) = - ArcTables(this.Tables).GetColumn(tableName, columnIndex) - - static member getColumn(tableName: string, columnIndex: int) = - fun (study: ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetColumn(tableName, columnIndex) - - // - Row CRUD API - // - member this.AddRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) = - ArcTables(this.Tables).AddRowAt(tableIndex, ?cells=cells, ?rowIndex = rowIndex) - - static member addRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) : ArcStudy -> ArcStudy = - fun (study: ArcStudy) -> - let newAssay = study.Copy() - newAssay.AddRowAt(tableIndex, ?cells=cells, ?rowIndex=rowIndex) - newAssay - - // - Row CRUD API - // - member this.AddRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) = - ArcTables(this.Tables).AddRow(tableName, ?cells=cells, ?rowIndex = rowIndex) - - static member addRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.AddRow(tableName, ?cells=cells, ?rowIndex=rowIndex) - newAssay - - // - Row CRUD API - // - member this.RemoveRowAt(tableIndex: int, rowIndex: int) = - ArcTables(this.Tables).RemoveRowAt(tableIndex, rowIndex) - - static member removeRowAt(tableIndex: int, rowIndex: int) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveColumnAt(tableIndex, rowIndex) - newAssay - - // - Row CRUD API - // - member this.RemoveRow(tableName: string, rowIndex: int) : unit = - ArcTables(this.Tables).RemoveRow(tableName, rowIndex) - - static member removeRow(tableName: string, rowIndex: int) : ArcStudy -> ArcStudy = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.RemoveRow(tableName, rowIndex) - newAssay - - // - Row CRUD API - // - member this.UpdateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = - ArcTables(this.Tables).UpdateRowAt(tableIndex, rowIndex, cells) - - static member updateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateRowAt(tableIndex, rowIndex, cells) - newAssay - - // - Row CRUD API - // - member this.UpdateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = - ArcTables(this.Tables).UpdateRow(tableName, rowIndex, cells) - - static member updateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.UpdateRow(tableName, rowIndex, cells) - newAssay - - // - Row CRUD API - // - member this.GetRowAt(tableIndex: int, rowIndex: int) = - ArcTables(this.Tables).GetRowAt(tableIndex, rowIndex) - - static member getRowAt(tableIndex: int, rowIndex: int) = - fun (study:ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetRowAt(tableIndex, rowIndex) - - // - Row CRUD API - // - member this.GetRow(tableName: string, rowIndex: int) = - ArcTables(this.Tables).GetRow(tableName, rowIndex) - - static member getRow(tableName: string, rowIndex: int) = - fun (study: ArcStudy) -> - let newAssay = study.Copy() - newAssay.GetRow(tableName, rowIndex) - - member this.Copy() : ArcStudy = - let nextTables = ResizeArray() - let nextAssays = ResizeArray() - for table in this.Tables do - let copy = table.Copy() - nextTables.Add(copy) - for study in this.Assays do - let copy = study.Copy() - nextAssays.Add(copy) - let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) - let nextFactors = this.Factors |> Array.map (fun c -> c.Copy()) - let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) - let nextPublications = this.Publications |> Array.map (fun c -> c.Copy()) - let nextStudyDesignDescriptors = this.StudyDesignDescriptors |> Array.map (fun c -> c.Copy()) - ArcStudy( - this.Identifier, - ?title = this.Title, - ?description = this.Description, - ?submissionDate = this.SubmissionDate, - ?publicReleaseDate = this.PublicReleaseDate, - publications = nextPublications, - contacts = nextContacts, - studyDesignDescriptors = nextStudyDesignDescriptors, - tables = nextTables, - assays = nextAssays, - factors = nextFactors, - comments = nextComments - ) - - /// Transform an ArcStudy to an ISA Json Study. - member this.ToStudy() : Study = - let processSeq = ArcTables(this.Tables).GetProcesses() - let protocols = ProcessSequence.getProtocols processSeq |> Option.fromValueWithDefault [] - let assays = this.Assays |> Seq.toList |> List.map (fun a -> a.ToAssay()) |> Option.fromValueWithDefault [] - let studyMaterials = - StudyMaterials.create( - ?Sources = (ProcessSequence.getSources processSeq |> Option.fromValueWithDefault []), - ?Samples = (ProcessSequence.getSamples processSeq |> Option.fromValueWithDefault []), - ?OtherMaterials = (ProcessSequence.getMaterials processSeq |> Option.fromValueWithDefault []) - ) - |> Option.fromValueWithDefault StudyMaterials.empty - let identifier,fileName = - if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then - None, None - else - Some this.Identifier, Some (Identifier.Study.fileNameFromIdentifier this.Identifier) - Study.create( - ?FileName = fileName, - ?Identifier = identifier, - ?Title = this.Title, - ?Description = this.Description, - ?SubmissionDate = this.SubmissionDate, - ?PublicReleaseDate = this.PublicReleaseDate, - ?Publications = (this.Publications |> List.ofArray |> Option.fromValueWithDefault []), - ?Contacts = (this.Contacts |> List.ofArray |> Option.fromValueWithDefault []), - ?StudyDesignDescriptors = (this.StudyDesignDescriptors |> List.ofArray |> Option.fromValueWithDefault []), - ?Protocols = protocols, - ?Materials = studyMaterials, - ?ProcessSequence = (processSeq |> Option.fromValueWithDefault []), - ?Assays = assays, - ?Factors = (this.Factors |> List.ofArray |> Option.fromValueWithDefault []), - ?CharacteristicCategories = (ProcessSequence.getCharacteristics processSeq |> Option.fromValueWithDefault []), - ?UnitCategories = (ProcessSequence.getUnits processSeq |> Option.fromValueWithDefault []), - ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) - ) - - // Create an ArcStudy from an ISA Json Study. - static member fromStudy (s : Study) : ArcStudy = - let tables = (s.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) - let identifer = - match s.FileName with - | Some fn -> Identifier.Study.identifierFromFileName fn - | None -> Identifier.createMissingIdentifier() - let assays = s.Assays |> Option.map (List.map ArcAssay.fromAssay >> ResizeArray) - ArcStudy.create( - identifer, - ?title = s.Title, - ?description = s.Description, - ?submissionDate = s.SubmissionDate, - ?publicReleaseDate = s.PublicReleaseDate, - ?publications = (s.Publications |> Option.map Array.ofList), - ?contacts = (s.Contacts|> Option.map Array.ofList), - ?studyDesignDescriptors = (s.StudyDesignDescriptors |> Option.map Array.ofList), - ?tables = tables, - ?assays = assays, - ?factors = (s.Factors |> Option.map Array.ofList), - ?comments = (s.Comments |> Option.map Array.ofList) - ) - diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs new file mode 100644 index 00000000..a34647b6 --- /dev/null +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -0,0 +1,1403 @@ +namespace rec ARCtrl.ISA + +open Fable.Core +open ARCtrl.ISA.Aux + +module ArcTypesAux = + module SanityChecks = + + let inline validateRegisteredInvestigation (investigation: ArcInvestigation option) = + match investigation with + | None -> failwith "Cannot execute this function. Object is not part of ArcInvestigation." + | Some i -> i + + let inline validateAssayRegister (assayIdent: string) (existingAssayIdents: seq) = + match existingAssayIdents |> Seq.tryFind (fun x -> x = assayIdent) with + | None -> + failwith $"The given assay must be added to Investigation before it can be registered." + | Some _ -> + () + + let inline validateUniqueAssayIdentifier (assayIdent: string) (existingAssayIdents: seq) = + match existingAssayIdents |> Seq.tryFindIndex (fun x -> x = assayIdent) with + | Some i -> + failwith $"Cannot create assay with name {assayIdent}, as assay names must be unique and assay at index {i} has the same name." + | None -> + () + + let inline validateUniqueStudyIdentifier (study: ArcStudy) (existingStudies: seq) = + match existingStudies |> Seq.tryFindIndex (fun x -> x.Identifier = study.Identifier) with + | Some i -> + failwith $"Cannot create study with name {study.Identifier}, as study names must be unique and study at index {i} has the same name." + | None -> + () + + /// + /// Some functions can change ArcInvestigation.Assays elements. After these functions we must remove all registered assays which might have gone lost. + /// + /// + let inline removeMissingRegisteredAssays (inv: ArcInvestigation) : unit = + let existingAssays = inv.AssayIdentifiers + for study in inv.Studies do + for registeredAssay in study.Assays do + if Seq.contains registeredAssay existingAssays |> not then + study.Assays.Remove registeredAssay |> ignore + + +[] +type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray, ?performers : Person [], ?comments : Comment []) = + let tables = defaultArg tables <| ResizeArray() + let performers = defaultArg performers [||] + let comments = defaultArg comments [||] + let mutable identifier : string = identifier + let mutable investigation : ArcInvestigation option = None + + /// Must be unique in one study + member this.Identifier + with get() = identifier + and internal set(i) = identifier <- i + + // read-online + member this.Investigation + with get() = investigation + and internal set(i) = investigation <- i + + static member FileName = ARCtrl.Path.AssayFileName + + member val MeasurementType : OntologyAnnotation option = measurementType with get, set + member val TechnologyType : OntologyAnnotation option = technologyType with get, set + member val TechnologyPlatform : OntologyAnnotation option = technologyPlatform with get, set + member val Tables : ResizeArray = tables with get, set + member val Performers : Person [] = performers with get, set + member val Comments : Comment [] = comments with get, set + + static member init (identifier : string) = ArcAssay(identifier) + static member create (identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray, ?performers : Person [], ?comments : Comment []) = + ArcAssay(identifier = identifier, ?measurementType = measurementType, ?technologyType = technologyType, ?technologyPlatform = technologyPlatform, ?tables =tables, ?performers = performers, ?comments = comments) + + static member make + (identifier : string) + (measurementType : OntologyAnnotation option) + (technologyType : OntologyAnnotation option) + (technologyPlatform : OntologyAnnotation option) + (tables : ResizeArray) + (performers : Person []) + (comments : Comment []) = + ArcAssay(identifier = identifier, ?measurementType = measurementType, ?technologyType = technologyType, ?technologyPlatform = technologyPlatform, tables =tables, performers = performers, comments = comments) + + member this.TableCount + with get() = ArcTables(this.Tables).Count + + member this.TableNames + with get() = ArcTables(this.Tables).TableNames + + // - Table API - // + // remark should this return ArcTable? + member this.AddTable(table:ArcTable, ?index: int) : unit = ArcTables(this.Tables).AddTable(table, ?index = index) + + static member addTable(table:ArcTable, ?index: int) = + fun (assay:ArcAssay) -> + let c = assay.Copy() + c.AddTable(table, ?index = index) + c + + // - Table API - // + member this.AddTables(tables:seq, ?index: int) = ArcTables(this.Tables).AddTables(tables, ?index = index) + + static member addTables(tables:seq, ?index: int) = + fun (assay:ArcAssay) -> + let c = assay.Copy() + c.AddTables(tables, ?index = index) + c + + // - Table API - // + member this.InitTable(tableName:string, ?index: int) = ArcTables(this.Tables).InitTable(tableName, ?index = index) + + static member initTable(tableName: string, ?index: int) = + fun (assay:ArcAssay) -> + let c = assay.Copy() + c.InitTable(tableName, ?index=index) + + + // - Table API - // + member this.InitTables(tableNames:seq, ?index: int) = ArcTables(this.Tables).InitTables(tableNames, ?index = index) + + static member initTables(tableNames:seq, ?index: int) = + fun (assay:ArcAssay) -> + let c = assay.Copy() + c.InitTables(tableNames, ?index=index) + c + + // - Table API - // + member this.GetTableAt(index:int) : ArcTable = ArcTables(this.Tables).GetTableAt(index) + + /// Receive **copy** of table at `index` + static member getTableAt(index:int) : ArcAssay -> ArcTable = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetTableAt(index) + + // - Table API - // + member this.GetTable(name: string) : ArcTable = ArcTables(this.Tables).GetTable(name) + + /// Receive **copy** of table with `name` = `ArcTable.Name` + static member getTable(name: string) : ArcAssay -> ArcTable = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetTable(name) + + // - Table API - // + member this.UpdateTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).UpdateTableAt(index, table) + + static member updateTableAt(index:int, table:ArcTable) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateTableAt(index, table) + newAssay + + // - Table API - // + member this.UpdateTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).UpdateTable(name, table) + + static member updateTable(name: string, table:ArcTable) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateTable(name, table) + newAssay + + // - Table API - // + member this.SetTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).SetTableAt(index, table) + + static member setTableAt(index:int, table:ArcTable) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.SetTableAt(index, table) + newAssay + + // - Table API - // + member this.SetTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).SetTable(name, table) + + static member setTable(name: string, table:ArcTable) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.SetTable(name, table) + newAssay + + // - Table API - // + member this.RemoveTableAt(index:int) : unit = ArcTables(this.Tables).RemoveTableAt(index) + + static member removeTableAt(index:int) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveTableAt(index) + newAssay + + // - Table API - // + member this.RemoveTable(name: string) : unit = ArcTables(this.Tables).RemoveTable(name) + + static member removeTable(name: string) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveTable(name) + newAssay + + // - Table API - // + // Remark: This must stay `ArcTable -> unit` so name cannot be changed here. + member this.MapTableAt(index: int, updateFun: ArcTable -> unit) = ArcTables(this.Tables).MapTableAt(index, updateFun) + + static member mapTableAt(index:int, updateFun: ArcTable -> unit) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.MapTableAt(index, updateFun) + newAssay + + // - Table API - // + member this.MapTable(name: string, updateFun: ArcTable -> unit) : unit = ArcTables(this.Tables).MapTable(name, updateFun) + + static member updateTable(name: string, updateFun: ArcTable -> unit) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.MapTable(name, updateFun) + newAssay + + // - Table API - // + member this.RenameTableAt(index: int, newName: string) : unit = ArcTables(this.Tables).RenameTableAt(index, newName) + + static member renameTableAt(index: int, newName: string) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RenameTableAt(index, newName) + newAssay + + // - Table API - // + member this.RenameTable(name: string, newName: string) : unit = ArcTables(this.Tables).RenameTable(name, newName) + + static member renameTable(name: string, newName: string) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RenameTable(name, newName) + newAssay + + // - Column CRUD API - // + member this.AddColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = + ArcTables(this.Tables).AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) + + static member addColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcAssay -> ArcAssay = + fun (assay: ArcAssay) -> + let newAssay = assay.Copy() + newAssay.AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) + newAssay + + // - Column CRUD API - // + member this.AddColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = + ArcTables(this.Tables).AddColumn(tableName, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) + + static member addColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.AddColumn(tableName, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) + newAssay + + // - Column CRUD API - // + member this.RemoveColumnAt(tableIndex: int, columnIndex: int) = + ArcTables(this.Tables).RemoveColumnAt(tableIndex, columnIndex) + + static member removeColumnAt(tableIndex: int, columnIndex: int) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveColumnAt(tableIndex, columnIndex) + newAssay + + // - Column CRUD API - // + member this.RemoveColumn(tableName: string, columnIndex: int) : unit = + ArcTables(this.Tables).RemoveColumn(tableName, columnIndex) + + static member removeColumn(tableName: string, columnIndex: int) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveColumn(tableName, columnIndex) + newAssay + + // - Column CRUD API - // + member this.UpdateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + ArcTables(this.Tables).UpdateColumnAt(tableIndex, columnIndex, header, ?cells = cells) + + static member updateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateColumnAt(tableIndex, columnIndex, header, ?cells=cells) + newAssay + + // - Column CRUD API - // + member this.UpdateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + ArcTables(this.Tables).UpdateColumn(tableName, columnIndex, header, ?cells=cells) + + static member updateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateColumn(tableName, columnIndex, header, ?cells=cells) + newAssay + + // - Column CRUD API - // + member this.GetColumnAt(tableIndex: int, columnIndex: int) = + ArcTables(this.Tables).GetColumnAt(tableIndex, columnIndex) + + static member getColumnAt(tableIndex: int, columnIndex: int) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetColumnAt(tableIndex, columnIndex) + + // - Column CRUD API - // + member this.GetColumn(tableName: string, columnIndex: int) = + ArcTables(this.Tables).GetColumn(tableName, columnIndex) + + static member getColumn(tableName: string, columnIndex: int) = + fun (assay: ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetColumn(tableName, columnIndex) + + // - Row CRUD API - // + member this.AddRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) = + ArcTables(this.Tables).AddRowAt(tableIndex, ?cells=cells, ?rowIndex = rowIndex) + + static member addRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) : ArcAssay -> ArcAssay = + fun (assay: ArcAssay) -> + let newAssay = assay.Copy() + newAssay.AddRowAt(tableIndex, ?cells=cells, ?rowIndex=rowIndex) + newAssay + + // - Row CRUD API - // + member this.AddRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) = + ArcTables(this.Tables).AddRow(tableName, ?cells=cells, ?rowIndex = rowIndex) + + static member addRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.AddRow(tableName, ?cells=cells, ?rowIndex=rowIndex) + newAssay + + // - Row CRUD API - // + member this.RemoveRowAt(tableIndex: int, rowIndex: int) = + ArcTables(this.Tables).RemoveRowAt(tableIndex, rowIndex) + + static member removeRowAt(tableIndex: int, rowIndex: int) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveColumnAt(tableIndex, rowIndex) + newAssay + + // - Row CRUD API - // + member this.RemoveRow(tableName: string, rowIndex: int) : unit = + ArcTables(this.Tables).RemoveRow(tableName, rowIndex) + + static member removeRow(tableName: string, rowIndex: int) : ArcAssay -> ArcAssay = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.RemoveRow(tableName, rowIndex) + newAssay + + // - Row CRUD API - // + member this.UpdateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = + ArcTables(this.Tables).UpdateRowAt(tableIndex, rowIndex, cells) + + static member updateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateRowAt(tableIndex, rowIndex, cells) + newAssay + + // - Row CRUD API - // + member this.UpdateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = + ArcTables(this.Tables).UpdateRow(tableName, rowIndex, cells) + + static member updateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.UpdateRow(tableName, rowIndex, cells) + newAssay + + // - Row CRUD API - // + member this.GetRowAt(tableIndex: int, rowIndex: int) = + ArcTables(this.Tables).GetRowAt(tableIndex, rowIndex) + + static member getRowAt(tableIndex: int, rowIndex: int) = + fun (assay:ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetRowAt(tableIndex, rowIndex) + + // - Row CRUD API - // + member this.GetRow(tableName: string, rowIndex: int) = + ArcTables(this.Tables).GetRow(tableName, rowIndex) + + static member getRow(tableName: string, rowIndex: int) = + fun (assay: ArcAssay) -> + let newAssay = assay.Copy() + newAssay.GetRow(tableName, rowIndex) + + // - Mutable properties API - // + static member setPerformers performers (assay: ArcAssay) = + assay.Performers <- performers + assay + + member this.Copy() : ArcAssay = + let nextTables = ResizeArray() + for table in this.Tables do + let copy = table.Copy() + nextTables.Add(copy) + let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) + let nextPerformers = this.Performers |> Array.map (fun c -> c.Copy()) + ArcAssay( + this.Identifier, + ?measurementType = this.MeasurementType, + ?technologyType = this.TechnologyType, + ?technologyPlatform = this.TechnologyPlatform, + tables=nextTables, + performers=nextPerformers, + comments=nextComments + ) + + /// 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 = """(?[^\(]+) \((?[^(]*:[^)]*)\)""" + + let r = System.Text.RegularExpressions.Regex.Match(name,pattern) + + + if r.Success then + let oa = (r.Groups.Item "ontology").Value |> OntologyAnnotation.fromTermAnnotation + let v = (r.Groups.Item "value").Value |> Value.fromString + {oa with Name = (Some (AnnotationValue.Text v.Text))} + else + OntologyAnnotation.fromString(termName = name) + + member internal this.AddToInvestigation (investigation: ArcInvestigation) = + this.Investigation <- Some investigation + + member internal this.RemoveFromInvestigation () = + this.Investigation <- None + + /// Transform an ArcAssay to an ISA Json Assay. + member this.ToAssay() : Assay = + let processSeq = ArcTables(this.Tables).GetProcesses() + let assayMaterials = + AssayMaterials.create( + ?Samples = (ProcessSequence.getSamples processSeq |> Option.fromValueWithDefault []), + ?OtherMaterials = (ProcessSequence.getMaterials processSeq |> Option.fromValueWithDefault []) + ) + |> Option.fromValueWithDefault AssayMaterials.empty + let fileName = + if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then + None + else + Some (ARCtrl.ISA.Identifier.Assay.fileNameFromIdentifier this.Identifier) + Assay.create( + ?FileName = fileName, + ?MeasurementType = this.MeasurementType, + ?TechnologyType = this.TechnologyType, + ?TechnologyPlatform = (this.TechnologyPlatform |> Option.map ArcAssay.composeTechnologyPlatform), + ?DataFiles = (ProcessSequence.getData processSeq |> Option.fromValueWithDefault []), + ?Materials = assayMaterials, + ?CharacteristicCategories = (ProcessSequence.getCharacteristics processSeq |> Option.fromValueWithDefault []), + ?UnitCategories = (ProcessSequence.getUnits processSeq |> Option.fromValueWithDefault []), + ?ProcessSequence = (processSeq |> Option.fromValueWithDefault []), + ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) + ) + + // Create an ArcAssay from an ISA Json Assay. + static member fromAssay (a : Assay) : ArcAssay = + let tables = (a.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) + let identifer = + match a.FileName with + | Some fn -> Identifier.Assay.identifierFromFileName fn + | None -> Identifier.createMissingIdentifier() + ArcAssay.create( + identifer, + ?measurementType = (a.MeasurementType |> Option.map (fun x -> x.Copy())), + ?technologyType = (a.TechnologyType |> Option.map (fun x -> x.Copy())), + ?technologyPlatform = (a.TechnologyPlatform |> Option.map ArcAssay.decomposeTechnologyPlatform), + ?tables = tables, + ?comments = (a.Comments |> Option.map Array.ofList) + ) + +[] +type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays: ResizeArray, ?factors, ?comments) = + let publications = defaultArg publications [||] + let contacts = defaultArg contacts [||] + let studyDesignDescriptors = defaultArg studyDesignDescriptors [||] + let tables = defaultArg tables <| ResizeArray() + let assays = defaultArg assays <| ResizeArray() + let factors = defaultArg factors [||] + let comments = defaultArg comments [||] + + let mutable identifier = identifier + let mutable investigation : ArcInvestigation option = None + /// Must be unique in one investigation + member this.Identifier + with get() = identifier + and internal set(i) = identifier <- i + // read-online + member this.Investigation + with get() = investigation + and internal set(i) = investigation <- i + + member val Title : string option = title with get, set + member val Description : string option = description with get, set + member val SubmissionDate : string option = submissionDate with get, set + member val PublicReleaseDate : string option = publicReleaseDate with get, set + member val Publications : Publication [] = publications with get, set + member val Contacts : Person [] = contacts with get, set + member val StudyDesignDescriptors : OntologyAnnotation [] = studyDesignDescriptors with get, set + member val Tables : ResizeArray = tables with get, set + member val Assays : ResizeArray = assays with get, set + member val Factors : Factor [] = factors with get, set + member val Comments : Comment []= comments with get, set + + static member init(identifier : string) = ArcStudy identifier + + static member create(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays, ?factors, ?comments) = + ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?publications = publications, ?contacts = contacts, ?studyDesignDescriptors = studyDesignDescriptors, ?tables = tables, ?assays = assays, ?factors = factors, ?comments = comments) + + static member make identifier title description submissionDate publicReleaseDate publications contacts studyDesignDescriptors tables assays factors comments = + ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, publications = publications, contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, assays = assays, factors = factors, comments = comments) + + /// + /// Returns true if all fields are None/ empty sequences **except** Identifier. + /// + member this.isEmpty + with get() = + (this.Title = None) && + (this.Description = None) && + (this.SubmissionDate = None) && + (this.PublicReleaseDate = None) && + (this.Publications = [||]) && + (this.Contacts = [||]) && + (this.StudyDesignDescriptors = [||]) && + (this.Tables.Count = 0) && + (this.Assays.Count = 0) && + (this.Factors = [||]) && + (this.Comments = [||]) + + // Not sure how to handle this best case. + static member FileName = ARCtrl.Path.StudyFileName + //member this.FileName = ArcStudy.FileName + + member this.AssayCount + with get() = this.Assays.Count + + member this.AssayIdentifiers + with get(): seq = seq this.Assays + + // - Assay API - CRUD // + /// + /// Add assay to investigation and register it to study. + /// + /// + member internal this.AddAssay(assay: ArcAssay) = + let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation + inv.AddAssay(assay) + inv.RegisterAssay(this.Identifier,assay.Identifier) + + static member internal addAssay(assay: ArcAssay) = + fun (study:ArcStudy) -> + let newStudy = study.Copy() + newStudy.AddAssay(assay) + newStudy + + // - Assay API - CRUD // + member internal this.InitAssay(assayIdentifier: string) = + let assay = ArcAssay(assayIdentifier) + this.AddAssay(assay) + assay + + static member internal initAssay(assayIdentifier: string) = + fun (study:ArcStudy) -> + let newStudy = study.Copy() + newStudy.InitAssay(assayIdentifier) + + member this.RegisterAssay(assayIdentifier: string) = + this.Assays.Add(assayIdentifier) + + static member registerAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.RegisterAssay(assayIdentifier) + copy + + member this.DeregisterAssay(assayIdentifier: string) = + this.Assays.Remove(assayIdentifier) |> ignore + + static member deregisterAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.DeregisterAssay(assayIdentifier) + copy + + member this.GetRegisteredAssays() = + let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation + let assays = ResizeArray() + for assay in inv.Assays do + if Seq.contains assay.Identifier this.AssayIdentifiers then + assays.Add assay + assays + + static member getRegisteredAssays() = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.GetRegisteredAssays() + + //////////////////////////////////// + // - Copy & Paste from ArcAssay - // + //////////////////////////////////// + + member this.TableCount + with get() = ArcTables(this.Tables).Count + + member this.TableNames + with get() = ArcTables(this.Tables).TableNames + + // - Table API - // + // remark should this return ArcTable? + member this.AddTable(table:ArcTable, ?index: int) = ArcTables(this.Tables).AddTable(table, ?index = index) + + static member addTable(table:ArcTable, ?index: int) = + fun (study:ArcStudy) -> + let c = study.Copy() + c.AddTable(table, ?index = index) + c + + // - Table API - // + member this.AddTables(tables:seq, ?index: int) = ArcTables(this.Tables).AddTables(tables, ?index = index) + + static member addTables(tables:seq, ?index: int) = + fun (study:ArcStudy) -> + let c = study.Copy() + c.AddTables(tables, ?index = index) + c + + // - Table API - // + member this.InitTable(tableName:string, ?index: int) = ArcTables(this.Tables).InitTable(tableName, ?index = index) + + static member initTable(tableName: string, ?index: int) = + fun (study:ArcStudy) -> + let c = study.Copy() + c.InitTable(tableName, ?index=index) + + + // - Table API - // + member this.InitTables(tableNames:seq, ?index: int) = ArcTables(this.Tables).InitTables(tableNames, ?index = index) + + static member initTables(tableNames:seq, ?index: int) = + fun (study:ArcStudy) -> + let c = study.Copy() + c.InitTables(tableNames, ?index=index) + c + + // - Table API - // + member this.GetTableAt(index:int) : ArcTable = ArcTables(this.Tables).GetTableAt(index) + + /// Receive **copy** of table at `index` + static member getTableAt(index:int) : ArcStudy -> ArcTable = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetTableAt(index) + + // - Table API - // + member this.GetTable(name: string) : ArcTable = ArcTables(this.Tables).GetTable(name) + + /// Receive **copy** of table with `name` = `ArcTable.Name` + static member getTable(name: string) : ArcStudy -> ArcTable = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetTable(name) + + // - Table API - // + member this.UpdateTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).UpdateTableAt(index, table) + + static member updateTableAt(index:int, table:ArcTable) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateTableAt(index, table) + newAssay + + // - Table API - // + member this.UpdateTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).UpdateTable(name, table) + + static member updateTable(name: string, table:ArcTable) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateTable(name, table) + newAssay + + + // - Table API - // + member this.SetTableAt(index:int, table:ArcTable) = ArcTables(this.Tables).SetTableAt(index, table) + + static member setTableAt(index:int, table:ArcTable) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.SetTableAt(index, table) + newAssay + + // - Table API - // + member this.SetTable(name: string, table:ArcTable) : unit = ArcTables(this.Tables).SetTable(name, table) + + static member setTable(name: string, table:ArcTable) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.SetTable(name, table) + newAssay + + // - Table API - // + member this.RemoveTableAt(index:int) : unit = ArcTables(this.Tables).RemoveTableAt(index) + + static member removeTableAt(index:int) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveTableAt(index) + newAssay + + // - Table API - // + member this.RemoveTable(name: string) : unit = ArcTables(this.Tables).RemoveTable(name) + + static member removeTable(name: string) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveTable(name) + newAssay + + // - Table API - // + // Remark: This must stay `ArcTable -> unit` so name cannot be changed here. + member this.MapTableAt(index: int, updateFun: ArcTable -> unit) = ArcTables(this.Tables).MapTableAt(index, updateFun) + + static member mapTableAt(index:int, updateFun: ArcTable -> unit) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.MapTableAt(index, updateFun) + newAssay + + // - Table API - // + member this.MapTable(name: string, updateFun: ArcTable -> unit) : unit = ArcTables(this.Tables).MapTable(name, updateFun) + + static member mapTable(name: string, updateFun: ArcTable -> unit) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.MapTable(name, updateFun) + newAssay + + // - Table API - // + member this.RenameTableAt(index: int, newName: string) : unit = ArcTables(this.Tables).RenameTableAt(index, newName) + + static member renameTableAt(index: int, newName: string) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RenameTableAt(index, newName) + newAssay + + // - Table API - // + member this.RenameTable(name: string, newName: string) : unit = ArcTables(this.Tables).RenameTable(name, newName) + + static member renameTable(name: string, newName: string) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RenameTable(name, newName) + newAssay + + // - Column CRUD API - // + member this.AddColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = + ArcTables(this.Tables).AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) + + static member addColumnAt(tableIndex:int, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcStudy -> ArcStudy = + fun (study: ArcStudy) -> + let newAssay = study.Copy() + newAssay.AddColumnAt(tableIndex, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) + newAssay + + // - Column CRUD API - // + member this.AddColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) = + ArcTables(this.Tables).AddColumn(tableName, header, ?cells=cells, ?columnIndex = columnIndex, ?forceReplace = forceReplace) + + static member addColumn(tableName: string, header: CompositeHeader, ?cells: CompositeCell [], ?columnIndex: int, ?forceReplace: bool) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.AddColumn(tableName, header, ?cells=cells, ?columnIndex=columnIndex, ?forceReplace=forceReplace) + newAssay + + // - Column CRUD API - // + member this.RemoveColumnAt(tableIndex: int, columnIndex: int) = + ArcTables(this.Tables).RemoveColumnAt(tableIndex, columnIndex) + + static member removeColumnAt(tableIndex: int, columnIndex: int) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveColumnAt(tableIndex, columnIndex) + newAssay + + // - Column CRUD API - // + member this.RemoveColumn(tableName: string, columnIndex: int) : unit = + ArcTables(this.Tables).RemoveColumn(tableName, columnIndex) + + static member removeColumn(tableName: string, columnIndex: int) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveColumn(tableName, columnIndex) + newAssay + + // - Column CRUD API - // + member this.UpdateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + ArcTables(this.Tables).UpdateColumnAt(tableIndex, columnIndex, header, ?cells = cells) + + static member updateColumnAt(tableIndex: int, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateColumnAt(tableIndex, columnIndex, header, ?cells=cells) + newAssay + + // - Column CRUD API - // + member this.UpdateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + ArcTables(this.Tables).UpdateColumn(tableName, columnIndex, header, ?cells=cells) + + static member updateColumn(tableName: string, columnIndex: int, header: CompositeHeader, ?cells: CompositeCell []) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateColumn(tableName, columnIndex, header, ?cells=cells) + newAssay + + // - Column CRUD API - // + member this.GetColumnAt(tableIndex: int, columnIndex: int) = + ArcTables(this.Tables).GetColumnAt(tableIndex, columnIndex) + + static member getColumnAt(tableIndex: int, columnIndex: int) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetColumnAt(tableIndex, columnIndex) + + // - Column CRUD API - // + member this.GetColumn(tableName: string, columnIndex: int) = + ArcTables(this.Tables).GetColumn(tableName, columnIndex) + + static member getColumn(tableName: string, columnIndex: int) = + fun (study: ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetColumn(tableName, columnIndex) + + // - Row CRUD API - // + member this.AddRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) = + ArcTables(this.Tables).AddRowAt(tableIndex, ?cells=cells, ?rowIndex = rowIndex) + + static member addRowAt(tableIndex:int, ?cells: CompositeCell [], ?rowIndex: int) : ArcStudy -> ArcStudy = + fun (study: ArcStudy) -> + let newAssay = study.Copy() + newAssay.AddRowAt(tableIndex, ?cells=cells, ?rowIndex=rowIndex) + newAssay + + // - Row CRUD API - // + member this.AddRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) = + ArcTables(this.Tables).AddRow(tableName, ?cells=cells, ?rowIndex = rowIndex) + + static member addRow(tableName: string, ?cells: CompositeCell [], ?rowIndex: int) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.AddRow(tableName, ?cells=cells, ?rowIndex=rowIndex) + newAssay + + // - Row CRUD API - // + member this.RemoveRowAt(tableIndex: int, rowIndex: int) = + ArcTables(this.Tables).RemoveRowAt(tableIndex, rowIndex) + + static member removeRowAt(tableIndex: int, rowIndex: int) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveColumnAt(tableIndex, rowIndex) + newAssay + + // - Row CRUD API - // + member this.RemoveRow(tableName: string, rowIndex: int) : unit = + ArcTables(this.Tables).RemoveRow(tableName, rowIndex) + + static member removeRow(tableName: string, rowIndex: int) : ArcStudy -> ArcStudy = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.RemoveRow(tableName, rowIndex) + newAssay + + // - Row CRUD API - // + member this.UpdateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = + ArcTables(this.Tables).UpdateRowAt(tableIndex, rowIndex, cells) + + static member updateRowAt(tableIndex: int, rowIndex: int, cells: CompositeCell []) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateRowAt(tableIndex, rowIndex, cells) + newAssay + + // - Row CRUD API - // + member this.UpdateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = + ArcTables(this.Tables).UpdateRow(tableName, rowIndex, cells) + + static member updateRow(tableName: string, rowIndex: int, cells: CompositeCell []) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.UpdateRow(tableName, rowIndex, cells) + newAssay + + // - Row CRUD API - // + member this.GetRowAt(tableIndex: int, rowIndex: int) = + ArcTables(this.Tables).GetRowAt(tableIndex, rowIndex) + + static member getRowAt(tableIndex: int, rowIndex: int) = + fun (study:ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetRowAt(tableIndex, rowIndex) + + // - Row CRUD API - // + member this.GetRow(tableName: string, rowIndex: int) = + ArcTables(this.Tables).GetRow(tableName, rowIndex) + + static member getRow(tableName: string, rowIndex: int) = + fun (study: ArcStudy) -> + let newAssay = study.Copy() + newAssay.GetRow(tableName, rowIndex) + + member internal this.AddToInvestigation (investigation: ArcInvestigation) = + this.Investigation <- Some investigation + + member internal this.RemoveFromInvestigation () = + this.Investigation <- None + + member this.Copy() : ArcStudy = + let nextTables = ResizeArray() + let nextAssays = ResizeArray() + for table in this.Tables do + let copy = table.Copy() + nextTables.Add(copy) + for study in this.Assays do + let copy = study.Copy() + nextAssays.Add(copy) + let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) + let nextFactors = this.Factors |> Array.map (fun c -> c.Copy()) + let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) + let nextPublications = this.Publications |> Array.map (fun c -> c.Copy()) + let nextStudyDesignDescriptors = this.StudyDesignDescriptors |> Array.map (fun c -> c.Copy()) + ArcStudy( + this.Identifier, + ?title = this.Title, + ?description = this.Description, + ?submissionDate = this.SubmissionDate, + ?publicReleaseDate = this.PublicReleaseDate, + publications = nextPublications, + contacts = nextContacts, + studyDesignDescriptors = nextStudyDesignDescriptors, + tables = nextTables, + assays = nextAssays, + factors = nextFactors, + comments = nextComments + ) + + /// Transform an ArcStudy to an ISA Json Study. + member this.ToStudy() : Study = + let processSeq = ArcTables(this.Tables).GetProcesses() + let protocols = ProcessSequence.getProtocols processSeq |> Option.fromValueWithDefault [] + let assays = this.Assays |> Seq.toList |> List.map (fun a -> a.ToAssay()) |> Option.fromValueWithDefault [] + let studyMaterials = + StudyMaterials.create( + ?Sources = (ProcessSequence.getSources processSeq |> Option.fromValueWithDefault []), + ?Samples = (ProcessSequence.getSamples processSeq |> Option.fromValueWithDefault []), + ?OtherMaterials = (ProcessSequence.getMaterials processSeq |> Option.fromValueWithDefault []) + ) + |> Option.fromValueWithDefault StudyMaterials.empty + let identifier,fileName = + if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then + None, None + else + Some this.Identifier, Some (Identifier.Study.fileNameFromIdentifier this.Identifier) + Study.create( + ?FileName = fileName, + ?Identifier = identifier, + ?Title = this.Title, + ?Description = this.Description, + ?SubmissionDate = this.SubmissionDate, + ?PublicReleaseDate = this.PublicReleaseDate, + ?Publications = (this.Publications |> List.ofArray |> Option.fromValueWithDefault []), + ?Contacts = (this.Contacts |> List.ofArray |> Option.fromValueWithDefault []), + ?StudyDesignDescriptors = (this.StudyDesignDescriptors |> List.ofArray |> Option.fromValueWithDefault []), + ?Protocols = protocols, + ?Materials = studyMaterials, + ?ProcessSequence = (processSeq |> Option.fromValueWithDefault []), + ?Assays = assays, + ?Factors = (this.Factors |> List.ofArray |> Option.fromValueWithDefault []), + ?CharacteristicCategories = (ProcessSequence.getCharacteristics processSeq |> Option.fromValueWithDefault []), + ?UnitCategories = (ProcessSequence.getUnits processSeq |> Option.fromValueWithDefault []), + ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) + ) + + // Create an ArcStudy from an ISA Json Study. + static member fromStudy (s : Study) : ArcStudy = + let tables = (s.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) + let identifer = + match s.FileName with + | Some fn -> Identifier.Study.identifierFromFileName fn + | None -> Identifier.createMissingIdentifier() + let assays = s.Assays |> Option.map (List.map ArcAssay.fromAssay >> ResizeArray) + ArcStudy.create( + identifer, + ?title = s.Title, + ?description = s.Description, + ?submissionDate = s.SubmissionDate, + ?publicReleaseDate = s.PublicReleaseDate, + ?publications = (s.Publications |> Option.map Array.ofList), + ?contacts = (s.Contacts|> Option.map Array.ofList), + ?studyDesignDescriptors = (s.StudyDesignDescriptors |> Option.map Array.ofList), + ?tables = tables, + ?assays = assays, + ?factors = (s.Factors |> Option.map Array.ofList), + ?comments = (s.Comments |> Option.map Array.ofList) + ) + +[] +type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = + + let ontologySourceReferences = defaultArg ontologySourceReferences [||] + let publications = defaultArg publications [||] + let contacts = defaultArg contacts [||] + let assays = defaultArg assays <| ResizeArray() + let studies = defaultArg studies (ResizeArray()) + let comments = defaultArg comments [||] + let remarks = defaultArg remarks [||] + + let mutable identifier = identifier + /// Must be unique in one investigation + member this.Identifier + with get() = identifier + and internal set(i) = identifier <- i + + member val Title : string option = title with get, set + member val Description : string option = description with get, set + member val SubmissionDate : string option = submissionDate with get, set + member val PublicReleaseDate : string option = publicReleaseDate with get, set + member val OntologySourceReferences : OntologySourceReference [] = ontologySourceReferences with get, set + member val Publications : Publication [] = publications with get, set + member val Contacts : Person [] = contacts with get, set + member val Assays : ResizeArray = assays with get, set + member val Studies : ResizeArray = studies with get, set + member val Comments : Comment [] = comments with get, set + member val Remarks : Remark [] = remarks with get, set + + static member FileName = ARCtrl.Path.InvestigationFileName + + static member init(identifier: string) = ArcInvestigation identifier + static member create(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = + ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?ontologySourceReferences = ontologySourceReferences, ?publications = publications, ?contacts = contacts, ?assays = assays, ?studies = studies, ?comments = comments, ?remarks = remarks) + + static member make (identifier : string) (title : string option) (description : string option) (submissionDate : string option) (publicReleaseDate : string option) (ontologySourceReferences : OntologySourceReference []) (publications : Publication []) (contacts : Person []) (assays: ResizeArray) (studies : ResizeArray) (comments : Comment []) (remarks : Remark []) : ArcInvestigation = + ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, assays = assays, studies = studies, comments = comments, remarks = remarks) + + member this.AssayCount + with get() = this.Assays.Count + + member this.AssayIdentifiers + with get(): seq = this.Assays |> Seq.map (fun (x:ArcAssay) -> x.Identifier) + + // - Assay API - CRUD // + member this.AddAssay(assay: ArcAssay) = + ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assay.Identifier (this.Assays |> Seq.map (fun x -> x.Identifier)) + this.Assays.Add(assay) + + static member addAssay(assay: ArcAssay) = + fun (inv:ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.AddAssay(assay) + newInvestigation + + // - Assay API - CRUD // + member this.InitAssay(assayIdentifier: string) = + let assay = ArcAssay(assayIdentifier) + this.AddAssay(assay) + assay + + static member initAssay(assayIdentifier: string) = + fun (inv:ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.InitAssay(assayIdentifier) + + // - Assay API - CRUD // + /// + /// Removes assay at specified index from ArcInvestigation and deregisteres it from all studies. + /// + /// + member this.RemoveAssayAt(index: int) = + this.Assays.RemoveAt(index) + this.DeregisterMissingAssays() + + static member removeAssayAt(index: int) = + fun (inv: ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.RemoveAssayAt(index) + newInvestigation + + // - Assay API - CRUD // + /// + /// Removes assay with specified identifier from ArcInvestigation and deregisteres it from all studies. + /// + /// + member this.RemoveAssay(assayIdentifier: string) = + let index = this.GetAssayIndex(assayIdentifier) + this.RemoveAssayAt(index) + + static member removeAssay(assayIdentifier: string) = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.RemoveAssay(assayIdentifier) + newInv + + // - Assay API - CRUD // + member this.SetAssayAt(index: int, assay: ArcAssay) = + ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assay.Identifier (this.Assays |> Seq.removeAt index |> Seq.map (fun a -> a.Identifier)) + this.Assays.[index] <- assay + this.DeregisterMissingAssays() + + static member setAssayAt(index: int, assay: ArcAssay) = + fun (inv:ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.SetAssayAt(index, assay) + newInvestigation + + // - Assay API - CRUD // + member this.SetAssay(assayIdentifier: string, assay: ArcAssay) = + let index = this.GetAssayIndex(assayIdentifier) + this.SetAssayAt(index, assay) + + static member setAssay(assayIdentifier: string, assay: ArcAssay) = + fun (inv:ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.SetAssay(assayIdentifier, assay) + newInvestigation + + // - Assay API - CRUD // + member this.GetAssayIndex(assayIdentifier: string) = + let index = this.Assays.FindIndex (fun a -> a.Identifier = assayIdentifier) + if index = -1 then failwith $"Unable to find assay with specified identifier '{assayIdentifier}'!" + index + + static member getAssayIndex(assayIdentifier: string) : ArcInvestigation -> int = + fun (inv: ArcInvestigation) -> inv.GetAssayIndex(assayIdentifier) + + // - Assay API - CRUD // + member this.GetAssayAt(index: int) : ArcAssay = + this.Assays.[index] + + static member getAssayAt(index: int) : ArcInvestigation -> ArcAssay = + fun (inv: ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.GetAssayAt(index) + + // - Assay API - CRUD // + member this.GetAssay(assayIdentifier: string) : ArcAssay = + let index = this.GetAssayIndex(assayIdentifier) + this.GetAssayAt index + + static member getAssay(assayIdentifier: string) : ArcInvestigation -> ArcAssay = + fun (inv: ArcInvestigation) -> + let newInvestigation = inv.Copy() + newInvestigation.GetAssay(assayIdentifier) + + member this.StudyCount + with get() = this.Studies.Count + + member this.StudyIdentifiers + with get() = this.Studies |> Seq.map (fun (x:ArcStudy) -> x.Identifier) + + // - Study API - CRUD // + member this.AddStudy(study: ArcStudy) = + ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study this.Studies + this.Studies.Add(study) + + static member addStudy(study: ArcStudy) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.AddStudy(study) + copy + + // - Study API - CRUD // + member this.InitStudy (studyName: string) = + let study = ArcStudy.init(studyName) + this.AddStudy(study) + study + + static member initStudy(studyName: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.InitStudy(studyName) + + // - Study API - CRUD // + member this.RemoveStudyAt(index: int) = + this.Studies.RemoveAt(index) + + static member removeStudyAt(index: int) = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.RemoveStudyAt(index) + newInv + + // - Study API - CRUD // + member this.RemoveStudy(studyIdentifier: string) = + this.GetStudy(studyIdentifier) + |> this.Studies.Remove + |> ignore + + static member removeStudy(studyIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.RemoveStudy(studyIdentifier) + copy + + // - Study API - CRUD // + member this.SetStudyAt(index: int, study: ArcStudy) = + ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) + this.Studies.[index] <- study + + static member setStudyAt(index: int, study: ArcStudy) = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.SetStudyAt(index, study) + newInv + + // - Study API - CRUD // + member this.SetStudy(studyIdentifier: string, study: ArcStudy) = + let index = this.GetStudyIndex studyIdentifier + ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) + this.Studies.[index] <- study + + static member setStudy(studyIdentifier: string, study: ArcStudy) = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.SetStudy(studyIdentifier, study) + newInv + + // - Study API - CRUD // + member this.GetStudyIndex(studyIdentifier: string) : int = + let index = this.Studies.FindIndex (fun s -> s.Identifier = studyIdentifier) + if index = -1 then failwith $"Unable to find study with specified identifier '{studyIdentifier}'!" + index + + // - Study API - CRUD // + static member getStudyIndex(studyIdentifier: string) : ArcInvestigation -> int = + fun (inv: ArcInvestigation) -> inv.GetStudyIndex (studyIdentifier) + + // - Study API - CRUD // + member this.GetStudyAt(index: int) : ArcStudy = + this.Studies.[index] + + static member getStudyAt(index: int) : ArcInvestigation -> ArcStudy = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.GetStudyAt(index) + + // - Study API - CRUD // + member this.GetStudy(studyIdentifier: string) : ArcStudy = + this.Studies.Find (fun s -> s.Identifier = studyIdentifier) + + static member getStudy(studyIdentifier: string) : ArcInvestigation -> ArcStudy = + fun (inv: ArcInvestigation) -> + let newInv = inv.Copy() + newInv.GetStudy(studyIdentifier) + + // - Study API - CRUD // + /// + /// Register an existing assay from ArcInvestigation.Assays to a existing study. + /// + /// + /// + member this.RegisterAssayAt(studyIndex: int, assayIdentifier: string) = + let study = this.GetStudyAt(studyIndex) + ArcTypesAux.SanityChecks.validateAssayRegister assayIdentifier (this.Assays |> Seq.map (fun a -> a.Identifier)) + ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assayIdentifier study.Assays + study.RegisterAssay(assayIdentifier) + + static member registerAssayAt(studyIndex: int, assayIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.RegisterAssayAt(studyIndex, assayIdentifier) + copy + + // - Study API - CRUD // + /// + /// Register an existing assay from ArcInvestigation.Assays to a existing study. + /// + /// + /// + member this.RegisterAssay(studyIdentifier: string, assayIdentifier: string) = + let index = this.GetStudyIndex(studyIdentifier) + this.RegisterAssayAt(index, assayIdentifier) + + static member registerAssay(studyIdentifier: string, assayIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.RegisterAssay(studyIdentifier, assayIdentifier) + copy + + // - Study API - CRUD // + member this.DeregisterAssayAt(studyIndex: int, assayIdentifier: string) = + let study = this.GetStudyAt(studyIndex) + study.DeregisterAssay(assayIdentifier) + + static member deregisterAssayAt(studyIndex: int, assayIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.DeregisterAssayAt(studyIndex, assayIdentifier) + copy + + // - Study API - CRUD // + /// + /// Deregisters assays not found in ArcInvestigation.Assays from all studies. + /// + member this.DeregisterMissingAssays() = + ArcTypesAux.removeMissingRegisteredAssays this + + static member deregisterMissingAssays() = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.DeregisterMissingAssays() + copy + + member this.Copy() : ArcInvestigation = + let nextAssays = ResizeArray() + let nextStudies = ResizeArray() + for assay in this.Assays do + let copy = assay.Copy() + nextAssays.Add(copy) + for study in this.Studies do + let copy = study.Copy() + nextStudies.Add(copy) + let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) + let nextRemarks = this.Remarks |> Array.map (fun c -> c.Copy()) + let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) + let nextPublications = this.Publications |> Array.map (fun c -> c.Copy()) + let nextOntologySourceReferences = this.OntologySourceReferences |> Array.map (fun c -> c.Copy()) + ArcInvestigation( + this.Identifier, + ?title = this.Title, + ?description = this.Description, + ?submissionDate = this.SubmissionDate, + ?publicReleaseDate = this.PublicReleaseDate, + ontologySourceReferences = nextOntologySourceReferences, + publications = nextPublications, + contacts = nextContacts, + assays = nextAssays, + studies = nextStudies, // correct mutable behaviour is tested on this field + comments = nextComments, + remarks = nextRemarks + ) + + + /// Transform an ArcInvestigation to an ISA Json Investigation. + member this.ToInvestigation() : Investigation = + let studies = this.Studies |> Seq.toList |> List.map (fun a -> a.ToStudy()) |> Option.fromValueWithDefault [] + let identifier = + if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then None + else Some this.Identifier + Investigation.create( + FileName = ARCtrl.Path.InvestigationFileName, + ?Identifier = identifier, + ?Title = this.Title, + ?Description = this.Description, + ?SubmissionDate = this.SubmissionDate, + ?PublicReleaseDate = this.PublicReleaseDate, + ?Publications = (this.Publications |> List.ofArray |> Option.fromValueWithDefault []), + ?Contacts = (this.Contacts |> List.ofArray |> Option.fromValueWithDefault []), + ?Studies = studies, + ?Comments = (this.Comments |> List.ofArray |> Option.fromValueWithDefault []) + ) + + // Create an ArcInvestigation from an ISA Json Investigation. + static member fromInvestigation (i : Investigation) : ArcInvestigation = + let identifer = + match i.Identifier with + | Some i -> i + | None -> Identifier.createMissingIdentifier() + let studies = i.Studies |> Option.map (List.map ArcStudy.fromStudy >> ResizeArray) + ArcInvestigation.create( + identifer, + ?title = i.Title, + ?description = i.Description, + ?submissionDate = i.SubmissionDate, + ?publicReleaseDate = i.PublicReleaseDate, + ?publications = (i.Publications |> Option.map Array.ofList), + ?contacts = (i.Contacts |> Option.map Array.ofList), + ?studies = studies, + ?comments = (i.Comments |> Option.map Array.ofList) + ) From 2912c02182e15826945053c5366f0e7f5fb2b463 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Wed, 6 Sep 2023 16:29:12 +0200 Subject: [PATCH 02/17] Work on update :construction: --- src/ISA/ISA/ArcTypes/ArcTypes.fs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index a34647b6..9ca442a0 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -941,13 +941,10 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi member this.Copy() : ArcStudy = let nextTables = ResizeArray() - let nextAssays = ResizeArray() + let nextAssays = ResizeArray(this.Assays) for table in this.Tables do let copy = table.Copy() nextTables.Add(copy) - for study in this.Assays do - let copy = study.Copy() - nextAssays.Add(copy) let nextComments = this.Comments |> Array.map (fun c -> c.Copy()) let nextFactors = this.Factors |> Array.map (fun c -> c.Copy()) let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) @@ -972,7 +969,18 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi member this.ToStudy() : Study = let processSeq = ArcTables(this.Tables).GetProcesses() let protocols = ProcessSequence.getProtocols processSeq |> Option.fromValueWithDefault [] - let assays = this.Assays |> Seq.toList |> List.map (fun a -> a.ToAssay()) |> Option.fromValueWithDefault [] + /// Two Options: + /// 1. Init new assays with only identifier. This is possible without ArcInvestigation parent. + /// 2. Get full assays from ArcInvestigation parent. + let assays = + match this.Investigation with + | Some i -> + this.GetRegisteredAssays() + | None -> + this.Assays |> Seq.map (fun x -> ArcAssay.init(x)) |> ResizeArray + |> Seq.toList + |> List.map (fun a -> a.ToAssay()) + |> Option.fromValueWithDefault [] let studyMaterials = StudyMaterials.create( ?Sources = (ProcessSequence.getSources processSeq |> Option.fromValueWithDefault []), @@ -1006,13 +1014,14 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ) // Create an ArcStudy from an ISA Json Study. - static member fromStudy (s : Study) : ArcStudy = + static member fromStudy (s : Study) : (ArcStudy * ResizeArray option) = let tables = (s.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) let identifer = match s.FileName with | Some fn -> Identifier.Study.identifierFromFileName fn | None -> Identifier.createMissingIdentifier() let assays = s.Assays |> Option.map (List.map ArcAssay.fromAssay >> ResizeArray) + let assaysIdentifiers = assays |> Option.map (fun aArr -> aArr |> Seq.map (fun a -> a.Identifier) |> ResizeArray) ArcStudy.create( identifer, ?title = s.Title, @@ -1023,10 +1032,11 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ?contacts = (s.Contacts|> Option.map Array.ofList), ?studyDesignDescriptors = (s.StudyDesignDescriptors |> Option.map Array.ofList), ?tables = tables, - ?assays = assays, + ?assays = assaysIdentifiers, ?factors = (s.Factors |> Option.map Array.ofList), ?comments = (s.Comments |> Option.map Array.ofList) - ) + ), + assays [] type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = From 658f914cdc5a124df93724c2d538aae202ecf492 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Thu, 7 Sep 2023 16:56:24 +0200 Subject: [PATCH 03/17] isa and isa.json tests pass :heavy_check_mark: --- src/ISA/ISA.Json/Study.fs | 8 +- src/ISA/ISA/ArcTypes/ArcTypes.fs | 87 ++++++--- tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 183 +++++++++++++----- tests/ISA/ISA.Tests/ArcJsonConversionTests.fs | 13 +- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 70 +++---- tests/ISA/ISA.Tests/Fable.Tests.fs | 2 +- 6 files changed, 228 insertions(+), 135 deletions(-) diff --git a/src/ISA/ISA.Json/Study.fs b/src/ISA/ISA.Json/Study.fs index a9a79bbc..d2f8b04d 100644 --- a/src/ISA/ISA.Json/Study.fs +++ b/src/ISA/ISA.Json/Study.fs @@ -113,11 +113,11 @@ module ArcStudy = GDecode.fromString (decoder (ConverterOptions())) s |> ArcStudy.fromStudy - let toString (a:ArcStudy) = - encoder (ConverterOptions()) (a.ToStudy()) + let toString (a:ArcStudy) (assays: ResizeArray) = + encoder (ConverterOptions()) (a.ToStudy(assays)) |> Encode.toString 2 /// exports in json-ld format - let toStringLD (a:ArcStudy) = - encoder (ConverterOptions(SetID=true,IncludeType=true)) (a.ToStudy()) + let toStringLD (a:ArcStudy) (assays: ResizeArray) = + encoder (ConverterOptions(SetID=true,IncludeType=true)) (a.ToStudy(assays)) |> Encode.toString 2 \ No newline at end of file diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 9ca442a0..541d4ce6 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -39,7 +39,8 @@ module ArcTypesAux = let inline removeMissingRegisteredAssays (inv: ArcInvestigation) : unit = let existingAssays = inv.AssayIdentifiers for study in inv.Studies do - for registeredAssay in study.Assays do + let registeredAssays = study.AssayIdentifiers + for registeredAssay in registeredAssays do if Seq.contains registeredAssay existingAssays |> not then study.Assays.Remove registeredAssay |> ignore @@ -560,31 +561,31 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi with get() = this.Assays.Count member this.AssayIdentifiers - with get(): seq = seq this.Assays + with get(): string [] = Array.ofSeq this.Assays // - Assay API - CRUD // /// /// Add assay to investigation and register it to study. /// /// - member internal this.AddAssay(assay: ArcAssay) = + member this.AddAssay(assay: ArcAssay) = let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation inv.AddAssay(assay) inv.RegisterAssay(this.Identifier,assay.Identifier) - static member internal addAssay(assay: ArcAssay) = + static member addAssay(assay: ArcAssay) = fun (study:ArcStudy) -> let newStudy = study.Copy() newStudy.AddAssay(assay) newStudy // - Assay API - CRUD // - member internal this.InitAssay(assayIdentifier: string) = + member this.InitAssay(assayIdentifier: string) = let assay = ArcAssay(assayIdentifier) this.AddAssay(assay) assay - static member internal initAssay(assayIdentifier: string) = + static member initAssay(assayIdentifier: string) = fun (study:ArcStudy) -> let newStudy = study.Copy() newStudy.InitAssay(assayIdentifier) @@ -620,6 +621,26 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi let copy = study.Copy() copy.GetRegisteredAssays() + /// + /// Returns ArcAssays registered in study, or if no parent exists, initializies new ArcAssay from identifier. + /// + member this.GetRegisteredAssaysOrIdentifier() = + // Two Options: + // 1. Init new assays with only identifier. This is possible without ArcInvestigation parent. + // 2. Get full assays from ArcInvestigation parent. + match this.Investigation with + | Some i -> + this.GetRegisteredAssays() + | None -> + this.Assays |> Seq.map (fun x -> ArcAssay.init(x)) |> ResizeArray + |> Seq.toList + |> List.map (fun a -> a.ToAssay()) + + static member getRegisteredAssaysOrIdentifier() = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.GetRegisteredAssaysOrIdentifier() + //////////////////////////////////// // - Copy & Paste from ArcAssay - // //////////////////////////////////// @@ -966,21 +987,9 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ) /// Transform an ArcStudy to an ISA Json Study. - member this.ToStudy() : Study = + member this.ToStudy(arcAssays: ResizeArray) : Study = let processSeq = ArcTables(this.Tables).GetProcesses() let protocols = ProcessSequence.getProtocols processSeq |> Option.fromValueWithDefault [] - /// Two Options: - /// 1. Init new assays with only identifier. This is possible without ArcInvestigation parent. - /// 2. Get full assays from ArcInvestigation parent. - let assays = - match this.Investigation with - | Some i -> - this.GetRegisteredAssays() - | None -> - this.Assays |> Seq.map (fun x -> ArcAssay.init(x)) |> ResizeArray - |> Seq.toList - |> List.map (fun a -> a.ToAssay()) - |> Option.fromValueWithDefault [] let studyMaterials = StudyMaterials.create( ?Sources = (ProcessSequence.getSources processSeq |> Option.fromValueWithDefault []), @@ -992,7 +1001,8 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then None, None else - Some this.Identifier, Some (Identifier.Study.fileNameFromIdentifier this.Identifier) + Some this.Identifier, Some (Identifier.Study.fileNameFromIdentifier this.Identifier) + let assays = arcAssays |> List.ofSeq |> List.map (fun a -> a.ToAssay()) Study.create( ?FileName = fileName, ?Identifier = identifier, @@ -1006,7 +1016,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ?Protocols = protocols, ?Materials = studyMaterials, ?ProcessSequence = (processSeq |> Option.fromValueWithDefault []), - ?Assays = assays, + ?Assays = (assays |> Option.fromValueWithDefault []), ?Factors = (this.Factors |> List.ofArray |> Option.fromValueWithDefault []), ?CharacteristicCategories = (ProcessSequence.getCharacteristics processSeq |> Option.fromValueWithDefault []), ?UnitCategories = (ProcessSequence.getUnits processSeq |> Option.fromValueWithDefault []), @@ -1014,14 +1024,14 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ) // Create an ArcStudy from an ISA Json Study. - static member fromStudy (s : Study) : (ArcStudy * ResizeArray option) = + static member fromStudy (s : Study) : (ArcStudy * ResizeArray) = let tables = (s.ProcessSequence |> Option.map (ArcTables.fromProcesses >> fun t -> t.Tables)) let identifer = match s.FileName with | Some fn -> Identifier.Study.identifierFromFileName fn | None -> Identifier.createMissingIdentifier() - let assays = s.Assays |> Option.map (List.map ArcAssay.fromAssay >> ResizeArray) - let assaysIdentifiers = assays |> Option.map (fun aArr -> aArr |> Seq.map (fun a -> a.Identifier) |> ResizeArray) + let assays = s.Assays |> Option.map (List.map ArcAssay.fromAssay >> ResizeArray) |> Option.defaultValue (ResizeArray()) + let assaysIdentifiers = assays |> Seq.map (fun a -> a.Identifier) |> ResizeArray ArcStudy.create( identifer, ?title = s.Title, @@ -1032,7 +1042,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ?contacts = (s.Contacts|> Option.map Array.ofList), ?studyDesignDescriptors = (s.StudyDesignDescriptors |> Option.map Array.ofList), ?tables = tables, - ?assays = assaysIdentifiers, + ?assays = Some assaysIdentifiers, ?factors = (s.Factors |> Option.map Array.ofList), ?comments = (s.Comments |> Option.map Array.ofList) ), @@ -1044,7 +1054,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin let ontologySourceReferences = defaultArg ontologySourceReferences [||] let publications = defaultArg publications [||] let contacts = defaultArg contacts [||] - let assays = defaultArg assays <| ResizeArray() + let assays = defaultArg assays (ResizeArray()) let studies = defaultArg studies (ResizeArray()) let comments = defaultArg comments [||] let remarks = defaultArg remarks [||] @@ -1194,6 +1204,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin // - Study API - CRUD // member this.AddStudy(study: ArcStudy) = ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study this.Studies + study.Investigation <- Some this this.Studies.Add(study) static member addStudy(study: ArcStudy) = @@ -1331,6 +1342,17 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin copy.DeregisterAssayAt(studyIndex, assayIdentifier) copy + // - Study API - CRUD // + member this.DeregisterAssay(studyIdentifier: string, assayIdentifier: string) = + let index = this.GetStudyIndex(studyIdentifier) + this.DeregisterAssayAt(index, assayIdentifier) + + static member deregisterAssay(studyIdentifier: string, assayIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.DeregisterAssay(studyIdentifier, assayIdentifier) + copy + // - Study API - CRUD // /// /// Deregisters assays not found in ArcInvestigation.Assays from all studies. @@ -1376,7 +1398,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin /// Transform an ArcInvestigation to an ISA Json Investigation. member this.ToInvestigation() : Investigation = - let studies = this.Studies |> Seq.toList |> List.map (fun a -> a.ToStudy()) |> Option.fromValueWithDefault [] + let studies = this.Studies |> Seq.toList |> List.map (fun a -> a.ToStudy(a.GetRegisteredAssays())) |> Option.fromValueWithDefault [] let identifier = if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then None else Some this.Identifier @@ -1399,7 +1421,13 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin match i.Identifier with | Some i -> i | None -> Identifier.createMissingIdentifier() - let studies = i.Studies |> Option.map (List.map ArcStudy.fromStudy >> ResizeArray) + let studiesRaw, assaysRaw = + i.Studies + |> Option.defaultValue [] + |> List.map ArcStudy.fromStudy + |> List.unzip + let studies = ResizeArray(studiesRaw) + let assays = assaysRaw |> Seq.concat |> Seq.distinctBy (fun a -> a.Identifier) |> ResizeArray ArcInvestigation.create( identifer, ?title = i.Title, @@ -1408,6 +1436,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin ?publicReleaseDate = i.PublicReleaseDate, ?publications = (i.Publications |> Option.map Array.ofList), ?contacts = (i.Contacts |> Option.map Array.ofList), - ?studies = studies, + ?assays = Some assays, + ?studies = Some studies, ?comments = (i.Comments |> Option.map Array.ofList) ) diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 5cf0c867..0d0e4df5 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -8,6 +8,11 @@ open Fable.Mocha open Expecto #endif +let private assay_Identifier = "MyAssay" +let private assay_MeasurementType = OntologyAnnotation.fromString("My Measurement Type", "MST", "MST:42424242") +let private create_ExampleAssay() = ArcAssay.create(assay_Identifier,assay_MeasurementType) +let private create_ExampleAssays() = ResizeArray([create_ExampleAssay()]) + let private test_create = testList "create" [ testCase "constructor" <| fun _ -> @@ -19,6 +24,7 @@ let private test_create = let ontologySourceReferences = [|OntologySourceReference.create("Reference 1")|] let publications = [|Publication.create("Publication 1")|] let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] + let assays = create_ExampleAssays() let studies = ResizeArray([|ArcStudy.init("Study 1")|]) let comments = [|Comment.create("Comment 1")|] let remarks = [|Remark.create(1, "Remark 1")|] @@ -33,6 +39,7 @@ let private test_create = ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, + assays = assays, studies = studies, comments = comments, remarks = remarks @@ -46,6 +53,7 @@ let private test_create = Expect.equal actual.OntologySourceReferences ontologySourceReferences "OntologySourceReferences" Expect.equal actual.Publications publications "Publications" Expect.equal actual.Contacts contacts "Contacts" + Expect.equal actual.Assays assays "Assays" Expect.equal actual.Studies studies "Studies" Expect.equal actual.Comments comments "Comments" Expect.equal actual.Remarks remarks "Remarks" @@ -59,6 +67,7 @@ let private test_create = let ontologySourceReferences = [|OntologySourceReference.create("Reference 1")|] let publications = [|Publication.create("Publication 1")|] let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] + let assays = create_ExampleAssays() let studies = ResizeArray([|ArcStudy.init("Study 1")|]) let comments = [|Comment.create("Comment 1")|] let remarks = [|Remark.create(1, "Remark 1")|] @@ -72,6 +81,7 @@ let private test_create = ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, + assays = assays, studies = studies, comments = comments, remarks = remarks @@ -85,6 +95,7 @@ let private test_create = Expect.equal actual.OntologySourceReferences ontologySourceReferences "OntologySourceReferences" Expect.equal actual.Publications publications "Publications" Expect.equal actual.Contacts contacts "Contacts" + Expect.equal actual.Assays assays "Assays" Expect.equal actual.Studies studies "Studies" Expect.equal actual.Comments comments "Comments" Expect.equal actual.Remarks remarks "Remarks" @@ -102,6 +113,7 @@ let private test_create = Expect.isEmpty actual.OntologySourceReferences "OntologySourceReferences" Expect.isEmpty actual.Publications "Publications" Expect.isEmpty actual.Contacts "Contacts" + Expect.isEmpty actual.Assays "Assays" Expect.isEmpty actual.Studies "Studies" Expect.isEmpty actual.Comments "Comments" Expect.isEmpty actual.Remarks "Remarks" @@ -115,6 +127,7 @@ let private test_create = let ontologySourceReferences = [|OntologySourceReference.create("Reference 1")|] let publications = [|Publication.create("Publication 1")|] let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] + let assays = create_ExampleAssays() let studies = ResizeArray([|ArcStudy.init("Study 1")|]) let comments = [|Comment.create("Comment 1")|] let remarks = [|Remark.create(1, "Remark 1")|] @@ -129,6 +142,7 @@ let private test_create = ontologySourceReferences publications contacts + assays studies comments remarks @@ -141,11 +155,81 @@ let private test_create = Expect.equal actual.OntologySourceReferences ontologySourceReferences "OntologySourceReferences" Expect.equal actual.Publications publications "Publications" Expect.equal actual.Contacts contacts "Contacts" + Expect.equal actual.Assays assays "Assays" Expect.equal actual.Studies studies "Studies" Expect.equal actual.Comments comments "Comments" Expect.equal actual.Remarks remarks "Remarks" ] +let test_RegisteredAssays = testList "RegisteredAssay" [ + testCase "Investigation.RegisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + i.RegisterAssay(s.Identifier, a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + testCase "Study.RegisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + s.RegisterAssay(a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + testCase "Investigation.DeregisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + i.RegisterAssay(s.Identifier, a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + i.DeregisterAssay(s.Identifier,a.Identifier) + Expect.equal s.AssayCount 0 "registered assay count 2" + testCase "Study.DeregisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + s.RegisterAssay(a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + s.DeregisterAssay(a.Identifier) + Expect.equal s.AssayCount 0 "registered assay count 2" + testCase "Remove registered assay from investigation" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + s.RegisterAssay(a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + i.RemoveAssayAt 0 + Expect.equal i.AssayCount 0 "assay count 2" + Expect.equal s.AssayCount 0 "registered assay count 2" + testCase "Investigation.DeregisterMissingAssays" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let a = i.InitAssay("MyAssay") + let s = i.InitStudy("MyStudy") + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + s.RegisterAssay(a.Identifier) + Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.Assays.[0] a.Identifier "identifier" + i.Assays <- (ResizeArray()) + Expect.equal i.AssayCount 0 "assay count 2" + Expect.equal s.AssayCount 1 "registered assay count 2" + i.DeregisterMissingAssays() + Expect.equal s.AssayCount 0 "registered assay count 3" +] + let tests_MutableFields = testList "MutableFields" [ testCase "ensure investigation" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -159,6 +243,23 @@ let tests_MutableFields = testList "MutableFields" [ Expect.equal i.Description (Some "MyName") "FileName" Expect.equal i.Contacts persons "Contacts" Expect.equal i.Title (Some "Awesome Title") "Title" + testCase "test mutable fields on registered assay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let assay = create_ExampleAssay() + let study_identifier = "MyStudy" + i.AddAssay(assay) + let study = i.InitStudy(study_identifier) + study.RegisterAssay(assay.Identifier) + Expect.equal i.AssayCount 1 "assay count" + Expect.equal i.StudyCount 1 "study count" + Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal assay.TableCount 0 "assay table count" + Expect.equal study.Assays.[0] assay.Identifier "registered assay identifier" + let registeredAssay = study.GetRegisteredAssays().[0] + Expect.equal registeredAssay.Identifier assay.Identifier "full registered assay identifier" + let table = registeredAssay.InitTable("My New Table") + Expect.equal assay.TableCount 1 "table count to init table" + Expect.equal i.Assays.[0].TableCount 1 "table count from investigation" ] let tests_Copy = testList "Copy" [ @@ -297,21 +398,24 @@ let tests_Assay = testList "CRUD Assay" [ let createExampleInvestigation() = let i = ArcInvestigation.init("MyInvestigation") let s = ArcStudy.init("Study 1") + let s2 = ArcStudy.create("Study 2",title="Study 2 Title") let a = ArcAssay.init("Assay 1") let a2 = ArcAssay.init("Assay 2") - s.AddAssay(a) - s.AddAssay(a2) - let s2 = ArcStudy.create("Study 2",title="Study 2 Title") i.AddStudy s i.AddStudy s2 + i.AddAssay(a) + i.AddAssay(a2) + i.RegisterAssay(s.Identifier, a.Identifier) + i.RegisterAssay(s.Identifier, a2.Identifier) i testCase "ensure example" <| fun _ -> let i = createExampleInvestigation() Expect.equal i.StudyCount 2 "StudyCount" - TestingUtils.mySequenceEqual i.StudyIdentifiers (seq {"Study 1"; "Study 2"}) "StudyCount" + Expect.equal i.AssayCount 2 "AssayCount" + TestingUtils.mySequenceEqual i.StudyIdentifiers (seq {"Study 1"; "Study 2"}) "StudyIdentifiers" + TestingUtils.mySequenceEqual i.AssayIdentifiers (seq {"Assay 1"; "Assay 2"}) "AssayIdentifiers" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 2 "AssayCount" - TestingUtils.mySequenceEqual s.AssayIdentifiers (seq {"Assay 1"; "Assay 2"}) "AssayIdentifiers" + Expect.equal s.AssayCount 2 "Registered AssayCount" let s2 = i.Studies.Item 1 Expect.equal s2.Title (Some "Study 2 Title") "Study 2 Title" testList "AddAssay" [ @@ -320,22 +424,24 @@ let tests_Assay = testList "CRUD Assay" [ let assay_ident = "New Assay" let assay_techPlatform = OntologyAnnotation.fromString("Assay Tech") let expected = ArcAssay(assay_ident, technologyPlatform = assay_techPlatform) - i.AddAssayAt(0, expected) + i.AddAssay(expected) + i.RegisterAssayAt(0, expected.Identifier) Expect.equal i.StudyCount 2 "StudyCount" let s = i.Studies.Item 0 Expect.equal s.AssayCount 3 "AssayCount" - let actual = s.GetAssayAt 2 + let actual = i.GetAssayAt 2 Expect.equal actual expected "equal" testCase "by identifier" <| fun _ -> let i = createExampleInvestigation() let assay_ident = "New Assay" let assay_techPlatform = OntologyAnnotation.fromString("Assay Tech") let expected = ArcAssay(assay_ident, technologyPlatform = assay_techPlatform) - i.AddAssay("Study 1", expected) + i.AddAssay(expected) + i.RegisterAssay("Study 1", expected.Identifier) Expect.equal i.StudyCount 2 "StudyCount" let s = i.Studies.Item 0 Expect.equal s.AssayCount 3 "AssayCount" - let actual = s.GetAssayAt 2 + let actual = i.GetAssayAt 2 Expect.equal actual expected "equal" ] testList "SetAssay" [ @@ -344,83 +450,65 @@ let tests_Assay = testList "CRUD Assay" [ let assay_ident = "New Assay" let assay_techPlatform = OntologyAnnotation.fromString("Assay Tech") let expected = ArcAssay(assay_ident, technologyPlatform = assay_techPlatform) - i.SetAssayAt("Study 1", 0, expected) + i.SetAssayAt(0, expected) Expect.equal i.StudyCount 2 "StudyCount" + Expect.equal i.AssayCount 2 "AssayCount" + Expect.equal i.Assays.[0] expected "equal" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 2 "AssayCount" - let actual = s.GetAssayAt 0 - Expect.equal actual expected "equal" + Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" testCase "by index tpOntology" <| fun _ -> let i = createExampleInvestigation() let assay_ident = "New Assay" let assay_techPlatform = OntologyAnnotation.fromString("Assay Tech","ABC","ABC:123") let expected = ArcAssay(assay_ident, technologyPlatform = assay_techPlatform) - i.SetAssayAt("Study 1", 0, expected) + i.SetAssayAt(0, expected) Expect.equal i.StudyCount 2 "StudyCount" + Expect.equal i.AssayCount 2 "AssayCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 2 "AssayCount" - let actual = s.GetAssayAt 0 - Expect.equal actual expected "equal" + Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" + Expect.equal i.Assays.[0] expected "equal" testCase "by identifier" <| fun _ -> let i = createExampleInvestigation() let assay_ident = "New Assay" let assay_techPlatform = OntologyAnnotation.fromString("Assay Tech") let expected = ArcAssay(assay_ident, technologyPlatform = assay_techPlatform) - i.SetAssay("Study 1", "Assay 2", expected) + i.SetAssay("Assay 2", expected) Expect.equal i.StudyCount 2 "StudyCount" + Expect.equal i.AssayCount 2 "AssayCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 2 "AssayCount" - let actual = s.GetAssayAt 1 + Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" + let actual = i.Assays.[1] Expect.equal actual expected "equal" ] testList "GetAssay" [ testCase "by index" <| fun _ -> let i = createExampleInvestigation() - let a = i.GetAssayAt("Study 1", 0) + let a = i.GetAssayAt(0) Expect.equal a.Identifier "Assay 1" "FileName" testCase "by ident" <| fun _ -> let i = createExampleInvestigation() - let a = i.GetAssay("Study 1", "Assay 2") + let a = i.GetAssay("Assay 2") Expect.equal a.Identifier "Assay 2" "FileName" testCase "mutable propagation" <| fun _ -> let i = createExampleInvestigation() let tech = Some (OntologyAnnotation.fromString("New Tech Stuff")) - let a = i.GetAssayAt("Study 1", 0) + let a = i.GetAssayAt(0) Expect.equal a.Identifier "Assay 1" "FileName" Expect.equal a.TechnologyPlatform None "TechnologyPlatform" a.TechnologyPlatform <- tech Expect.equal a.TechnologyPlatform tech "TechnologyPlatform, a" - Expect.equal i.Studies.[0].Assays.[0].TechnologyPlatform tech "TechnologyPlatform, direct" + Expect.equal i.Assays.[0].TechnologyPlatform tech "TechnologyPlatform, direct" testCase "mutable propagation, copy" <| fun _ -> let i = createExampleInvestigation() let copy = createExampleInvestigation() let tech = Some (OntologyAnnotation.fromString("New Tech Stuff")) - let a = i.GetAssayAt("Study 1", 0) + let a = i.GetAssayAt(0) Expect.equal a.Identifier "Assay 1" "FileName" Expect.equal a.TechnologyPlatform None "TechnologyPlatform" a.TechnologyPlatform <- tech Expect.equal a.TechnologyPlatform tech "TechnologyPlatform, a" - Expect.equal i.Studies.[0].Assays.[0].TechnologyPlatform tech "TechnologyPlatform, direct" - Expect.equal copy.Studies.[0].Assays.[0].TechnologyPlatform None "TechnologyPlatform, copy direct" - ] - testList "FindAssay" [ - testCase "by identifier" <| fun _ -> - let i = createExampleInvestigation() - let a = i.FindAssay("Assay 1") - Expect.equal a.Identifier "Assay 1" "Identifier" - testCase "mutability" <| fun _ -> - let i = createExampleInvestigation() - let a = i.FindAssay("Assay 1") - let t = a.InitTable("NewTable") - t.AddColumn(CompositeHeader.Input IOType.Sample,[|for i in 1 .. 10 do yield CompositeCell.createFreeText $"My awesome probe {i}"|]) - Expect.equal t.Name "NewTable" "table name" - Expect.equal t.ColumnCount 1 "column count" - Expect.equal t.RowCount 10 "row count" - let aNew = i.FindAssay("Assay 1") - Expect.equal aNew.TableCount 1 "table count" - Expect.equal aNew.Tables.[0].Name "NewTable" "next table name" - Expect.equal aNew.Tables.[0].ColumnCount 1 "next column count" - Expect.equal aNew.Tables.[0].RowCount 10 "next row count" + Expect.equal i.Assays.[0].TechnologyPlatform tech "TechnologyPlatform, direct" + Expect.equal copy.Assays.[0].TechnologyPlatform None "TechnologyPlatform, copy direct" ] ] @@ -428,6 +516,7 @@ let tests_Assay = testList "CRUD Assay" [ let main = testList "ArcInvestigation" [ test_create + test_RegisteredAssays tests_MutableFields tests_Copy tests_Study diff --git a/tests/ISA/ISA.Tests/ArcJsonConversionTests.fs b/tests/ISA/ISA.Tests/ArcJsonConversionTests.fs index 9e648586..eadb1384 100644 --- a/tests/ISA/ISA.Tests/ArcJsonConversionTests.fs +++ b/tests/ISA/ISA.Tests/ArcJsonConversionTests.fs @@ -466,25 +466,24 @@ let private tests_arcAssay = let private tests_arcStudy = testList "ARCStudy" [ - - - testCase "Identifier Set" (fun () -> let identifier = "MyIdentifier" let arcStudy = ArcStudy.create(identifier) - let study = arcStudy.ToStudy() + let study = arcStudy.ToStudy(ResizeArray()) Expect.isSome study.Identifier "Study should have identifier" Expect.equal study.Identifier.Value identifier "Study identifier should match" - let resultArcStudy = ArcStudy.fromStudy study + let resultArcStudy, resultArcAssays = ArcStudy.fromStudy study Expect.equal resultArcStudy.Identifier identifier "ArcStudy identifier should match" + Expect.isEmpty resultArcAssays "ArcAssays should match" ) testCase "No Identifier Set" (fun () -> let identifier = Identifier.createMissingIdentifier() let arcStudy = ArcStudy.create(identifier) - let study = arcStudy.ToStudy() + let study = arcStudy.ToStudy(ResizeArray()) Expect.isNone study.Identifier "Study should not have identifier" - let resultArcStudy = ArcStudy.fromStudy study + let resultArcStudy, resultArcAssays = ArcStudy.fromStudy study Expect.isTrue (Identifier.isMissingIdentifier resultArcStudy.Identifier) "ArcStudy identifier should be missing" + Expect.isEmpty resultArcAssays "ArcAssays should match" ) ] diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 937340cd..3ed5183c 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -8,6 +8,10 @@ open Fable.Mocha open Expecto #endif +let createExampleAssays() = ResizeArray([|ArcAssay.init("Assay 1")|]) + +let getAssayIdentifiers(assays: ResizeArray) = assays |> Seq.map (fun a -> a.Identifier) |> ResizeArray + let private test_create = testList "create" [ testCase "constructor" <| fun _ -> @@ -20,7 +24,8 @@ let private test_create = let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] let studyDesignDescriptors = [|OntologyAnnotation.fromString("Design Descriptor")|] let tables = ResizeArray([|ArcTable.init("Table 1")|]) - let assays = ResizeArray([|ArcAssay.init("Assay 1")|]) + let assays = createExampleAssays() + let assay_identifiers = getAssayIdentifiers assays let factors = [|Factor.create("Factor 1")|] let comments = [|Comment.create("Comment 1")|] @@ -35,7 +40,7 @@ let private test_create = contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, - assays = assays, + assays = assay_identifiers, factors = factors, comments = comments ) @@ -49,7 +54,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assays "Assays" + Expect.equal actual.Assays assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" @@ -63,7 +68,8 @@ let private test_create = let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] let studyDesignDescriptors = [|OntologyAnnotation.fromString("Design Descriptor")|] let tables = ResizeArray([|ArcTable.init("Table 1")|]) - let assays = ResizeArray([|ArcAssay.init("Assay 1")|]) + let assays = createExampleAssays() + let assay_identifiers = getAssayIdentifiers assays let factors = [|Factor.create("Factor 1")|] let comments = [|Comment.create("Comment 1")|] @@ -77,7 +83,7 @@ let private test_create = contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, - assays = assays, + assays = assay_identifiers, factors = factors, comments = comments ) @@ -91,7 +97,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assays "Assays" + Expect.equal actual.Assays assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" @@ -120,7 +126,8 @@ let private test_create = let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] let studyDesignDescriptors = [|OntologyAnnotation.fromString("Design Descriptor")|] let tables = ResizeArray([|ArcTable.init("Table 1")|]) - let assays = ResizeArray([|ArcAssay.init("Assay 1")|]) + let assays = createExampleAssays() + let assay_identifiers = getAssayIdentifiers assays let factors = [|Factor.create("Factor 1")|] let comments = [|Comment.create("Comment 1")|] @@ -135,7 +142,7 @@ let private test_create = contacts studyDesignDescriptors tables - assays + assay_identifiers factors comments @@ -148,7 +155,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assays "Assays" + Expect.equal actual.Assays assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" ] @@ -158,21 +165,18 @@ let tests_copy = let _study_identifier = "My Study" let _study_description = Some "Lorem Ipsum" let _assay_identifier = "My Assay" - let _assay_technologyPlatform = OntologyAnnotation.fromString("Awesome Technology") let createTestStudy() = let s = ArcStudy(_study_identifier) s.Description <- _study_description - let a = ArcAssay(_assay_identifier, technologyPlatform = _assay_technologyPlatform) - s.AddAssay(a) + s.RegisterAssay(_assay_identifier) s testCase "ensure test study" <| fun _ -> let study = createTestStudy() Expect.equal study.Identifier _study_identifier "_study_identifier" Expect.equal study.Description _study_description "_study_description" Expect.equal study.AssayCount 1 "AssayCount" - let assay = study.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "_assay_identifier" - Expect.equal assay.TechnologyPlatform (Some _assay_technologyPlatform) "_assay_technologyPlatform" + let assayIdentifier = study.AssayIdentifiers.[0] + Expect.equal assayIdentifier _assay_identifier "_assay_identifier" testCase "test mutable fields" <| fun _ -> let newDesciption = Some "New Description" let newPublicReleaseDate = Some "01.01.2000" @@ -185,43 +189,15 @@ let tests_copy = Expect.equal study.Description _study_description "_study_description" Expect.equal study.PublicReleaseDate None "PublicReleaseDate" Expect.equal study.AssayCount 1 "AssayCount" - let assay = study.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "_assay_identifier" - Expect.equal assay.TechnologyPlatform (Some _assay_technologyPlatform) "_assay_technologyPlatform" + let assayIdentifier = study.AssayIdentifiers.[0] + Expect.equal assayIdentifier _assay_identifier "_assay_identifier" let checkCopy = Expect.equal copy.Identifier _study_identifier "copy _study_identifier" Expect.equal copy.Description newDesciption "copy _study_description" Expect.equal copy.PublicReleaseDate newPublicReleaseDate "copy PublicReleaseDate" Expect.equal copy.AssayCount 1 "copy AssayCount" - let assay = copy.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "copy _assay_identifier" - Expect.equal assay.TechnologyPlatform (Some _assay_technologyPlatform) "copy _assay_technologyPlatform" - () - testCase "test mutable fields on assay children" <| fun _ -> - let newTechnologyPlatform = Some (OntologyAnnotation.fromString("New TechnologyPlatform")) - let newTechnologyType = Some <| OntologyAnnotation.fromString("nice technology type") - let study = createTestStudy() - let copy = study.Copy() - let copy_assay = copy.GetAssayAt(0) - copy_assay.TechnologyType <- newTechnologyType - copy_assay.TechnologyPlatform <- newTechnologyPlatform - let checkSourceStudy = - Expect.equal study.Identifier _study_identifier "_study_identifier" - Expect.equal study.Description _study_description "_study_description" - Expect.equal study.PublicReleaseDate None "PublicReleaseDate" - Expect.equal study.AssayCount 1 "AssayCount" - let assay = study.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "_assay_identifier" - Expect.equal assay.TechnologyPlatform (Some _assay_technologyPlatform) "_assay_technologyPlatform" - Expect.equal assay.TechnologyType None "TechnologyType" - let checkCopy = - Expect.equal copy.Identifier _study_identifier "copy _study_identifier" - Expect.equal copy.Description _study_description "copy _study_description" - Expect.equal copy.AssayCount 1 "copy AssayCount" - let assay = copy.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "copy _assay_identifier" - Expect.equal assay.TechnologyPlatform newTechnologyPlatform "copy _assay_technologyPlatform" - Expect.equal assay.TechnologyType newTechnologyType "TechnologyType" + let assayIdentifier = study.AssayIdentifiers.[0] + Expect.equal assayIdentifier _assay_identifier "copy _assay_identifier" () ] diff --git a/tests/ISA/ISA.Tests/Fable.Tests.fs b/tests/ISA/ISA.Tests/Fable.Tests.fs index db5e0623..9509692f 100644 --- a/tests/ISA/ISA.Tests/Fable.Tests.fs +++ b/tests/ISA/ISA.Tests/Fable.Tests.fs @@ -23,7 +23,7 @@ let private tests_EmptyObjectCreation = Expect.isNone s.Description "Should be None" ) testCase "MakeEmptyInvestigation" (fun () -> - let i = ArcInvestigation.make "My Identifier" None None None None [||] [||] [||] (ResizeArray()) [||] [||] + let i = ArcInvestigation.make "My Identifier" None None None None [||] [||] [||] (ResizeArray()) (ResizeArray()) [||] [||] Expect.isNone i.Description "Should be None" Expect.equal i.Identifier "My Identifier" "Should be None" ) From bb2a84c8d073883d81d78d8f3307c122d42066bf Mon Sep 17 00:00:00 2001 From: Kevin F Date: Thu, 7 Sep 2023 17:16:22 +0200 Subject: [PATCH 04/17] Add ArcStudy assay handling tests :white_check_mark: --- src/ISA/ISA/ArcTypes/ArcTypes.fs | 14 ++++++++-- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 40 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 541d4ce6..6730cf06 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -11,7 +11,7 @@ module ArcTypesAux = | None -> failwith "Cannot execute this function. Object is not part of ArcInvestigation." | Some i -> i - let inline validateAssayRegister (assayIdent: string) (existingAssayIdents: seq) = + let inline validateAssayRegisterInInvestigation (assayIdent: string) (existingAssayIdents: seq) = match existingAssayIdents |> Seq.tryFind (fun x -> x = assayIdent) with | None -> failwith $"The given assay must be added to Investigation before it can be registered." @@ -608,6 +608,16 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi copy.DeregisterAssay(assayIdentifier) copy + member this.GetRegisteredAssay(assayIdentifier: string) = + if Seq.contains assayIdentifier this.Assays |> not then failwith $"Assay `{assayIdentifier}` is not registered on the study." + let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation + inv.GetAssay(assayIdentifier) + + static member getRegisteredAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.GetRegisteredAssay(assayIdentifier) + member this.GetRegisteredAssays() = let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation let assays = ResizeArray() @@ -1305,7 +1315,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin /// member this.RegisterAssayAt(studyIndex: int, assayIdentifier: string) = let study = this.GetStudyAt(studyIndex) - ArcTypesAux.SanityChecks.validateAssayRegister assayIdentifier (this.Assays |> Seq.map (fun a -> a.Identifier)) + ArcTypesAux.SanityChecks.validateAssayRegisterInInvestigation assayIdentifier (this.Assays |> Seq.map (fun a -> a.Identifier)) ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assayIdentifier study.Assays study.RegisterAssay(assayIdentifier) diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 3ed5183c..ff80053b 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -160,6 +160,44 @@ let private test_create = Expect.equal actual.Comments comments "Comments" ] +let tests_RegisteredAssays = testList "RegisteredAssays" [ + let _study_identifier = "My Study" + let _study_description = Some "Lorem Ipsum" + let _assay_identifier = "My Assay" + let createTestStudy() = + let s = ArcStudy(_study_identifier) + s.Description <- _study_description + s + testCase "RegisterAssay, no parent" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + Expect.equal study.AssayCount 1 "registered assay count" + testCase "GetRegisteredAssay, no parent" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + let eval() = study.GetRegisteredAssay(_assay_identifier) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "GetRegisteredAssays, no parent" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + let eval() = study.GetRegisteredAssays() |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "DeregisterAssay, no parent" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + Expect.equal study.AssayCount 1 "registered assay count" + study.DeregisterAssay(_assay_identifier) + Expect.equal study.AssayCount 0 "registered assay count 2" + testCase "InitAssay, no parent" <| fun _ -> + let study = createTestStudy() + let eval() = study.InitAssay(_assay_identifier) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "AddAssay, no parent" <| fun _ -> + let study = createTestStudy() + let eval() = study.AddAssay(ArcAssay(_assay_identifier)) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." +] + let tests_copy = testList "copy" [ let _study_identifier = "My Study" @@ -201,9 +239,11 @@ let tests_copy = () ] + let main = testList "ArcStudy" [ tests_copy + tests_RegisteredAssays test_create ] From 923eb2dff5f73160294c997ece7dd257dac1d731 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Fri, 8 Sep 2023 08:51:14 +0200 Subject: [PATCH 05/17] Add more tests :white_check_mark: --- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 131 ++++++++++++++++++++------ 1 file changed, 103 insertions(+), 28 deletions(-) diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index ff80053b..a2ba3383 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -168,34 +168,109 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ let s = ArcStudy(_study_identifier) s.Description <- _study_description s - testCase "RegisterAssay, no parent" <| fun _ -> - let study = createTestStudy() - study.RegisterAssay(_assay_identifier) - Expect.equal study.AssayCount 1 "registered assay count" - testCase "GetRegisteredAssay, no parent" <| fun _ -> - let study = createTestStudy() - study.RegisterAssay(_assay_identifier) - let eval() = study.GetRegisteredAssay(_assay_identifier) |> ignore - Expect.throws eval "throws as single study has no parent, therefore no access to full assays." - testCase "GetRegisteredAssays, no parent" <| fun _ -> - let study = createTestStudy() - study.RegisterAssay(_assay_identifier) - let eval() = study.GetRegisteredAssays() |> ignore - Expect.throws eval "throws as single study has no parent, therefore no access to full assays." - testCase "DeregisterAssay, no parent" <| fun _ -> - let study = createTestStudy() - study.RegisterAssay(_assay_identifier) - Expect.equal study.AssayCount 1 "registered assay count" - study.DeregisterAssay(_assay_identifier) - Expect.equal study.AssayCount 0 "registered assay count 2" - testCase "InitAssay, no parent" <| fun _ -> - let study = createTestStudy() - let eval() = study.InitAssay(_assay_identifier) |> ignore - Expect.throws eval "throws as single study has no parent, therefore no access to full assays." - testCase "AddAssay, no parent" <| fun _ -> - let study = createTestStudy() - let eval() = study.AddAssay(ArcAssay(_assay_identifier)) |> ignore - Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + let createTestAssay() = + ArcAssay.init(_assay_identifier) + testList "no parent" [ + testCase "RegisterAssay" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + Expect.equal study.AssayCount 1 "registered assay count" + testCase "GetRegisteredAssay" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + let eval() = study.GetRegisteredAssay(_assay_identifier) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "GetRegisteredAssays" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + let eval() = study.GetRegisteredAssays() |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "DeregisterAssay" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + Expect.equal study.AssayCount 1 "registered assay count" + study.DeregisterAssay(_assay_identifier) + Expect.equal study.AssayCount 0 "registered assay count 2" + testCase "InitAssay" <| fun _ -> + let study = createTestStudy() + let eval() = study.InitAssay(_assay_identifier) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + testCase "AddAssay" <| fun _ -> + let study = createTestStudy() + let eval() = study.AddAssay(ArcAssay(_assay_identifier)) |> ignore + Expect.throws eval "throws as single study has no parent, therefore no access to full assays." + ] + testList "with parent" [ + testCase "RegisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let assay = createTestAssay() + let study = createTestStudy() + i.AddStudy(study) + i.AddAssay(assay) + study.RegisterAssay(_assay_identifier) + Expect.equal i.AssayCount 1 "AssayCount" + Expect.equal i.StudyCount 1 "StudyCount" + Expect.equal study.AssayCount 1 "registered assay count" + testCase "GetRegisteredAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let assay = createTestAssay() + let study = createTestStudy() + i.AddStudy(study) + i.AddAssay(assay) + study.RegisterAssay(_assay_identifier) + let actual = study.GetRegisteredAssay(_assay_identifier) + Expect.equal actual assay "equal" + testCase "GetRegisteredAssays" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let assay = createTestAssay() + let study = createTestStudy() + i.AddStudy(study) + i.AddAssay(assay) + study.RegisterAssay(_assay_identifier) + let actual = study.GetRegisteredAssays() + Expect.equal actual.[0] assay "equal" + testCase "DeregisterAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let assay = createTestAssay() + let study = createTestStudy() + i.AddStudy(study) + i.AddAssay(assay) + study.RegisterAssay(_assay_identifier) + study.DeregisterAssay(_assay_identifier) + Expect.equal study.AssayCount 0 "registered assay count 2" + Expect.equal i.AssayCount 1 "AssayCount, only deregister from study, not remove" + Expect.equal i.StudyCount 1 "StudyCount" + testCase "InitAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let study = createTestStudy() + i.AddStudy(study) + let assay = study.InitAssay(_assay_identifier) + Expect.equal i.AssayCount 1 "AssayCount" + Expect.equal i.StudyCount 1 "StudyCount" + Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal i.Assays.[0] assay "equal" + testCase "AddAssay" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let study = createTestStudy() + i.AddStudy(study) + let assay = ArcAssay.init(_assay_identifier) + study.AddAssay(assay) + Expect.equal i.AssayCount 1 "AssayCount" + Expect.equal i.StudyCount 1 "StudyCount" + Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal i.Assays.[0] assay "equal" + testCase "Check mutability" <| fun _ -> + let i = ArcInvestigation.init("MyInvestigation") + let study = createTestStudy() + i.AddStudy(study) + let assay = ArcAssay.init(_assay_identifier) + study.AddAssay(assay) + let assayFromInv = i.Assays.[0] + let table = assayFromInv.InitTable("MyNewTable") + Expect.equal assayFromInv assay "equal" + Expect.equal assayFromInv.TableCount 1 "assayFromInv.TableCount" + Expect.equal assay.TableCount 1 "assay.TableCount" + ] ] let tests_copy = From a1555284ee0c173348aff955a9ce18b8e1b022da Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Fri, 8 Sep 2023 11:14:39 +0200 Subject: [PATCH 06/17] add registered studies to investigation --- src/ISA/ISA/ArcTypes/ArcTypes.fs | 191 ++++++++++++------ tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 18 +- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 24 +-- 3 files changed, 149 insertions(+), 84 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 6730cf06..45fffe74 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -18,6 +18,20 @@ module ArcTypesAux = | Some _ -> () + let inline validateExistingStudyRegisterInInvestigation (studyIdent: string) (existingStudyIdents: seq) = + match existingStudyIdents |> Seq.tryFind (fun x -> x = studyIdent) with + | None -> + failwith $"The given study with identifier '{studyIdent}' must be added to Investigation before it can be registered." + | Some _ -> + () + + let inline validateUniqueRegisteredStudyIdentifiers (studyIdent: string) (studyIdents: seq) = + match studyIdents |> Seq.contains studyIdent with + | true -> + failwith $"Study with identifier '{studyIdent}' is already registered!" + | false -> + () + let inline validateUniqueAssayIdentifier (assayIdent: string) (existingAssayIdents: seq) = match existingAssayIdents |> Seq.tryFindIndex (fun x -> x = assayIdent) with | Some i -> @@ -39,10 +53,10 @@ module ArcTypesAux = let inline removeMissingRegisteredAssays (inv: ArcInvestigation) : unit = let existingAssays = inv.AssayIdentifiers for study in inv.Studies do - let registeredAssays = study.AssayIdentifiers + let registeredAssays = study.RegisteredAssayIdentifiers |> Seq.map id for registeredAssay in registeredAssays do if Seq.contains registeredAssay existingAssays |> not then - study.Assays.Remove registeredAssay |> ignore + study.DeregisterAssay registeredAssay |> ignore [] @@ -117,7 +131,7 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno static member initTable(tableName: string, ?index: int) = fun (assay:ArcAssay) -> let c = assay.Copy() - c.InitTable(tableName, ?index=index) + c,c.InitTable(tableName, ?index=index) // - Table API - // @@ -452,7 +466,9 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno member internal this.RemoveFromInvestigation () = this.Investigation <- None - /// Transform an ArcAssay to an ISA Json Assay. + /// Copies ArcAssay object without the pointer to the parent ArcInvestigation + /// + /// In order to copy the pointer to the parent ArcInvestigation as well, use the Copy() method of the ArcInvestigation instead. member this.ToAssay() : Assay = let processSeq = ArcTables(this.Tables).GetProcesses() let assayMaterials = @@ -496,12 +512,12 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno ) [] -type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays: ResizeArray, ?factors, ?comments) = +type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?registeredAssayIdentifiers: ResizeArray, ?factors, ?comments) = let publications = defaultArg publications [||] let contacts = defaultArg contacts [||] let studyDesignDescriptors = defaultArg studyDesignDescriptors [||] let tables = defaultArg tables <| ResizeArray() - let assays = defaultArg assays <| ResizeArray() + let registeredAssayIdentifiers = defaultArg registeredAssayIdentifiers <| ResizeArray() let factors = defaultArg factors [||] let comments = defaultArg comments [||] @@ -524,17 +540,17 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi member val Contacts : Person [] = contacts with get, set member val StudyDesignDescriptors : OntologyAnnotation [] = studyDesignDescriptors with get, set member val Tables : ResizeArray = tables with get, set - member val Assays : ResizeArray = assays with get, set + member val RegisteredAssayIdentifiers : ResizeArray = registeredAssayIdentifiers with get, set member val Factors : Factor [] = factors with get, set member val Comments : Comment []= comments with get, set static member init(identifier : string) = ArcStudy identifier - static member create(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?assays, ?factors, ?comments) = - ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?publications = publications, ?contacts = contacts, ?studyDesignDescriptors = studyDesignDescriptors, ?tables = tables, ?assays = assays, ?factors = factors, ?comments = comments) + static member create(identifier : string, ?title, ?description, ?submissionDate, ?publicReleaseDate, ?publications, ?contacts, ?studyDesignDescriptors, ?tables, ?registeredAssayIdentifiers, ?factors, ?comments) = + ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?publications = publications, ?contacts = contacts, ?studyDesignDescriptors = studyDesignDescriptors, ?tables = tables, ?registeredAssayIdentifiers = registeredAssayIdentifiers, ?factors = factors, ?comments = comments) - static member make identifier title description submissionDate publicReleaseDate publications contacts studyDesignDescriptors tables assays factors comments = - ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, publications = publications, contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, assays = assays, factors = factors, comments = comments) + static member make identifier title description submissionDate publicReleaseDate publications contacts studyDesignDescriptors tables registeredAssayIdentifiers factors comments = + ArcStudy(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, publications = publications, contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, registeredAssayIdentifiers = registeredAssayIdentifiers, factors = factors, comments = comments) /// /// Returns true if all fields are None/ empty sequences **except** Identifier. @@ -549,7 +565,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi (this.Contacts = [||]) && (this.StudyDesignDescriptors = [||]) && (this.Tables.Count = 0) && - (this.Assays.Count = 0) && + (this.RegisteredAssayIdentifiers.Count = 0) && (this.Factors = [||]) && (this.Comments = [||]) @@ -558,10 +574,16 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi //member this.FileName = ArcStudy.FileName member this.AssayCount - with get() = this.Assays.Count + with get() = this.RegisteredAssayIdentifiers.Count - member this.AssayIdentifiers - with get(): string [] = Array.ofSeq this.Assays + member this.Assays + with get(): ResizeArray = + let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation + let assays = ResizeArray() + for assay in inv.Assays do + if Seq.contains assay.Identifier this.RegisteredAssayIdentifiers then + assays.Add assay + assays // - Assay API - CRUD // /// @@ -587,11 +609,12 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi static member initAssay(assayIdentifier: string) = fun (study:ArcStudy) -> - let newStudy = study.Copy() - newStudy.InitAssay(assayIdentifier) + let copy = study.Copy() + copy,copy.InitAssay(assayIdentifier) member this.RegisterAssay(assayIdentifier: string) = - this.Assays.Add(assayIdentifier) + if Seq.contains assayIdentifier this.RegisteredAssayIdentifiers then failwith $"Assay `{assayIdentifier}` is already registered on the study." + this.RegisteredAssayIdentifiers.Add(assayIdentifier) static member registerAssay(assayIdentifier: string) = fun (study: ArcStudy) -> @@ -600,7 +623,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi copy member this.DeregisterAssay(assayIdentifier: string) = - this.Assays.Remove(assayIdentifier) |> ignore + this.RegisteredAssayIdentifiers.Remove(assayIdentifier) |> ignore static member deregisterAssay(assayIdentifier: string) = fun (study: ArcStudy) -> @@ -608,28 +631,21 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi copy.DeregisterAssay(assayIdentifier) copy - member this.GetRegisteredAssay(assayIdentifier: string) = - if Seq.contains assayIdentifier this.Assays |> not then failwith $"Assay `{assayIdentifier}` is not registered on the study." + member this.GetAssay(assayIdentifier: string) = + if Seq.contains assayIdentifier this.RegisteredAssayIdentifiers |> not then failwith $"Assay `{assayIdentifier}` is not registered on the study." let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation inv.GetAssay(assayIdentifier) - static member getRegisteredAssay(assayIdentifier: string) = + static member getAssay(assayIdentifier: string) = fun (study: ArcStudy) -> let copy = study.Copy() - copy.GetRegisteredAssay(assayIdentifier) - - member this.GetRegisteredAssays() = - let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation - let assays = ResizeArray() - for assay in inv.Assays do - if Seq.contains assay.Identifier this.AssayIdentifiers then - assays.Add assay - assays + copy.GetAssay(assayIdentifier) - static member getRegisteredAssays() = + + static member getAssays() = fun (study: ArcStudy) -> let copy = study.Copy() - copy.GetRegisteredAssays() + copy.Assays /// /// Returns ArcAssays registered in study, or if no parent exists, initializies new ArcAssay from identifier. @@ -640,12 +656,13 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi // 2. Get full assays from ArcInvestigation parent. match this.Investigation with | Some i -> - this.GetRegisteredAssays() + this.Assays | None -> - this.Assays |> Seq.map (fun x -> ArcAssay.init(x)) |> ResizeArray - |> Seq.toList - |> List.map (fun a -> a.ToAssay()) + this.RegisteredAssayIdentifiers |> Seq.map (fun identifier -> ArcAssay.init(identifier)) |> ResizeArray + /// + /// Returns ArcAssays registered in study, or if no parent exists, initializies new ArcAssay from identifier. + /// static member getRegisteredAssaysOrIdentifier() = fun (study: ArcStudy) -> let copy = study.Copy() @@ -686,7 +703,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi static member initTable(tableName: string, ?index: int) = fun (study:ArcStudy) -> let c = study.Copy() - c.InitTable(tableName, ?index=index) + c,c.InitTable(tableName, ?index=index) // - Table API - // @@ -970,9 +987,15 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi member internal this.RemoveFromInvestigation () = this.Investigation <- None + + /// Copies ArcStudy objec without the pointer to the parent ArcInvestigation + /// + /// This copy does only contain the identifiers of the registered ArcAssays and not the actual objects. + /// + /// In order to copy the ArcAssays as well, use the Copy() method of the ArcInvestigation. member this.Copy() : ArcStudy = let nextTables = ResizeArray() - let nextAssays = ResizeArray(this.Assays) + let nextAssayIdentifiers = ResizeArray(this.RegisteredAssayIdentifiers) for table in this.Tables do let copy = table.Copy() nextTables.Add(copy) @@ -991,13 +1014,16 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi contacts = nextContacts, studyDesignDescriptors = nextStudyDesignDescriptors, tables = nextTables, - assays = nextAssays, + registeredAssayIdentifiers = nextAssayIdentifiers, factors = nextFactors, comments = nextComments ) - /// Transform an ArcStudy to an ISA Json Study. - member this.ToStudy(arcAssays: ResizeArray) : Study = + /// + /// Creates an ISA-Json compatible Study from ArcStudy. + /// + /// If this parameter is given, will transform these ArcAssays to Assays and include them as children of the Study. If not, tries to get them from the parent ArcInvestigation instead. If ArcStudy has no parent ArcInvestigation either, initializes new ArcAssay from registered Identifiers. + member this.ToStudy(?arcAssays: ResizeArray) : Study = let processSeq = ArcTables(this.Tables).GetProcesses() let protocols = ProcessSequence.getProtocols processSeq |> Option.fromValueWithDefault [] let studyMaterials = @@ -1012,7 +1038,9 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi None, None else Some this.Identifier, Some (Identifier.Study.fileNameFromIdentifier this.Identifier) - let assays = arcAssays |> List.ofSeq |> List.map (fun a -> a.ToAssay()) + let assays = + arcAssays |> Option.defaultValue (this.GetRegisteredAssaysOrIdentifier()) + |> List.ofSeq |> List.map (fun a -> a.ToAssay()) Study.create( ?FileName = fileName, ?Identifier = identifier, @@ -1052,20 +1080,21 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ?contacts = (s.Contacts|> Option.map Array.ofList), ?studyDesignDescriptors = (s.StudyDesignDescriptors |> Option.map Array.ofList), ?tables = tables, - ?assays = Some assaysIdentifiers, + ?registeredAssayIdentifiers = Some assaysIdentifiers, ?factors = (s.Factors |> Option.map Array.ofList), ?comments = (s.Comments |> Option.map Array.ofList) ), assays [] -type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = +type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?registeredStudyIdentifiers : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = let ontologySourceReferences = defaultArg ontologySourceReferences [||] let publications = defaultArg publications [||] let contacts = defaultArg contacts [||] let assays = defaultArg assays (ResizeArray()) let studies = defaultArg studies (ResizeArray()) + let registeredStudyIdentifiers = defaultArg registeredStudyIdentifiers (ResizeArray()) let comments = defaultArg comments [||] let remarks = defaultArg remarks [||] @@ -1084,17 +1113,18 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin member val Contacts : Person [] = contacts with get, set member val Assays : ResizeArray = assays with get, set member val Studies : ResizeArray = studies with get, set + member val RegisteredStudyIdentifiers : ResizeArray = registeredStudyIdentifiers with get, set member val Comments : Comment [] = comments with get, set member val Remarks : Remark [] = remarks with get, set static member FileName = ARCtrl.Path.InvestigationFileName static member init(identifier: string) = ArcInvestigation identifier - static member create(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = - ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?ontologySourceReferences = ontologySourceReferences, ?publications = publications, ?contacts = contacts, ?assays = assays, ?studies = studies, ?comments = comments, ?remarks = remarks) + static member create(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray,?registeredStudyIdentifiers : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = + ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ?ontologySourceReferences = ontologySourceReferences, ?publications = publications, ?contacts = contacts, ?assays = assays, ?studies = studies, ?registeredStudyIdentifiers = registeredStudyIdentifiers, ?comments = comments, ?remarks = remarks) - static member make (identifier : string) (title : string option) (description : string option) (submissionDate : string option) (publicReleaseDate : string option) (ontologySourceReferences : OntologySourceReference []) (publications : Publication []) (contacts : Person []) (assays: ResizeArray) (studies : ResizeArray) (comments : Comment []) (remarks : Remark []) : ArcInvestigation = - ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, assays = assays, studies = studies, comments = comments, remarks = remarks) + static member make (identifier : string) (title : string option) (description : string option) (submissionDate : string option) (publicReleaseDate : string option) (ontologySourceReferences : OntologySourceReference []) (publications : Publication []) (contacts : Person []) (assays: ResizeArray) (studies : ResizeArray) (registeredStudyIdentifiers : ResizeArray) (comments : Comment []) (remarks : Remark []) : ArcInvestigation = + ArcInvestigation(identifier, ?title = title, ?description = description, ?submissionDate = submissionDate, ?publicReleaseDate = publicReleaseDate, ontologySourceReferences = ontologySourceReferences, publications = publications, contacts = contacts, assays = assays, studies = studies, registeredStudyIdentifiers = registeredStudyIdentifiers, comments = comments, remarks = remarks) member this.AssayCount with get() = this.Assays.Count @@ -1105,6 +1135,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin // - Assay API - CRUD // member this.AddAssay(assay: ArcAssay) = ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assay.Identifier (this.Assays |> Seq.map (fun x -> x.Identifier)) + assay.Investigation <- Some(this) this.Assays.Add(assay) static member addAssay(assay: ArcAssay) = @@ -1157,6 +1188,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin // - Assay API - CRUD // member this.SetAssayAt(index: int, assay: ArcAssay) = ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assay.Identifier (this.Assays |> Seq.removeAt index |> Seq.map (fun a -> a.Identifier)) + assay.Investigation <- Some(this) this.Assays.[index] <- assay this.DeregisterMissingAssays() @@ -1205,6 +1237,12 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin let newInvestigation = inv.Copy() newInvestigation.GetAssay(assayIdentifier) + member this.RegisteredStudies + with get() = + this.RegisteredStudyIdentifiers + |> Seq.map (fun identifier -> this.GetStudy identifier) + |> ResizeArray + member this.StudyCount with get() = this.Studies.Count @@ -1224,15 +1262,27 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin copy // - Study API - CRUD // - member this.InitStudy (studyName: string) = - let study = ArcStudy.init(studyName) + member this.InitStudy (studyIdentifier: string) = + let study = ArcStudy.init(studyIdentifier) this.AddStudy(study) study - static member initStudy(studyName: string) = + static member initStudy(studyIdentifier: string) = fun (inv: ArcInvestigation) -> let copy = inv.Copy() - copy.InitStudy(studyName) + copy,copy.InitStudy(studyIdentifier) + + // - Study API - CRUD // + member this.RegisterStudy (studyIdentifier : string) = + ArcTypesAux.SanityChecks.validateExistingStudyRegisterInInvestigation studyIdentifier this.StudyIdentifiers + ArcTypesAux.SanityChecks.validateUniqueRegisteredStudyIdentifiers studyIdentifier this.RegisteredStudyIdentifiers + this.RegisteredStudyIdentifiers.Add(studyIdentifier) + + static member registerStudy(studyIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + copy.RegisterStudy(studyIdentifier) + copy // - Study API - CRUD // member this.RemoveStudyAt(index: int) = @@ -1259,6 +1309,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin // - Study API - CRUD // member this.SetStudyAt(index: int, study: ArcStudy) = ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) + study.Investigation <- Some this this.Studies.[index] <- study static member setStudyAt(index: int, study: ArcStudy) = @@ -1270,8 +1321,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin // - Study API - CRUD // member this.SetStudy(studyIdentifier: string, study: ArcStudy) = let index = this.GetStudyIndex studyIdentifier - ArcTypesAux.SanityChecks.validateUniqueStudyIdentifier study (this.Studies |> Seq.removeAt index) - this.Studies.[index] <- study + this.SetStudyAt(index,study) static member setStudy(studyIdentifier: string, study: ArcStudy) = fun (inv: ArcInvestigation) -> @@ -1316,7 +1366,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin member this.RegisterAssayAt(studyIndex: int, assayIdentifier: string) = let study = this.GetStudyAt(studyIndex) ArcTypesAux.SanityChecks.validateAssayRegisterInInvestigation assayIdentifier (this.Assays |> Seq.map (fun a -> a.Identifier)) - ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assayIdentifier study.Assays + ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assayIdentifier study.RegisteredAssayIdentifiers study.RegisterAssay(assayIdentifier) static member registerAssayAt(studyIndex: int, assayIdentifier: string) = @@ -1390,25 +1440,32 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin let nextContacts = this.Contacts |> Array.map (fun c -> c.Copy()) let nextPublications = this.Publications |> Array.map (fun c -> c.Copy()) let nextOntologySourceReferences = this.OntologySourceReferences |> Array.map (fun c -> c.Copy()) - ArcInvestigation( + let nextStudyIdentifiers = ResizeArray(this.RegisteredStudyIdentifiers) + let i = ArcInvestigation( this.Identifier, ?title = this.Title, ?description = this.Description, ?submissionDate = this.SubmissionDate, ?publicReleaseDate = this.PublicReleaseDate, + registeredStudyIdentifiers = nextStudyIdentifiers, ontologySourceReferences = nextOntologySourceReferences, publications = nextPublications, contacts = nextContacts, - assays = nextAssays, - studies = nextStudies, // correct mutable behaviour is tested on this field comments = nextComments, remarks = nextRemarks ) + // This is necessary, so the studies and assays know which investigation they belong to. + for study in nextStudies do + i.AddStudy study + for assay in nextAssays do + i.AddAssay assay + + i /// Transform an ArcInvestigation to an ISA Json Investigation. member this.ToInvestigation() : Investigation = - let studies = this.Studies |> Seq.toList |> List.map (fun a -> a.ToStudy(a.GetRegisteredAssays())) |> Option.fromValueWithDefault [] + let studies = this.RegisteredStudies |> Seq.toList |> List.map (fun a -> a.ToStudy()) |> Option.fromValueWithDefault [] let identifier = if ARCtrl.ISA.Identifier.isMissingIdentifier this.Identifier then None else Some this.Identifier @@ -1438,15 +1495,21 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin |> List.unzip let studies = ResizeArray(studiesRaw) let assays = assaysRaw |> Seq.concat |> Seq.distinctBy (fun a -> a.Identifier) |> ResizeArray - ArcInvestigation.create( + let i = ArcInvestigation.create( identifer, ?title = i.Title, ?description = i.Description, ?submissionDate = i.SubmissionDate, ?publicReleaseDate = i.PublicReleaseDate, ?publications = (i.Publications |> Option.map Array.ofList), - ?contacts = (i.Contacts |> Option.map Array.ofList), - ?assays = Some assays, - ?studies = Some studies, + ?contacts = (i.Contacts |> Option.map Array.ofList), ?comments = (i.Comments |> Option.map Array.ofList) ) + + // This is necessary, so the studies and assays know which investigation they belong to. + for study in studies do + i.AddStudy study + for assay in assays do + i.AddAssay assay + + i \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 0d0e4df5..040d3590 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -129,6 +129,7 @@ let private test_create = let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] let assays = create_ExampleAssays() let studies = ResizeArray([|ArcStudy.init("Study 1")|]) + let registeredStudyIdentifiers = ResizeArray(["Study 1"]) let comments = [|Comment.create("Comment 1")|] let remarks = [|Remark.create(1, "Remark 1")|] @@ -144,6 +145,7 @@ let private test_create = contacts assays studies + registeredStudyIdentifiers comments remarks @@ -170,7 +172,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" i.RegisterAssay(s.Identifier, a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" testCase "Study.RegisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -179,7 +181,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" testCase "Investigation.DeregisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -188,7 +190,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" i.RegisterAssay(s.Identifier, a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.DeregisterAssay(s.Identifier,a.Identifier) Expect.equal s.AssayCount 0 "registered assay count 2" testCase "Study.DeregisterAssay" <| fun _ -> @@ -199,7 +201,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" s.DeregisterAssay(a.Identifier) Expect.equal s.AssayCount 0 "registered assay count 2" testCase "Remove registered assay from investigation" <| fun _ -> @@ -210,7 +212,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.RemoveAssayAt 0 Expect.equal i.AssayCount 0 "assay count 2" Expect.equal s.AssayCount 0 "registered assay count 2" @@ -222,7 +224,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) Expect.equal s.AssayCount 1 "registered assay count" - Expect.equal s.Assays.[0] a.Identifier "identifier" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.Assays <- (ResizeArray()) Expect.equal i.AssayCount 0 "assay count 2" Expect.equal s.AssayCount 1 "registered assay count 2" @@ -254,8 +256,8 @@ let tests_MutableFields = testList "MutableFields" [ Expect.equal i.StudyCount 1 "study count" Expect.equal study.AssayCount 1 "registered assay count" Expect.equal assay.TableCount 0 "assay table count" - Expect.equal study.Assays.[0] assay.Identifier "registered assay identifier" - let registeredAssay = study.GetRegisteredAssays().[0] + Expect.equal study.RegisteredAssayIdentifiers.[0] assay.Identifier "registered assay identifier" + let registeredAssay = study.Assays.[0] Expect.equal registeredAssay.Identifier assay.Identifier "full registered assay identifier" let table = registeredAssay.InitTable("My New Table") Expect.equal assay.TableCount 1 "table count to init table" diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index a2ba3383..3b9c68d5 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -40,7 +40,7 @@ let private test_create = contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, - assays = assay_identifiers, + registeredAssayIdentifiers = assay_identifiers, factors = factors, comments = comments ) @@ -54,7 +54,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assay_identifiers "Assays" + Expect.equal actual.RegisteredAssayIdentifiers assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" @@ -83,7 +83,7 @@ let private test_create = contacts = contacts, studyDesignDescriptors = studyDesignDescriptors, tables = tables, - assays = assay_identifiers, + registeredAssayIdentifiers = assay_identifiers, factors = factors, comments = comments ) @@ -97,7 +97,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assay_identifiers "Assays" + Expect.equal actual.RegisteredAssayIdentifiers assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" @@ -155,7 +155,7 @@ let private test_create = Expect.equal actual.Contacts contacts "Contacts" Expect.equal actual.StudyDesignDescriptors studyDesignDescriptors "StudyDesignDescriptors" Expect.equal actual.Tables tables "Tables" - Expect.equal actual.Assays assay_identifiers "Assays" + Expect.equal actual.RegisteredAssayIdentifiers assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" ] @@ -178,12 +178,12 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ testCase "GetRegisteredAssay" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - let eval() = study.GetRegisteredAssay(_assay_identifier) |> ignore + let eval() = study.GetAssay(_assay_identifier) |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." testCase "GetRegisteredAssays" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - let eval() = study.GetRegisteredAssays() |> ignore + let eval() = study.Assays |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." testCase "DeregisterAssay" <| fun _ -> let study = createTestStudy() @@ -218,7 +218,7 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ i.AddStudy(study) i.AddAssay(assay) study.RegisterAssay(_assay_identifier) - let actual = study.GetRegisteredAssay(_assay_identifier) + let actual = study.GetAssay(_assay_identifier) Expect.equal actual assay "equal" testCase "GetRegisteredAssays" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -227,7 +227,7 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ i.AddStudy(study) i.AddAssay(assay) study.RegisterAssay(_assay_identifier) - let actual = study.GetRegisteredAssays() + let actual = study.Assays Expect.equal actual.[0] assay "equal" testCase "DeregisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -288,7 +288,7 @@ let tests_copy = Expect.equal study.Identifier _study_identifier "_study_identifier" Expect.equal study.Description _study_description "_study_description" Expect.equal study.AssayCount 1 "AssayCount" - let assayIdentifier = study.AssayIdentifiers.[0] + let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "_assay_identifier" testCase "test mutable fields" <| fun _ -> let newDesciption = Some "New Description" @@ -302,14 +302,14 @@ let tests_copy = Expect.equal study.Description _study_description "_study_description" Expect.equal study.PublicReleaseDate None "PublicReleaseDate" Expect.equal study.AssayCount 1 "AssayCount" - let assayIdentifier = study.AssayIdentifiers.[0] + let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "_assay_identifier" let checkCopy = Expect.equal copy.Identifier _study_identifier "copy _study_identifier" Expect.equal copy.Description newDesciption "copy _study_description" Expect.equal copy.PublicReleaseDate newPublicReleaseDate "copy PublicReleaseDate" Expect.equal copy.AssayCount 1 "copy AssayCount" - let assayIdentifier = study.AssayIdentifiers.[0] + let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "copy _assay_identifier" () ] From de7f51f3aa95289686f49d981e5c553f34e29432 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Fri, 8 Sep 2023 12:05:21 +0200 Subject: [PATCH 07/17] Rename study.assay api and fix tests --- src/ISA/ISA/ArcTypes/ArcTypes.fs | 39 +++++++++--------- tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 40 +++++++++---------- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 40 +++++++++---------- tests/ISA/ISA.Tests/Fable.Tests.fs | 2 +- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 45fffe74..4f97154e 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -53,7 +53,7 @@ module ArcTypesAux = let inline removeMissingRegisteredAssays (inv: ArcInvestigation) : unit = let existingAssays = inv.AssayIdentifiers for study in inv.Studies do - let registeredAssays = study.RegisteredAssayIdentifiers |> Seq.map id + let registeredAssays = ResizeArray(study.RegisteredAssayIdentifiers) for registeredAssay in registeredAssays do if Seq.contains registeredAssay existingAssays |> not then study.DeregisterAssay registeredAssay |> ignore @@ -573,10 +573,10 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi static member FileName = ARCtrl.Path.StudyFileName //member this.FileName = ArcStudy.FileName - member this.AssayCount + member this.RegisteredAssayCount with get() = this.RegisteredAssayIdentifiers.Count - member this.Assays + member this.RegisteredAssays with get(): ResizeArray = let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation let assays = ResizeArray() @@ -590,28 +590,29 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi /// Add assay to investigation and register it to study. /// /// - member this.AddAssay(assay: ArcAssay) = + member this.AddRegisteredAssay(assay: ArcAssay) = let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation inv.AddAssay(assay) inv.RegisterAssay(this.Identifier,assay.Identifier) - static member addAssay(assay: ArcAssay) = + static member addRegisteredAssay(assay: ArcAssay) = fun (study:ArcStudy) -> let newStudy = study.Copy() - newStudy.AddAssay(assay) + newStudy.AddRegisteredAssay(assay) newStudy // - Assay API - CRUD // - member this.InitAssay(assayIdentifier: string) = + member this.InitRegisteredAssay(assayIdentifier: string) = let assay = ArcAssay(assayIdentifier) - this.AddAssay(assay) + this.AddRegisteredAssay(assay) assay - static member initAssay(assayIdentifier: string) = + static member initRegisteredAssay(assayIdentifier: string) = fun (study:ArcStudy) -> let copy = study.Copy() - copy,copy.InitAssay(assayIdentifier) + copy,copy.InitRegisteredAssay(assayIdentifier) + // - Assay API - CRUD // member this.RegisterAssay(assayIdentifier: string) = if Seq.contains assayIdentifier this.RegisteredAssayIdentifiers then failwith $"Assay `{assayIdentifier}` is already registered on the study." this.RegisteredAssayIdentifiers.Add(assayIdentifier) @@ -622,6 +623,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi copy.RegisterAssay(assayIdentifier) copy + // - Assay API - CRUD // member this.DeregisterAssay(assayIdentifier: string) = this.RegisteredAssayIdentifiers.Remove(assayIdentifier) |> ignore @@ -631,21 +633,22 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi copy.DeregisterAssay(assayIdentifier) copy - member this.GetAssay(assayIdentifier: string) = + // - Assay API - CRUD // + member this.GetRegisteredAssay(assayIdentifier: string) = if Seq.contains assayIdentifier this.RegisteredAssayIdentifiers |> not then failwith $"Assay `{assayIdentifier}` is not registered on the study." let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation inv.GetAssay(assayIdentifier) - static member getAssay(assayIdentifier: string) = + static member getRegisteredAssay(assayIdentifier: string) = fun (study: ArcStudy) -> let copy = study.Copy() - copy.GetAssay(assayIdentifier) + copy.GetRegisteredAssay(assayIdentifier) - - static member getAssays() = + // - Assay API - CRUD // + static member getRegisteredAssays() = fun (study: ArcStudy) -> let copy = study.Copy() - copy.Assays + copy.RegisteredAssays /// /// Returns ArcAssays registered in study, or if no parent exists, initializies new ArcAssay from identifier. @@ -656,7 +659,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi // 2. Get full assays from ArcInvestigation parent. match this.Investigation with | Some i -> - this.Assays + this.RegisteredAssays | None -> this.RegisteredAssayIdentifiers |> Seq.map (fun identifier -> ArcAssay.init(identifier)) |> ResizeArray @@ -1130,7 +1133,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin with get() = this.Assays.Count member this.AssayIdentifiers - with get(): seq = this.Assays |> Seq.map (fun (x:ArcAssay) -> x.Identifier) + with get(): string [] = this.Assays |> Seq.map (fun (x:ArcAssay) -> x.Identifier) |> Array.ofSeq // - Assay API - CRUD // member this.AddAssay(assay: ArcAssay) = diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 040d3590..86b351d4 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -163,7 +163,7 @@ let private test_create = Expect.equal actual.Remarks remarks "Remarks" ] -let test_RegisteredAssays = testList "RegisteredAssay" [ +let test_RegisteredAssays = testList "RegisteredAssays" [ testCase "Investigation.RegisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -171,7 +171,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" i.RegisterAssay(s.Identifier, a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" testCase "Study.RegisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -180,7 +180,7 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" testCase "Investigation.DeregisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -189,10 +189,10 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" i.RegisterAssay(s.Identifier, a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.DeregisterAssay(s.Identifier,a.Identifier) - Expect.equal s.AssayCount 0 "registered assay count 2" + Expect.equal s.RegisteredAssayCount 0 "registered assay count 2" testCase "Study.DeregisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -200,10 +200,10 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" s.DeregisterAssay(a.Identifier) - Expect.equal s.AssayCount 0 "registered assay count 2" + Expect.equal s.RegisteredAssayCount 0 "registered assay count 2" testCase "Remove registered assay from investigation" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -211,11 +211,11 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.RemoveAssayAt 0 Expect.equal i.AssayCount 0 "assay count 2" - Expect.equal s.AssayCount 0 "registered assay count 2" + Expect.equal s.RegisteredAssayCount 0 "registered assay count 2" testCase "Investigation.DeregisterMissingAssays" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let a = i.InitAssay("MyAssay") @@ -223,13 +223,13 @@ let test_RegisteredAssays = testList "RegisteredAssay" [ Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" s.RegisterAssay(a.Identifier) - Expect.equal s.AssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayCount 1 "registered assay count" Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" i.Assays <- (ResizeArray()) Expect.equal i.AssayCount 0 "assay count 2" - Expect.equal s.AssayCount 1 "registered assay count 2" + Expect.equal s.RegisteredAssayCount 1 "registered assay count 2" i.DeregisterMissingAssays() - Expect.equal s.AssayCount 0 "registered assay count 3" + Expect.equal s.RegisteredAssayCount 0 "registered assay count 3" ] let tests_MutableFields = testList "MutableFields" [ @@ -254,10 +254,10 @@ let tests_MutableFields = testList "MutableFields" [ study.RegisterAssay(assay.Identifier) Expect.equal i.AssayCount 1 "assay count" Expect.equal i.StudyCount 1 "study count" - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" Expect.equal assay.TableCount 0 "assay table count" Expect.equal study.RegisteredAssayIdentifiers.[0] assay.Identifier "registered assay identifier" - let registeredAssay = study.Assays.[0] + let registeredAssay = study.RegisteredAssays.[0] Expect.equal registeredAssay.Identifier assay.Identifier "full registered assay identifier" let table = registeredAssay.InitTable("My New Table") Expect.equal assay.TableCount 1 "table count to init table" @@ -417,7 +417,7 @@ let tests_Assay = testList "CRUD Assay" [ TestingUtils.mySequenceEqual i.StudyIdentifiers (seq {"Study 1"; "Study 2"}) "StudyIdentifiers" TestingUtils.mySequenceEqual i.AssayIdentifiers (seq {"Assay 1"; "Assay 2"}) "AssayIdentifiers" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 2 "Registered AssayCount" + Expect.equal s.RegisteredAssayCount 2 "Registered AssayCount" let s2 = i.Studies.Item 1 Expect.equal s2.Title (Some "Study 2 Title") "Study 2 Title" testList "AddAssay" [ @@ -430,7 +430,7 @@ let tests_Assay = testList "CRUD Assay" [ i.RegisterAssayAt(0, expected.Identifier) Expect.equal i.StudyCount 2 "StudyCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 3 "AssayCount" + Expect.equal s.RegisteredAssayCount 3 "AssayCount" let actual = i.GetAssayAt 2 Expect.equal actual expected "equal" testCase "by identifier" <| fun _ -> @@ -442,7 +442,7 @@ let tests_Assay = testList "CRUD Assay" [ i.RegisterAssay("Study 1", expected.Identifier) Expect.equal i.StudyCount 2 "StudyCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 3 "AssayCount" + Expect.equal s.RegisteredAssayCount 3 "AssayCount" let actual = i.GetAssayAt 2 Expect.equal actual expected "equal" ] @@ -457,7 +457,7 @@ let tests_Assay = testList "CRUD Assay" [ Expect.equal i.AssayCount 2 "AssayCount" Expect.equal i.Assays.[0] expected "equal" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" + Expect.equal s.RegisteredAssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" testCase "by index tpOntology" <| fun _ -> let i = createExampleInvestigation() let assay_ident = "New Assay" @@ -467,7 +467,7 @@ let tests_Assay = testList "CRUD Assay" [ Expect.equal i.StudyCount 2 "StudyCount" Expect.equal i.AssayCount 2 "AssayCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" + Expect.equal s.RegisteredAssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" Expect.equal i.Assays.[0] expected "equal" testCase "by identifier" <| fun _ -> let i = createExampleInvestigation() @@ -478,7 +478,7 @@ let tests_Assay = testList "CRUD Assay" [ Expect.equal i.StudyCount 2 "StudyCount" Expect.equal i.AssayCount 2 "AssayCount" let s = i.Studies.Item 0 - Expect.equal s.AssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" + Expect.equal s.RegisteredAssayCount 1 "Registered AssayCount. should be 1 less due to different identifier" let actual = i.Assays.[1] Expect.equal actual expected "equal" ] diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 3b9c68d5..3a794f7c 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -113,7 +113,7 @@ let private test_create = Expect.isEmpty actual.Contacts "Contacts" Expect.isEmpty actual.StudyDesignDescriptors "StudyDesignDescriptors" Expect.isEmpty actual.Tables "Tables" - Expect.isEmpty actual.Assays "Assays" + Expect.isEmpty actual.RegisteredAssayIdentifiers "Assays" Expect.isEmpty actual.Factors "Factors" Expect.isEmpty actual.Comments "Comments" testCase "make" <| fun _ -> @@ -174,30 +174,30 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ testCase "RegisterAssay" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" testCase "GetRegisteredAssay" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - let eval() = study.GetAssay(_assay_identifier) |> ignore + let eval() = study.GetRegisteredAssay(_assay_identifier) |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." testCase "GetRegisteredAssays" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - let eval() = study.Assays |> ignore + let eval() = study.RegisteredAssays |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." testCase "DeregisterAssay" <| fun _ -> let study = createTestStudy() study.RegisterAssay(_assay_identifier) - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" study.DeregisterAssay(_assay_identifier) - Expect.equal study.AssayCount 0 "registered assay count 2" + Expect.equal study.RegisteredAssayCount 0 "registered assay count 2" testCase "InitAssay" <| fun _ -> let study = createTestStudy() - let eval() = study.InitAssay(_assay_identifier) |> ignore + let eval() = study.InitRegisteredAssay(_assay_identifier) |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." testCase "AddAssay" <| fun _ -> let study = createTestStudy() - let eval() = study.AddAssay(ArcAssay(_assay_identifier)) |> ignore + let eval() = study.AddRegisteredAssay(ArcAssay(_assay_identifier)) |> ignore Expect.throws eval "throws as single study has no parent, therefore no access to full assays." ] testList "with parent" [ @@ -210,7 +210,7 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ study.RegisterAssay(_assay_identifier) Expect.equal i.AssayCount 1 "AssayCount" Expect.equal i.StudyCount 1 "StudyCount" - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" testCase "GetRegisteredAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let assay = createTestAssay() @@ -218,7 +218,7 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ i.AddStudy(study) i.AddAssay(assay) study.RegisterAssay(_assay_identifier) - let actual = study.GetAssay(_assay_identifier) + let actual = study.GetRegisteredAssay(_assay_identifier) Expect.equal actual assay "equal" testCase "GetRegisteredAssays" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -227,7 +227,7 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ i.AddStudy(study) i.AddAssay(assay) study.RegisterAssay(_assay_identifier) - let actual = study.Assays + let actual = study.RegisteredAssays Expect.equal actual.[0] assay "equal" testCase "DeregisterAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -237,34 +237,34 @@ let tests_RegisteredAssays = testList "RegisteredAssays" [ i.AddAssay(assay) study.RegisterAssay(_assay_identifier) study.DeregisterAssay(_assay_identifier) - Expect.equal study.AssayCount 0 "registered assay count 2" + Expect.equal study.RegisteredAssayCount 0 "registered assay count 2" Expect.equal i.AssayCount 1 "AssayCount, only deregister from study, not remove" Expect.equal i.StudyCount 1 "StudyCount" testCase "InitAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let study = createTestStudy() i.AddStudy(study) - let assay = study.InitAssay(_assay_identifier) + let assay = study.InitRegisteredAssay(_assay_identifier) Expect.equal i.AssayCount 1 "AssayCount" Expect.equal i.StudyCount 1 "StudyCount" - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" Expect.equal i.Assays.[0] assay "equal" testCase "AddAssay" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let study = createTestStudy() i.AddStudy(study) let assay = ArcAssay.init(_assay_identifier) - study.AddAssay(assay) + study.AddRegisteredAssay(assay) Expect.equal i.AssayCount 1 "AssayCount" Expect.equal i.StudyCount 1 "StudyCount" - Expect.equal study.AssayCount 1 "registered assay count" + Expect.equal study.RegisteredAssayCount 1 "registered assay count" Expect.equal i.Assays.[0] assay "equal" testCase "Check mutability" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") let study = createTestStudy() i.AddStudy(study) let assay = ArcAssay.init(_assay_identifier) - study.AddAssay(assay) + study.AddRegisteredAssay(assay) let assayFromInv = i.Assays.[0] let table = assayFromInv.InitTable("MyNewTable") Expect.equal assayFromInv assay "equal" @@ -287,7 +287,7 @@ let tests_copy = let study = createTestStudy() Expect.equal study.Identifier _study_identifier "_study_identifier" Expect.equal study.Description _study_description "_study_description" - Expect.equal study.AssayCount 1 "AssayCount" + Expect.equal study.RegisteredAssayCount 1 "AssayCount" let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "_assay_identifier" testCase "test mutable fields" <| fun _ -> @@ -301,14 +301,14 @@ let tests_copy = Expect.equal study.Identifier _study_identifier "_study_identifier" Expect.equal study.Description _study_description "_study_description" Expect.equal study.PublicReleaseDate None "PublicReleaseDate" - Expect.equal study.AssayCount 1 "AssayCount" + Expect.equal study.RegisteredAssayCount 1 "AssayCount" let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "_assay_identifier" let checkCopy = Expect.equal copy.Identifier _study_identifier "copy _study_identifier" Expect.equal copy.Description newDesciption "copy _study_description" Expect.equal copy.PublicReleaseDate newPublicReleaseDate "copy PublicReleaseDate" - Expect.equal copy.AssayCount 1 "copy AssayCount" + Expect.equal copy.RegisteredAssayCount 1 "copy AssayCount" let assayIdentifier = study.RegisteredAssayIdentifiers.[0] Expect.equal assayIdentifier _assay_identifier "copy _assay_identifier" () diff --git a/tests/ISA/ISA.Tests/Fable.Tests.fs b/tests/ISA/ISA.Tests/Fable.Tests.fs index 9509692f..ae9ef457 100644 --- a/tests/ISA/ISA.Tests/Fable.Tests.fs +++ b/tests/ISA/ISA.Tests/Fable.Tests.fs @@ -23,7 +23,7 @@ let private tests_EmptyObjectCreation = Expect.isNone s.Description "Should be None" ) testCase "MakeEmptyInvestigation" (fun () -> - let i = ArcInvestigation.make "My Identifier" None None None None [||] [||] [||] (ResizeArray()) (ResizeArray()) [||] [||] + let i = ArcInvestigation.make "My Identifier" None None None None [||] [||] [||] (ResizeArray()) (ResizeArray()) (ResizeArray()) [||] [||] Expect.isNone i.Description "Should be None" Expect.equal i.Identifier "My Identifier" "Should be None" ) From 535eaff9aac1ed8a800feb6397b8ae1c6222e9cf Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Fri, 8 Sep 2023 16:02:40 +0200 Subject: [PATCH 08/17] fix and improve ISA Spreadsheet tests for assay/study registration --- src/ISA/ISA.Spreadsheet/ArcInvestigation.fs | 15 +- src/ISA/ISA.Spreadsheet/ArcStudy.fs | 118 +++---- src/ISA/ISA.Spreadsheet/Metadata/Study.fs | 14 +- src/ISA/ISA/ArcTypes/ArcTypes.fs | 35 ++- .../ISA.Spreadsheet.Tests/StudyFileTests.fs | 21 +- .../TestObjects/Study.fs | 293 ++++++++++++++++++ tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 31 ++ 7 files changed, 441 insertions(+), 86 deletions(-) diff --git a/src/ISA/ISA.Spreadsheet/ArcInvestigation.fs b/src/ISA/ISA.Spreadsheet/ArcInvestigation.fs index fafd86ed..f6427fb9 100644 --- a/src/ISA/ISA.Spreadsheet/ArcInvestigation.fs +++ b/src/ISA/ISA.Spreadsheet/ArcInvestigation.fs @@ -98,7 +98,8 @@ module ArcInvestigation = |> InvestigationInfo.ToSparseTable |> SparseTable.ToRows - let fromParts (investigationInfo:InvestigationInfo) (ontologySourceReference:OntologySourceReference list) (publications: Publication list) (contacts: Person list) (studies: ArcStudy list) (remarks: Remark list) = + let fromParts (investigationInfo:InvestigationInfo) (ontologySourceReference:OntologySourceReference list) (publications: Publication list) (contacts: Person list) (studies: ArcStudy list) (assays: ArcAssay list) (remarks: Remark list) = + let studyIdentifiers = studies |> List.map (fun s -> s.Identifier) ArcInvestigation.make (investigationInfo.Identifier) (Option.fromValueWithDefault "" investigationInfo.Title) @@ -108,7 +109,9 @@ module ArcInvestigation = (Array.ofList ontologySourceReference) (Array.ofList publications) (Array.ofList contacts) + (ResizeArray(assays)) (ResizeArray(studies)) + (ResizeArray(studyIdentifiers)) (Array.ofList investigationInfo.Comments) (Array.ofList remarks) @@ -145,7 +148,13 @@ module ArcInvestigation = loop currentLine ontologySourceReferences investigationInfo publications contacts studies (List.append remarks newRemarks) lineNumber | k -> - fromParts investigationInfo ontologySourceReferences publications contacts (List.rev studies) remarks + let studies,assays = + studies + |> List.unzip + |> fun (s,a) -> + s |> List.rev, + a |> List.concat |> List.distinctBy (fun a -> a.Identifier) + fromParts investigationInfo ontologySourceReferences publications contacts studies assays remarks if en.MoveNext () then let currentLine = en.Current |> SparseRow.tryGetValueAt 0 @@ -187,7 +196,7 @@ module ArcInvestigation = for study in (List.ofSeq investigation.Studies) do yield SparseRow.fromValues [studyLabel] - yield! Studies.toRows study + yield! Studies.toRows study None } |> insertRemarks (List.ofArray investigation.Remarks) |> seq diff --git a/src/ISA/ISA.Spreadsheet/ArcStudy.fs b/src/ISA/ISA.Spreadsheet/ArcStudy.fs index e6f385fe..a775841c 100644 --- a/src/ISA/ISA.Spreadsheet/ArcStudy.fs +++ b/src/ISA/ISA.Spreadsheet/ArcStudy.fs @@ -1,69 +1,77 @@ -module ARCtrl.ISA.Spreadsheet.ArcStudy +namespace ARCtrl.ISA.Spreadsheet open ARCtrl.ISA open FsSpreadsheet -let [] obsoleteStudiesLabel = "STUDY METADATA" -let [] studiesLabel = "STUDY" -let [] obsoleteMetaDataSheetName = "Study" -let [] metaDataSheetName = "isa_study" +module ArcStudy = + let [] obsoleteStudiesLabel = "STUDY METADATA" + let [] studiesLabel = "STUDY" -let toMetadataSheet (study : ArcStudy) : FsWorksheet = - let toRows (study:ArcStudy) = - seq { - yield SparseRow.fromValues [studiesLabel] - yield! Studies.StudyInfo.toRows study - } - let sheet = FsWorksheet(metaDataSheetName) - study - |> toRows - |> Seq.iteri (fun rowI r -> SparseRow.writeToSheet (rowI + 1) r sheet) - sheet + let [] obsoleteMetaDataSheetName = "Study" + let [] metaDataSheetName = "isa_study" -let fromMetadataSheet (sheet : FsWorksheet) : ArcStudy = - let fromRows (rows: seq) = - let en = rows.GetEnumerator() - en.MoveNext() |> ignore - let _,_,_,study = Studies.fromRows 2 en - study - sheet.Rows - |> Seq.map SparseRow.fromFsRow - |> fromRows - |> Option.defaultValue (ArcStudy.create(Identifier.createMissingIdentifier())) + let toMetadataSheet (study : ArcStudy) (assays : ArcAssay list option) : FsWorksheet = + //let toRows (study:ArcStudy) assays = + // seq { + // yield SparseRow.fromValues [studiesLabel] + // yield! Studies.StudyInfo.toRows study + // } + let sheet = FsWorksheet(metaDataSheetName) + Studies.toRows study assays + |> Seq.append [SparseRow.fromValues [studiesLabel]] + |> Seq.iteri (fun rowI r -> SparseRow.writeToSheet (rowI + 1) r sheet) + sheet -/// Reads an assay from a spreadsheet -let fromFsWorkbook (doc:FsWorkbook) = - // Reading the "Assay" metadata sheet. Here metadata - let studyMetadata = + let fromMetadataSheet (sheet : FsWorksheet) : ArcStudy*ArcAssay list = + let fromRows (rows: seq) = + let en = rows.GetEnumerator() + en.MoveNext() |> ignore + let _,_,_,study = Studies.fromRows 2 en + study + sheet.Rows + |> Seq.map SparseRow.fromFsRow + |> fromRows + |> Option.defaultValue (ArcStudy.create(Identifier.createMissingIdentifier()),[]) + +[] +module Extensions = + + type ArcStudy with + + /// Reads an assay from a spreadsheet + static member fromFsWorkbook (doc:FsWorkbook) = + // Reading the "Assay" metadata sheet. Here metadata + let studyMetadata,assays = - match doc.TryGetWorksheetByName metaDataSheetName with - | Option.Some sheet -> - fromMetadataSheet sheet - | None -> - match doc.TryGetWorksheetByName obsoleteMetaDataSheetName with - | Option.Some sheet -> - fromMetadataSheet sheet - | None -> - printfn "Cannot retrieve metadata: Study file does not contain \"%s\" or \"%s\" sheet." metaDataSheetName obsoleteMetaDataSheetName - ArcStudy.create(Identifier.createMissingIdentifier()) + match doc.TryGetWorksheetByName ArcStudy.metaDataSheetName with + | Option.Some sheet -> + ArcStudy.fromMetadataSheet sheet + | None -> + match doc.TryGetWorksheetByName ArcStudy.obsoleteMetaDataSheetName with + | Option.Some sheet -> + ArcStudy.fromMetadataSheet sheet + | None -> + printfn "Cannot retrieve metadata: Study file does not contain \"%s\" or \"%s\" sheet." ArcStudy.metaDataSheetName ArcStudy.obsoleteMetaDataSheetName + ArcStudy.create(Identifier.createMissingIdentifier()),[] - let sheets = - doc.GetWorksheets() - |> Seq.choose ArcTable.tryFromFsWorksheet - if sheets |> Seq.isEmpty then - studyMetadata - else - studyMetadata.Tables <- ResizeArray(sheets) - studyMetadata + let sheets = + doc.GetWorksheets() + |> Seq.choose ArcTable.tryFromFsWorksheet + if sheets |> Seq.isEmpty then + studyMetadata + else + studyMetadata.Tables <- ResizeArray(sheets) + studyMetadata + ,assays -let toFsWorkbook (study : ArcStudy) = - let doc = new FsWorkbook() - let metaDataSheet = toMetadataSheet study - doc.AddWorksheet metaDataSheet + static member toFsWorkbook (study : ArcStudy,?assays : ArcAssay list) = + let doc = new FsWorkbook() + let metaDataSheet = ArcStudy.toMetadataSheet study assays + doc.AddWorksheet metaDataSheet - study.Tables - |> Seq.iter (ArcTable.toFsWorksheet >> doc.AddWorksheet) + study.Tables + |> Seq.iter (ArcTable.toFsWorksheet >> doc.AddWorksheet) - doc \ No newline at end of file + doc \ No newline at end of file diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Study.fs b/src/ISA/ISA.Spreadsheet/Metadata/Study.fs index d7c4748f..4fa8af2c 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Study.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Study.fs @@ -62,7 +62,7 @@ module Studies = Comment.fromString k (matrix.TryGetValueDefault("",(k,i)))) StudyInfo.create - (matrix.TryGetValueDefault("",(identifierLabel,i))) + (matrix.TryGetValueDefault(Identifier.createMissingIdentifier(),(identifierLabel,i))) (matrix.TryGetValueDefault("",(titleLabel,i))) (matrix.TryGetValueDefault("",(descriptionLabel,i))) (matrix.TryGetValueDefault("",(submissionDateLabel,i))) @@ -106,6 +106,7 @@ module Studies = |> SparseTable.ToRows let fromParts (studyInfo:StudyInfo) (designDescriptors:OntologyAnnotation list) (publications: Publication list) (factors: Factor list) (assays: ArcAssay list) (protocols : Protocol list) (contacts: Person list) = + let assayIdentifiers = assays |> List.map (fun assay -> assay.Identifier) ArcStudy.make (studyInfo.Identifier) (Option.fromValueWithDefault "" studyInfo.Title) @@ -116,10 +117,12 @@ module Studies = (Array.ofList contacts) (Array.ofList designDescriptors) (protocols |> List.map ArcTable.fromProtocol |> ResizeArray) - (ResizeArray(assays)) + (ResizeArray(assayIdentifiers)) (Array.ofList factors) (Array.ofList studyInfo.Comments) - |> fun arcstudy -> if arcstudy.isEmpty && arcstudy.Identifier = "" then None else Some arcstudy + |> fun arcstudy -> + if arcstudy.isEmpty && arcstudy.Identifier = "" + then None else Some (arcstudy,assays) let fromRows lineNumber (en:IEnumerator) = @@ -158,8 +161,9 @@ module Studies = loop currentLine item [] [] [] [] [] [] remarks lineNumber - let toRows (study : ArcStudy) = + let toRows (study : ArcStudy) (assays : ArcAssay list option) = let protocols = study.Tables |> Seq.collect (fun p -> p.GetProtocols()) |> List.ofSeq + let assays = assays |> Option.defaultValue (study.GetRegisteredAssaysOrIdentifier() |> List.ofSeq) seq { yield! StudyInfo.toRows study @@ -173,7 +177,7 @@ module Studies = yield! Factors.toRows (Some factorsLabelPrefix) (List.ofArray study.Factors) yield SparseRow.fromValues [assaysLabel] - yield! Assays.toRows (Some assaysLabelPrefix) (List.ofSeq study.Assays) + yield! Assays.toRows (Some assaysLabelPrefix) assays yield SparseRow.fromValues [protocolsLabel] yield! Protocols.toRows (Some protocolsLabelPrefix) protocols diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 4f97154e..e8b6db53 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -1090,13 +1090,21 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi assays [] -type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?registeredStudyIdentifiers : ResizeArray, ?comments : Comment [], ?remarks : Remark []) = +type ArcInvestigation(identifier : string, ?title : string, ?description : string, ?submissionDate : string, ?publicReleaseDate : string, ?ontologySourceReferences : OntologySourceReference [], ?publications : Publication [], ?contacts : Person [], ?assays : ResizeArray, ?studies : ResizeArray, ?registeredStudyIdentifiers : ResizeArray, ?comments : Comment [], ?remarks : Remark []) as this = let ontologySourceReferences = defaultArg ontologySourceReferences [||] let publications = defaultArg publications [||] let contacts = defaultArg contacts [||] - let assays = defaultArg assays (ResizeArray()) - let studies = defaultArg studies (ResizeArray()) + let assays = + let ass = defaultArg assays (ResizeArray()) + for a in ass do + a.Investigation <- Some this + ass + let studies = + let sss = defaultArg studies (ResizeArray()) + for s in sss do + s.Investigation <- Some this + sss let registeredStudyIdentifiers = defaultArg registeredStudyIdentifiers (ResizeArray()) let comments = defaultArg comments [||] let remarks = defaultArg remarks [||] @@ -1450,6 +1458,8 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin ?description = this.Description, ?submissionDate = this.SubmissionDate, ?publicReleaseDate = this.PublicReleaseDate, + studies = nextStudies, + assays = nextAssays, registeredStudyIdentifiers = nextStudyIdentifiers, ontologySourceReferences = nextOntologySourceReferences, publications = nextPublications, @@ -1457,12 +1467,6 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin comments = nextComments, remarks = nextRemarks ) - // This is necessary, so the studies and assays know which investigation they belong to. - for study in nextStudies do - i.AddStudy study - for assay in nextAssays do - i.AddAssay assay - i @@ -1497,6 +1501,7 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin |> List.map ArcStudy.fromStudy |> List.unzip let studies = ResizeArray(studiesRaw) + let studyIdentifiers = studiesRaw |> Seq.map (fun a -> a.Identifier) |> ResizeArray let assays = assaysRaw |> Seq.concat |> Seq.distinctBy (fun a -> a.Identifier) |> ResizeArray let i = ArcInvestigation.create( identifer, @@ -1505,14 +1510,10 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin ?submissionDate = i.SubmissionDate, ?publicReleaseDate = i.PublicReleaseDate, ?publications = (i.Publications |> Option.map Array.ofList), + studies = studies, + assays = assays, + registeredStudyIdentifiers = studyIdentifiers, ?contacts = (i.Contacts |> Option.map Array.ofList), ?comments = (i.Comments |> Option.map Array.ofList) - ) - - // This is necessary, so the studies and assays know which investigation they belong to. - for study in studies do - i.AddStudy study - for assay in assays do - i.AddAssay assay - + ) i \ No newline at end of file diff --git a/tests/ISA/ISA.Spreadsheet.Tests/StudyFileTests.fs b/tests/ISA/ISA.Spreadsheet.Tests/StudyFileTests.fs index b6b805fb..46140e1d 100644 --- a/tests/ISA/ISA.Spreadsheet.Tests/StudyFileTests.fs +++ b/tests/ISA/ISA.Spreadsheet.Tests/StudyFileTests.fs @@ -37,11 +37,11 @@ let testMetaDataFunctions = testCase "WriterSuccessEmpty" (fun () -> - let a = ArcStudy.fromMetadataSheet TestObjects.Study.studyMetadataEmpty + let study,assays = ArcStudy.fromMetadataSheet TestObjects.Study.studyMetadataEmpty let writingSuccess = try - ArcStudy.toMetadataSheet a |> ignore + ArcStudy.toMetadataSheet study (Some assays) |> ignore Result.Ok "DidRun" with | err -> Result.Error(sprintf "Writing the Empty test file failed: %s" err.Message) @@ -51,11 +51,11 @@ let testMetaDataFunctions = testCase "WriterSuccessEmptyObsoleteSheetName" (fun () -> - let a = + let study,assays = ArcStudy.fromMetadataSheet TestObjects.Study.studyMetadataEmptyObsoleteSheetName let writingSuccess = try - ArcStudy.toMetadataSheet a |> ignore + ArcStudy.toMetadataSheet study (Some assays) |> ignore Result.Ok "DidRun" with | err -> Result.Error(sprintf "Writing the Empty test file failed: %s" err.Message) @@ -68,17 +68,26 @@ let testMetaDataFunctions = let o = TestObjects.Study.studyMetadataEmpty |> ArcStudy.fromMetadataSheet - |> ArcStudy.toMetadataSheet + |> fun (s,a) -> ArcStudy.toMetadataSheet s (Some a) Expect.workSheetEqual o TestObjects.Study.studyMetadataEmpty "Written Empty study metadata does not match read study metadata" ) + testCase "OutputMatchesInput" (fun () -> + + let o = + TestObjects.Study.studyMetadata + |> ArcStudy.fromMetadataSheet + |> fun (s,a) -> ArcStudy.toMetadataSheet s (Some a) + + Expect.workSheetEqual o TestObjects.Study.studyMetadata "Written Empty study metadata does not match read study metadata" + ) testCase "OutputSheetNamesDifferentEmptyObsoleteSheetName" (fun () -> let o = TestObjects.Study.studyMetadataEmptyObsoleteSheetName |> ArcStudy.fromMetadataSheet - |> ArcStudy.toMetadataSheet + |> fun (s,a) -> ArcStudy.toMetadataSheet s (Some a) Expect.isTrue (o.Name <> TestObjects.Study.studyMetadataEmptyObsoleteSheetName.Name) "sheet names were expected to be different (obsolete replaced by new)" ) diff --git a/tests/ISA/ISA.Spreadsheet.Tests/TestObjects/Study.fs b/tests/ISA/ISA.Spreadsheet.Tests/TestObjects/Study.fs index 49c25def..b4a6dad8 100644 --- a/tests/ISA/ISA.Spreadsheet.Tests/TestObjects/Study.fs +++ b/tests/ISA/ISA.Spreadsheet.Tests/TestObjects/Study.fs @@ -18,6 +18,114 @@ let studyMetadataEmpty = row6.[1].Value <- "Study Public Release Date" let row7 = ws.Row(7) row7.[1].Value <- "Study File Name" + let row8 = ws.Row(8) + row8.[1].Value <- "STUDY DESIGN DESCRIPTORS" + let row9 = ws.Row(9) + row9.[1].Value <- "Study Design Type" + let row10 = ws.Row(10) + row10.[1].Value <- "Study Design Type Term Accession Number" + let row11 = ws.Row(11) + row11.[1].Value <- "Study Design Type Term Source REF" + let row12 = ws.Row(12) + row12.[1].Value <- "STUDY PUBLICATIONS" + let row13 = ws.Row(13) + row13.[1].Value <- "Study Publication PubMed ID" + let row14 = ws.Row(14) + row14.[1].Value <- "Study Publication DOI" + let row15 = ws.Row(15) + row15.[1].Value <- "Study Publication Author List" + let row16 = ws.Row(16) + row16.[1].Value <- "Study Publication Title" + let row17 = ws.Row(17) + row17.[1].Value <- "Study Publication Status" + let row18 = ws.Row(18) + row18.[1].Value <- "Study Publication Status Term Accession Number" + let row19 = ws.Row(19) + row19.[1].Value <- "Study Publication Status Term Source REF" + let row20 = ws.Row(20) + row20.[1].Value <- "STUDY FACTORS" + let row21 = ws.Row(21) + row21.[1].Value <- "Study Factor Name" + let row22 = ws.Row(22) + row22.[1].Value <- "Study Factor Type" + let row23 = ws.Row(23) + row23.[1].Value <- "Study Factor Type Term Accession Number" + let row24 = ws.Row(24) + row24.[1].Value <- "Study Factor Type Term Source REF" + let row25 = ws.Row(25) + row25.[1].Value <- "STUDY ASSAYS" + let row26 = ws.Row(26) + row26.[1].Value <- "Study Assay Measurement Type" + let row27 = ws.Row(27) + row27.[1].Value <- "Study Assay Measurement Type Term Accession Number" + let row28 = ws.Row(28) + row28.[1].Value <- "Study Assay Measurement Type Term Source REF" + let row29 = ws.Row(29) + row29.[1].Value <- "Study Assay Technology Type" + let row30 = ws.Row(30) + row30.[1].Value <- "Study Assay Technology Type Term Accession Number" + let row31 = ws.Row(31) + row31.[1].Value <- "Study Assay Technology Type Term Source REF" + let row32 = ws.Row(32) + row32.[1].Value <- "Study Assay Technology Platform" + let row33 = ws.Row(33) + row33.[1].Value <- "Study Assay File Name" + let row34 = ws.Row(34) + row34.[1].Value <- "STUDY PROTOCOLS" + let row35 = ws.Row(35) + row35.[1].Value <- "Study Protocol Name" + let row36 = ws.Row(36) + row36.[1].Value <- "Study Protocol Type" + let row37 = ws.Row(37) + row37.[1].Value <- "Study Protocol Type Term Accession Number" + let row38 = ws.Row(38) + row38.[1].Value <- "Study Protocol Type Term Source REF" + let row39 = ws.Row(39) + row39.[1].Value <- "Study Protocol Description" + let row40 = ws.Row(40) + row40.[1].Value <- "Study Protocol URI" + let row41 = ws.Row(41) + row41.[1].Value <- "Study Protocol Version" + let row42 = ws.Row(42) + row42.[1].Value <- "Study Protocol Parameters Name" + let row43 = ws.Row(43) + row43.[1].Value <- "Study Protocol Parameters Term Accession Number" + let row44 = ws.Row(44) + row44.[1].Value <- "Study Protocol Parameters Term Source REF" + let row45 = ws.Row(45) + row45.[1].Value <- "Study Protocol Components Name" + let row46 = ws.Row(46) + row46.[1].Value <- "Study Protocol Components Type" + let row47 = ws.Row(47) + row47.[1].Value <- "Study Protocol Components Type Term Accession Number" + let row48 = ws.Row(48) + row48.[1].Value <- "Study Protocol Components Type Term Source REF" + let row49 = ws.Row(49) + row49.[1].Value <- "STUDY CONTACTS" + let row50 = ws.Row(50) + row50.[1].Value <- "Study Person Last Name" + let row51 = ws.Row(51) + row51.[1].Value <- "Study Person First Name" + let row52 = ws.Row(52) + row52.[1].Value <- "Study Person Mid Initials" + let row53 = ws.Row(53) + row53.[1].Value <- "Study Person Email" + let row54 = ws.Row(54) + row54.[1].Value <- "Study Person Phone" + let row55 = ws.Row(55) + row55.[1].Value <- "Study Person Fax" + let row56 = ws.Row(56) + row56.[1].Value <- "Study Person Address" + let row57 = ws.Row(57) + row57.[1].Value <- "Study Person Affiliation" + let row58 = ws.Row(58) + row58.[1].Value <- "Study Person Roles" + let row59 = ws.Row(59) + row59.[1].Value <- "Study Person Roles Term Accession Number" + let row60 = ws.Row(60) + row60.[1].Value <- "Study Person Roles Term Source REF" + let row61 = ws.Row(61) + row61.[1].Value <- "Comment[Study Person REF]" ws [] @@ -45,6 +153,191 @@ let studyMetadata = let row7 = ws.Row(7) row7.[1].Value <- "Study File Name" row7.[2].Value <- $"studies/{studyIdentifier}/isa.study.xlsx" + let row8 = ws.Row(8) + row8.[1].Value <- "STUDY DESIGN DESCRIPTORS" + let row9 = ws.Row(9) + row9.[1].Value <- "Study Design Type" + row9.[2].Value <- "intervention design" + let row10 = ws.Row(10) + row10.[1].Value <- "Study Design Type Term Accession Number" + row10.[2].Value <- "http://purl.obolibrary.org/obo/OBI_0000115" + let row11 = ws.Row(11) + row11.[1].Value <- "Study Design Type Term Source REF" + row11.[2].Value <- "OBI" + let row12 = ws.Row(12) + row12.[1].Value <- "STUDY PUBLICATIONS" + let row13 = ws.Row(13) + row13.[1].Value <- "Study Publication PubMed ID" + row13.[2].Value <- "17439666" + let row14 = ws.Row(14) + row14.[1].Value <- "Study Publication DOI" + row14.[2].Value <- "doi:10.1186/jbiol54" + let row15 = ws.Row(15) + row15.[1].Value <- "Study Publication Author List" + row15.[2].Value <- "Castrillo JI, Zeef LA, Hoyle DC, Zhang N, Hayes A, Gardner DC, Cornell MJ, Petty J, Hakes L, Wardleworth L, Rash B, Brown M, Dunn WB, Broadhurst D, O'Donoghue K, Hester SS, Dunkley TP, Hart SR, Swainston N, Li P, Gaskell SJ, Paton NW, Lilley KS, Kell DB, Oliver SG." + let row16 = ws.Row(16) + row16.[1].Value <- "Study Publication Title" + row16.[2].Value <- "Growth control of the eukaryote cell: a systems biology study in yeast." + let row17 = ws.Row(17) + row17.[1].Value <- "Study Publication Status" + row17.[2].Value <- "published" + let row18 = ws.Row(18) + row18.[1].Value <- "Study Publication Status Term Accession Number" + let row19 = ws.Row(19) + row19.[1].Value <- "Study Publication Status Term Source REF" + let row20 = ws.Row(20) + row20.[1].Value <- "STUDY FACTORS" + let row21 = ws.Row(21) + row21.[1].Value <- "Study Factor Name" + row21.[2].Value <- "limiting nutrient" + row21.[3].Value <- "rate" + let row22 = ws.Row(22) + row22.[1].Value <- "Study Factor Type" + row22.[2].Value <- "chemical compound" + row22.[3].Value <- "rate" + let row23 = ws.Row(23) + row23.[1].Value <- "Study Factor Type Term Accession Number" + row23.[3].Value <- "http://purl.obolibrary.org/obo/PATO_0000161" + let row24 = ws.Row(24) + row24.[1].Value <- "Study Factor Type Term Source REF" + row24.[3].Value <- "PATO" + let row25 = ws.Row(25) + row25.[1].Value <- "STUDY ASSAYS" + let row26 = ws.Row(26) + row26.[1].Value <- "Study Assay Measurement Type" + row26.[2].Value <- "protein expression profiling" + row26.[3].Value <- "metabolite profiling" + row26.[4].Value <- "transcription profiling" + let row27 = ws.Row(27) + row27.[1].Value <- "Study Assay Measurement Type Term Accession Number" + row27.[2].Value <- "http://purl.obolibrary.org/obo/OBI_0000615" + row27.[3].Value <- "http://purl.obolibrary.org/obo/OBI_0000366" + row27.[4].Value <- "424" + let row28 = ws.Row(28) + row28.[1].Value <- "Study Assay Measurement Type Term Source REF" + row28.[2].Value <- "OBI" + row28.[3].Value <- "OBI" + row28.[4].Value <- "OBI" + let row29 = ws.Row(29) + row29.[1].Value <- "Study Assay Technology Type" + row29.[2].Value <- "mass spectrometry" + row29.[3].Value <- "mass spectrometry" + row29.[4].Value <- "DNA microarray" + let row30 = ws.Row(30) + row30.[1].Value <- "Study Assay Technology Type Term Accession Number" + row30.[4].Value <- "http://purl.obolibrary.org/obo/OBI_0400148" + let row31 = ws.Row(31) + row31.[1].Value <- "Study Assay Technology Type Term Source REF" + row31.[2].Value <- "OBI" + row31.[3].Value <- "OBI" + row31.[4].Value <- "OBI" + let row32 = ws.Row(32) + row32.[1].Value <- "Study Assay Technology Platform" + row32.[2].Value <- "iTRAQ" + row32.[3].Value <- "LC-MS/MS" + row32.[4].Value <- "Affymetrix" + let row33 = ws.Row(33) + row33.[1].Value <- "Study Assay File Name" + row33.[2].Value <- $"assays/{Assay.assayIdentifier}/isa.assay.xlsx" + row33.[3].Value <- "assays/metabolome/isa.assay.xlsx" + row33.[4].Value <- "assays/transcriptome/isa.assay.xlsx" + let row34 = ws.Row(34) + row34.[1].Value <- "STUDY PROTOCOLS" + let row35 = ws.Row(35) + row35.[1].Value <- "Study Protocol Name" + row35.[2].Value <- "growth protocol" + row35.[3].Value <- "mRNA extraction" + row35.[4].Value <- "protein extraction" + row35.[5].Value <- "biotin labeling" + row35.[6].Value <- "ITRAQ labeling" + row35.[7].Value <- "EukGE-WS4" + row35.[8].Value <- "metabolite extraction" + let row36 = ws.Row(36) + row36.[1].Value <- "Study Protocol Type" + row36.[2].Value <- "growth" + row36.[3].Value <- "mRNA extraction" + row36.[4].Value <- "protein extraction" + row36.[5].Value <- "labeling" + row36.[6].Value <- "labeling" + row36.[7].Value <- "hybridization" + row36.[8].Value <- "extraction" + let row37 = ws.Row(37) + row37.[1].Value <- "Study Protocol Type Term Accession Number" + row37.[8].Value <- "http://purl.obolibrary.org/obo/OBI_0302884" + let row38 = ws.Row(38) + row38.[1].Value <- "Study Protocol Type Term Source REF" + row38.[8].Value <- "OBI" + let row39 = ws.Row(39) + row39.[1].Value <- "Study Protocol Description" + row39.[2].Value <- "1. Biomass samples (45 ml) were taken via the sample port of the Applikon fermenters. The cells were pelleted by centrifugation for 5 min at 5000 rpm. The supernatant was removed and the RNA pellet resuspended in the residual medium to form a slurry. This was added in a dropwise manner directly into a 5 ml Teflon flask (B. Braun Biotech, Germany) containing liquid nitrogen and a 7 mm-diameter tungsten carbide ball. After allowing evaporation of the liquid nitrogen the flask was reassembled and the cells disrupted by agitation at 1500 rpm for 2 min in a Microdismembranator U (B. Braun Biotech, Germany) 2. The frozen powder was then dissolved in 1 ml of TriZol reagent (Sigma-Aldrich, UK), vortexed for 1 min, and then kept at room temperature for a further 5min. 3. Chloroform extraction was performed by addition of 0.2 ml chloroform, shaking vigorously or 15 s, then 5min incubation at room temperature. 4. Following centrifugation at 12,000 rpm for 5 min, the RNA (contained in the aqueous phase) was precipitated with 0.5 vol of 2-propanol at room temperature for 15 min. 5. After further centrifugation (12,000 rpm for 10 min at 4 C) the RNA pellet was washed twice with 70 % (v/v) ethanol, briefly air-dried, and redissolved in 0.5 ml diethyl pyrocarbonate (DEPC)-treated water. 6. The single-stranded RNA was precipitated once more by addition of 0.5 ml of LiCl buffer (4 M LiCl, 20 mM Tris-HCl, pH 7.5, 10 mM EDTA), thus removing tRNA and DNA from the sample. 7. After precipitation (20 C for 1h) and centrifugation (12,000 rpm, 30 min, 4 C), the RNA was washed twice in 70 % (v/v) ethanol prior to being dissolved in a minimal volume of DEPC-treated water. 8. Total RNA quality was checked using the RNA 6000 Nano Assay, and analysed on an Agilent 2100 Bioanalyser (Agilent Technologies). RNA was quantified using the Nanodrop ultra low volume spectrophotometer (Nanodrop Technologies)." + row39.[3].Value <- "1. Biomass samples (45 ml) were taken via the sample port of the Applikon fermenters. The cells were pelleted by centrifugation for 5 min at 5000 rpm. The supernatant was removed and the RNA pellet resuspended in the residual medium to form a slurry. This was added in a dropwise manner directly into a 5 ml Teflon flask (B. Braun Biotech, Germany) containing liquid nitrogen and a 7 mm-diameter tungsten carbide ball. After allowing evaporation of the liquid nitrogen the flask was reassembled and the cells disrupted by agitation at 1500 rpm for 2 min in a Microdismembranator U (B. Braun Biotech, Germany) 2. The frozen powder was then dissolved in 1 ml of TriZol reagent (Sigma-Aldrich, UK), vortexed for 1 min, and then kept at room temperature for a further 5min. 3. Chloroform extraction was performed by addition of 0.2 ml chloroform, shaking vigorouslyor 15 s, then 5min incubation at room temperature. 4. Following centrifugation at 12,000 rpm for 5 min, the RNA (contained in the aqueous phase) was precipitated with 0.5 vol of 2-propanol at room temperature for 15 min. 5. After further centrifugation (12,000 rpm for 10 min at 4 C) the RNA pellet was washed twice with 70 % (v/v) ethanol, briefly air-dried, and redissolved in 0.5 ml diethyl pyrocarbonate (DEPC)-treated water. 6. The single-stranded RNA was precipitated once more by addition of 0.5 ml of LiCl bffer (4 M LiCl, 20 mM Tris-HCl, pH 7.5, 10 mM EDTA), thus removing tRNA and DNA from the sample. 7. After precipitation (20 C for 1 h) and centrifugation (12,000 rpm, 30 min, 4 C), the RNA was washed twice in 70 % (v/v) ethanol prior to being dissolved in a minimal volume of DEPC-treated water. 8. Total RNA quality was checked using the RNA 6000 Nano Assay, and analysed on an Agilent 2100 Bioanalyser (Agilent Technologies). RNA was quantified using the Nanodrop ultra low volume spectrophotometer (Nanodrop Technologies)." + row39.[5].Value <- "This was done using Enzo BioArrayTM HighYieldTM RNA transcript labelling kit (T7) with 5 ul cDNA. The resultant cRNA was again purified using the GeneChip Sample Clean Up Module. The column was eluted in the first instance using 10 l RNase-free water, and for a second time using 11 ul RNase-free water. cRNA was quantified using the Nanodrop spectrophotometer. A total of 15 ug of cRNA (required for hybridisation) was fragmented. Fragmentation was carried out by using 2 ul of fragmentation buffer for every 8 ul cRNA." + row39.[7].Value <- "For each target, a hybridisation cocktail was made using the standard array recipe as described in the GeneChip Expression Analysis technical manual. GeneChip control oligonucleotide and 20x eukaryotic hybridisation controls were used. Hybridisation buffer was made as detailed in the GeneChip manual and the BSA and herring sperm DNA was purchased from Invitrogen. The cocktail was heated to 99 C for 5 min, transferred to 45 C for 5 min and then spun for 5 min to remove any insoluble material. Affymetrix Yeast Yg_s98 S. cerevisiae arrays were pre-hybridised with 200 ul 1x hybridisation buffer and incubated at 45 C for 10 min. 200 ul of the hybridisation cocktail was loaded onto the arrays. The probe array was incubated in a rotisserie at 45 C, rotating at 60 rpm. Following hybridisation, for 16 hr, chips were loaded onto a Fluidics station for washing and staining using the EukGe WS2v4 programme controlled using Microarray Suite 5 software." + let row40 = ws.Row(40) + row40.[1].Value <- "Study Protocol URI" + let row41 = ws.Row(41) + row41.[1].Value <- "Study Protocol Version" + let row42 = ws.Row(42) + row42.[1].Value <- "Study Protocol Parameters Name" + row42.[8].Value <- "sample volume;standard volume" + let row43 = ws.Row(43) + row43.[1].Value <- "Study Protocol Parameters Term Accession Number" + row43.[8].Value <- ";" + let row44 = ws.Row(44) + row44.[1].Value <- "Study Protocol Parameters Term Source REF" + row44.[8].Value <- ";" + let row45 = ws.Row(45) + row45.[1].Value <- "Study Protocol Components Name" + let row46 = ws.Row(46) + row46.[1].Value <- "Study Protocol Components Type" + let row47 = ws.Row(47) + row47.[1].Value <- "Study Protocol Components Type Term Accession Number" + let row48 = ws.Row(48) + row48.[1].Value <- "Study Protocol Components Type Term Source REF" + let row49 = ws.Row(49) + row49.[1].Value <- "STUDY CONTACTS" + let row50 = ws.Row(50) + row50.[1].Value <- "Study Person Last Name" + row50.[2].Value <- "Oliver" + row50.[3].Value <- "Juan" + row50.[4].Value <- "Leo" + let row51 = ws.Row(51) + row51.[1].Value <- "Study Person First Name" + row51.[2].Value <- "Stephen" + row51.[3].Value <- "Castrillo" + row51.[4].Value <- "Zeef" + let row52 = ws.Row(52) + row52.[1].Value <- "Study Person Mid Initials" + row52.[2].Value <- "G" + row52.[3].Value <- "I" + row52.[4].Value <- "A" + let row53 = ws.Row(53) + row53.[1].Value <- "Study Person Email" + let row54 = ws.Row(54) + row54.[1].Value <- "Study Person Phone" + let row55 = ws.Row(55) + row55.[1].Value <- "Study Person Fax" + let row56 = ws.Row(56) + row56.[1].Value <- "Study Person Address" + row56.[2].Value <- "Oxford Road, Manchester M13 9PT, UK" + row56.[3].Value <- "Oxford Road, Manchester M13 9PT, UK" + row56.[4].Value <- "Oxford Road, Manchester M13 9PT, UK" + let row57 = ws.Row(57) + row57.[1].Value <- "Study Person Affiliation" + row57.[2].Value <- "Faculty of Life Sciences, Michael Smith Building, University of Manchester" + row57.[3].Value <- "Faculty of Life Sciences, Michael Smith Building, University of Manchester" + row57.[4].Value <- "Faculty of Life Sciences, Michael Smith Building, University of Manchester" + let row58 = ws.Row(58) + row58.[1].Value <- "Study Person Roles" + row58.[2].Value <- "corresponding author" + row58.[3].Value <- "author" + row58.[4].Value <- "author" + let row59 = ws.Row(59) + row59.[1].Value <- "Study Person Roles Term Accession Number" + let row60 = ws.Row(60) + row60.[1].Value <- "Study Person Roles Term Source REF" + let row61 = ws.Row(61) + row61.[1].Value <- "Comment[Study Person REF]" ws let studyMetadataEmptyObsoleteSheetName = diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 86b351d4..5b385d99 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -161,6 +161,37 @@ let private test_create = Expect.equal actual.Studies studies "Studies" Expect.equal actual.Comments comments "Comments" Expect.equal actual.Remarks remarks "Remarks" + testCase "constructorAppliesReference" <| fun _ -> + let assayName = "MyAssay" + let studyName = "MyStudy" + let tableName = "MyTable" + let investigationName = "MyInvestigation" + + let myAssay = ArcAssay.init(assayName) + myAssay.InitTable(tableName) |> ignore + let myStudy = ArcStudy.init(studyName) + myStudy.RegisterAssay(assayName) + + let assays = ResizeArray([myAssay]) + let studies = ResizeArray([|myStudy|]) + let registeredStudyIdentifiers = ResizeArray([studyName]) + + let myInvestigation = + ArcInvestigation( + investigationName, + assays = assays, + studies = studies, + registeredStudyIdentifiers = registeredStudyIdentifiers + ) + + let result = myStudy.GetRegisteredAssay(assayName) + + Expect.equal result myAssay "Retrieved assay should be equal" + + result.InitTable("MySecondTable") |> ignore + + Expect.equal result.TableCount 2 "Table count should be 2" + Expect.equal myAssay.TableCount 2 "Table count should also be 2" ] let test_RegisteredAssays = testList "RegisteredAssays" [ From 72e1b02128e7abca5070b82fcd7dd06676ff0ad8 Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Mon, 11 Sep 2023 11:05:48 +0200 Subject: [PATCH 09/17] start adding update functions --- src/ARCtrl/ARCtrl.fs | 3 ++- src/ISA/ISA/ArcTypes/ArcTypes.fs | 16 ++++++++++++++++ tests/ARCtrl/ARCtrl.Tests.fs | 18 +++++++++--------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ARCtrl/ARCtrl.fs b/src/ARCtrl/ARCtrl.fs index d790798a..024cfa9a 100644 --- a/src/ARCtrl/ARCtrl.fs +++ b/src/ARCtrl/ARCtrl.fs @@ -3,6 +3,7 @@ open ARCtrl.FileSystem open ARCtrl.Contract open ARCtrl.ISA +open ARCtrl.ISA.Spreadsheet open FsSpreadsheet open Fable.Core @@ -18,7 +19,7 @@ module ARCAux = let getArcStudiesFromContracts (contracts: Contract []) = contracts |> Array.choose Contract.ArcStudy.tryFromContract - |> Array.map (fun x -> x :?> FsWorkbook |> ISA.Spreadsheet.ArcStudy.fromFsWorkbook) + |> Array.map (fun x -> x :?> FsWorkbook |> ArcStudy.fromFsWorkbook) let getArcInvestigationFromContracts (contracts: Contract []) = contracts diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index e8b6db53..34e99237 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -1022,6 +1022,22 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi comments = nextComments ) + member this.UpdateBy(study:ArcStudy,?onlyByExisting : bool,?appendLists : bool) = + let always = onlyByExisting |> Option.defaultValue false |> not + let append = appendLists |> Option.defaultValue false + if study.Title.IsSome || always then + this.Title <- study.Title + if study.Description.IsSome || always then + this.Description <- study.Description + if study.SubmissionDate.IsSome || always then + this.SubmissionDate <- study.SubmissionDate + if study.PublicReleaseDate.IsSome || always then + this.PublicReleaseDate <- study.PublicReleaseDate + if study.Publications.Length <> 0 || always then + let n = if append then Array.append this.Publications study.Publications + this.Publications <- l + + /// /// Creates an ISA-Json compatible Study from ArcStudy. /// diff --git a/tests/ARCtrl/ARCtrl.Tests.fs b/tests/ARCtrl/ARCtrl.Tests.fs index 12298f8f..c3922bc0 100644 --- a/tests/ARCtrl/ARCtrl.Tests.fs +++ b/tests/ARCtrl/ARCtrl.Tests.fs @@ -67,10 +67,10 @@ let private test_isaFromContracts = testList "read_contracts" [ Expect.equal study1.TableCount 8 "study 1 should have the 7 tables from investigation plus one extra. One table should be overwritten." Expect.equal study2.TableCount 4 "study 2 should have exactly as many tables as stated in investigation file" - Expect.equal study1.AssayCount 3 "study 1 should have read three assays" - let assay1 = study1.Assays.[0] - let assay2 = study1.Assays.[1] - let assay3 = study1.Assays.[2] + Expect.equal study1.RegisteredAssayCount 3 "study 1 should have read three assays" + let assay1 = study1.RegisteredAssays.[0] + let assay2 = study1.RegisteredAssays.[1] + let assay3 = study1.RegisteredAssays.[2] Expect.equal assay1.Identifier TestObjects.Assay.assayIdentifier "assay 1 identifier should have been read from assay contract" Expect.equal assay1.TableCount 1 "assay 1 should have read one table" Expect.equal assay2.TableCount 0 "assay 2 should have read no tables" @@ -97,7 +97,7 @@ let private test_writeContracts = testList "write_contracts" [ ) testCase "simpleISA" (fun _ -> let inv = ArcInvestigation("MyInvestigation", "BestTitle") - inv.InitStudy("MyStudy").InitAssay("MyAssay") |> ignore + inv.InitStudy("MyStudy").InitRegisteredAssay("MyAssay") |> ignore let arc = ARC(isa = inv) let contracts = arc.GetWriteContracts() let contractPathsString = contracts |> Array.map (fun c -> c.Path) |> String.concat ", " @@ -128,7 +128,7 @@ let private test_writeContracts = testList "write_contracts" [ ) testCase "sameAssayAndStudyName" (fun _ -> let inv = ArcInvestigation("MyInvestigation", "BestTitle") - inv.InitStudy("MyAssay").InitAssay("MyAssay") |> ignore + inv.InitStudy("MyAssay").InitRegisteredAssay("MyAssay") |> ignore let arc = ARC(isa = inv) let contracts = arc.GetWriteContracts() let contractPathsString = contracts |> Array.map (fun c -> c.Path) |> String.concat ", " @@ -167,8 +167,8 @@ let private test_writeContracts = testList "write_contracts" [ testCase "sameAssayInDifferentStudies" (fun _ -> let inv = ArcInvestigation("MyInvestigation", "BestTitle") let assay = ArcAssay("MyAssay") - inv.InitStudy("Study1").AddAssay(assay) |> ignore - inv.InitStudy("Study2").AddAssay(assay) |> ignore + inv.InitStudy("Study1").AddRegisteredAssay(assay) |> ignore + inv.InitStudy("Study2").AddRegisteredAssay(assay) |> ignore let arc = ARC(isa = inv) let contracts = arc.GetWriteContracts() let contractPathsString = contracts |> Array.map (fun c -> c.Path) |> String.concat ", " @@ -242,7 +242,7 @@ let private test_updateFileSystem = testList "update_Filesystem" [ let arc = ARC(isa = inv) let oldFS = arc.FileSystem.Copy() let assay = ArcAssay("MyAssay") - study.AddAssay(assay) + study.AddRegisteredAssay(assay) arc.UpdateFileSystem() let newFS = arc.FileSystem Expect.notEqual oldFS.Tree newFS.Tree "Tree should be unequal" From 145dbb7d6356608d9e86d4912eabad7661c0b524 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Mon, 11 Sep 2023 15:46:30 +0200 Subject: [PATCH 10/17] Work on UpdateBy on assay,study,investigation :construction: --- src/ISA/ISA/ArcTypes/ArcTypes.fs | 152 ++++++++++++++-- tests/ISA/ISA.Tests/ArcAssay.Tests.fs | 101 ++++++++++ tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 114 ++++++++++++ tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 172 ++++++++++++++++++ 4 files changed, 528 insertions(+), 11 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 34e99237..e746fc55 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -4,6 +4,9 @@ open Fable.Core open ARCtrl.ISA.Aux module ArcTypesAux = + + open System.Collections.Generic + module SanityChecks = let inline validateRegisteredInvestigation (investigation: ArcInvestigation option) = @@ -58,6 +61,22 @@ module ArcTypesAux = if Seq.contains registeredAssay existingAssays |> not then study.DeregisterAssay registeredAssay |> ignore + let inline updateAppendArray (append:bool) (origin: 'A []) (next: 'A []) = + if not append then + next + else + Array.append origin next + |> Array.distinct + + let inline updateAppendResizeArray (append:bool) (origin: ResizeArray<'A>) (next: ResizeArray<'A>) = + if not append then + next + else + for e in next do + if origin.Contains e |> not then + origin.Add(e) + origin + [] type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray, ?performers : Person [], ?comments : Comment []) = @@ -430,6 +449,51 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno comments=nextComments ) + /// + /// Updates given assay with another assay, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. + /// + /// The assay used for updating this assay. + /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** + /// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** + member this.UpdateBy(assay:ArcAssay,?onlyReplaceExisting : bool,?appendSequences : bool) = + let onlyReplaceExisting = defaultArg onlyReplaceExisting false + let appendSequences = defaultArg appendSequences false + let updateAlways = onlyReplaceExisting |> not + if assay.MeasurementType.IsSome || updateAlways then + this.MeasurementType <- assay.MeasurementType + if assay.TechnologyType.IsSome || updateAlways then + this.TechnologyType <- assay.TechnologyType + if assay.TechnologyPlatform.IsSome || updateAlways then + this.TechnologyPlatform <- assay.TechnologyPlatform + if assay.Tables.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Tables assay.Tables + this.Tables <- s + if assay.Performers.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Performers assay.Performers + this.Performers <- s + if assay.Comments.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Comments assay.Comments + this.Comments <- s + + // Use this for better print debugging and better unit test output + override this.ToString() = + sprintf + """ArcAssay({ + Identifier = "%s", + MeasurementType = %A, + TechnologyType = %A, + TechnologyPlatform = %A, + Tables = %A, + Performers = %A, + Comments = %A +})""" + this.Identifier + this.MeasurementType + this.TechnologyType + this.TechnologyPlatform + this.Tables + this.Performers + this.Comments /// 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. @@ -1022,21 +1086,45 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi comments = nextComments ) - member this.UpdateBy(study:ArcStudy,?onlyByExisting : bool,?appendLists : bool) = - let always = onlyByExisting |> Option.defaultValue false |> not - let append = appendLists |> Option.defaultValue false - if study.Title.IsSome || always then + /// + /// Updates given study with another study, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. + /// + /// The study used for updating this study. + /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** + /// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** + member this.UpdateBy(study:ArcStudy,?onlyReplaceExisting : bool,?appendSequences : bool) = + let onlyReplaceExisting = defaultArg onlyReplaceExisting false + let appendSequences = defaultArg appendSequences false + let updateAlways = onlyReplaceExisting |> not + if study.Title.IsSome || updateAlways then this.Title <- study.Title - if study.Description.IsSome || always then + if study.Description.IsSome || updateAlways then this.Description <- study.Description - if study.SubmissionDate.IsSome || always then + if study.SubmissionDate.IsSome || updateAlways then this.SubmissionDate <- study.SubmissionDate - if study.PublicReleaseDate.IsSome || always then + if study.PublicReleaseDate.IsSome || updateAlways then this.PublicReleaseDate <- study.PublicReleaseDate - if study.Publications.Length <> 0 || always then - let n = if append then Array.append this.Publications study.Publications - this.Publications <- l - + if study.Publications.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Publications study.Publications + this.Publications <- s + if study.Contacts.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Contacts study.Contacts + this.Contacts <- s + if study.StudyDesignDescriptors.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.StudyDesignDescriptors study.StudyDesignDescriptors + this.StudyDesignDescriptors <- s + if study.Tables.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Tables study.Tables + this.Tables <- s + if study.RegisteredAssayIdentifiers.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.RegisteredAssayIdentifiers study.RegisteredAssayIdentifiers + this.RegisteredAssayIdentifiers <- s + if study.Factors.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Factors study.Factors + this.Factors <- s + if study.Comments.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Comments study.Comments + this.Comments <- s /// /// Creates an ISA-Json compatible Study from ArcStudy. @@ -1485,6 +1573,48 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin ) i + /// + /// Updates given investigation with another investigation, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. + /// + /// The investigation used for updating this investigation. + /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** + /// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** + member this.UpdateBy(inv:ArcInvestigation,?onlyReplaceExisting : bool,?appendSequences : bool) = + let onlyReplaceExisting = defaultArg onlyReplaceExisting false + let appendSequences = defaultArg appendSequences false + let updateAlways = onlyReplaceExisting |> not + if inv.Title.IsSome || updateAlways then + this.Title <- inv.Title + if inv.Description.IsSome || updateAlways then + this.Description <- inv.Description + if inv.SubmissionDate.IsSome || updateAlways then + this.SubmissionDate <- inv.SubmissionDate + if inv.PublicReleaseDate.IsSome || updateAlways then + this.PublicReleaseDate <- inv.PublicReleaseDate + if inv.OntologySourceReferences.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.OntologySourceReferences inv.OntologySourceReferences + this.OntologySourceReferences <- s + if inv.Publications.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Publications inv.Publications + this.Publications <- s + if inv.Contacts.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Contacts inv.Contacts + this.Contacts <- s + if inv.Assays.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Assays inv.Assays + this.Assays <- s + if inv.Studies.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Studies inv.Studies + this.Studies <- s + if inv.RegisteredStudyIdentifiers.Count <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendResizeArray appendSequences this.RegisteredStudyIdentifiers inv.RegisteredStudyIdentifiers + this.RegisteredStudyIdentifiers <- s + if inv.Comments.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Comments inv.Comments + this.Comments <- s + if inv.Remarks.Length <> 0 || updateAlways then + let s = ArcTypesAux.updateAppendArray appendSequences this.Remarks inv.Remarks + this.Remarks <- s /// Transform an ArcInvestigation to an ISA Json Investigation. member this.ToInvestigation() : Investigation = diff --git a/tests/ISA/ISA.Tests/ArcAssay.Tests.fs b/tests/ISA/ISA.Tests/ArcAssay.Tests.fs index a820f189..6b958fe8 100644 --- a/tests/ISA/ISA.Tests/ArcAssay.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcAssay.Tests.fs @@ -502,6 +502,106 @@ let private tests_technologyPlatform = Expect.equal pt_new.TermAccessionShort "" "ShortTan should match" ) ] + +let private test_UpdateBy = testList "UpdateBy" [ + let create_testAssay() = + ArcAssay.create( + "MyAssay", + OntologyAnnotation.fromString("MyMeasurementType"), + OntologyAnnotation.fromString("MyTechnologyType"), + OntologyAnnotation.fromString("MyTechnologyPlatform"), + ResizeArray([ArcTable.init("MyTable")]), + [|Person.create(FirstName="Kevin", LastName="Frey")|], + [|Comment.create(Name="CommentName", Value="CommentValue")|] + ) + testCase "UpdateBy, full replace" <| fun _ -> + let actual = create_testAssay() + let next = + ArcAssay.create( + "NextAssay", + OntologyAnnotation.fromString("NextMeasurementType"), + OntologyAnnotation.fromString("NextTechnologyType"), + tables=ResizeArray([ArcTable.init("NextTable")]), + performers=[|Person.create(FirstName="NextKevin", LastName="NextFrey")|] + ) + actual.UpdateBy(next) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.MeasurementType next.MeasurementType "MeasurementType" + Expect.equal actual.TechnologyType next.TechnologyType "TechnologyType" + Expect.equal actual.TechnologyPlatform next.TechnologyPlatform "TechnologyPlatform" + TestingUtils.mySequenceEqual actual.Tables next.Tables "Tables" + Expect.equal actual.Performers next.Performers "Performers" + Expect.equal actual.Comments next.Comments "Comments" + testCase "UpdateBy, replace existing" <| fun _ -> + let actual = create_testAssay() + let next = + ArcAssay.create( + "NextAssay", + OntologyAnnotation.fromString("NextMeasurementType"), + OntologyAnnotation.fromString("NextTechnologyType"), + tables=ResizeArray([ArcTable.init("NextTable")]), + performers=[|Person.create(FirstName="NextKevin", LastName="NextFrey")|] + ) + let expected = create_testAssay() + actual.UpdateBy(next, true) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.MeasurementType next.MeasurementType "MeasurementType" + Expect.equal actual.TechnologyType next.TechnologyType "TechnologyType" + Expect.equal actual.TechnologyPlatform expected.TechnologyPlatform "TechnologyPlatform" + TestingUtils.mySequenceEqual actual.Tables next.Tables "Tables" + Expect.equal actual.Performers next.Performers "Performers" + Expect.equal actual.Comments expected.Comments "Comments" + testCase "UpdateBy, replace existing, empty tables" <| fun _ -> + let actual = create_testAssay() + let next = + ArcAssay.create( + "NextAssay", + OntologyAnnotation.fromString("NextMeasurementType"), + OntologyAnnotation.fromString("NextTechnologyType"), + performers=[|Person.create(FirstName="NextKevin", LastName="NextFrey")|] + ) + let expected = create_testAssay() + actual.UpdateBy(next, true) + TestingUtils.mySequenceEqual actual.Tables expected.Tables "Tables" + testCase "UpdateBy, replace existing, append" <| fun _ -> + let actual = create_testAssay() + let next = + ArcAssay.create( + "NextAssay", + OntologyAnnotation.fromString("NextMeasurementType"), + OntologyAnnotation.fromString("NextTechnologyType"), + tables=ResizeArray([ArcTable.init("NextTable")]), + performers=[|Person.create(FirstName="NextKevin", LastName="NextFrey")|] + ) + let expected = create_testAssay() + actual.UpdateBy(next, true, true) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.MeasurementType next.MeasurementType "MeasurementType" + Expect.equal actual.TechnologyType next.TechnologyType "TechnologyType" + Expect.equal actual.TechnologyPlatform expected.TechnologyPlatform "TechnologyPlatform" + TestingUtils.mySequenceEqual actual.Tables (ResizeArray([yield! expected.Tables; yield! next.Tables])) "Tables" + Expect.equal actual.Performers [|yield! expected.Performers; yield! next.Performers|] "Performers" + Expect.equal actual.Comments [|yield! expected.Comments; yield! next.Comments|] "Comments" + testCase "UpdateBy, replace all, append" <| fun _ -> + let actual = create_testAssay() + let next = + ArcAssay.create( + "NextAssay", + OntologyAnnotation.fromString("NextMeasurementType"), + OntologyAnnotation.fromString("NextTechnologyType"), + tables=ResizeArray([ArcTable.init("NextTable")]), + performers=[|Person.create(FirstName="NextKevin", LastName="NextFrey")|] + ) + let expected = create_testAssay() + actual.UpdateBy(next, false, true) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.MeasurementType next.MeasurementType "MeasurementType" + Expect.equal actual.TechnologyType next.TechnologyType "TechnologyType" + Expect.equal actual.TechnologyPlatform next.TechnologyPlatform "TechnologyPlatform" + TestingUtils.mySequenceEqual actual.Tables (ResizeArray([yield! expected.Tables; yield! next.Tables])) "Tables" + Expect.equal actual.Performers [|yield! expected.Performers; yield! next.Performers|] "Performers" + Expect.equal actual.Comments [|yield! expected.Comments; yield! next.Comments|] "Comments" +] let main = testList "ArcAssay" [ test_create @@ -513,4 +613,5 @@ let main = tests_UpdateTable tests_updateTable tests_technologyPlatform + test_UpdateBy ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 5b385d99..895c1d5a 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -545,6 +545,119 @@ let tests_Assay = testList "CRUD Assay" [ ] ] +let tests_UpdateBy = testList "UpdateBy" [ + let create_testInvestigation(assay) = + ArcInvestigation.create( + "MyInvestigation", + "MyTitle", + "MyDescription", + "MySubmissionDate", + "MyPublicReleaseDate", + [|OntologySourceReference.create(Name="MyOntologySourceReference")|], + [|Publication.create(Title="MyPublication")|], + [|Person.create(FirstName="Kevin", LastName="Frey")|], + ResizeArray([assay]), + ResizeArray([ArcStudy.init("MyStudy")]), + ResizeArray(["MyStudy"]), + [|Comment.create(Name="MyComment")|], + [|Remark.create(1,"MyRemark")|] + ) + let create_testInvestigationNextEmpty() = + ArcInvestigation.init("NextEmptyInvestigation") + let create_testInvestigationNext(assay) = + ArcInvestigation.create( + "NextInvestigation", + "NextTitle", + "NextDescription", + "NextSubmissionDate", + "NextPublicReleaseDate", + [|OntologySourceReference.create(Name="NextOntologySourceReference")|], + [|Publication.create(Title="NextPublication")|], + [|Person.create(FirstName="Kevin", LastName="Frey")|], + ResizeArray([assay]), + ResizeArray([ArcStudy.init("NextStudy")]), + ResizeArray(["NextStudy"]), + [|Comment.create(Name="NextComment")|], + [|Remark.create(12, "NextRemark")|] + ) + testCase "UpdateBy, full replace" <| fun _ -> + let myassay = ArcAssay.init("MyAssays") + let nextassay = ArcAssay.init("nextassay") + let actual = create_testInvestigation(myassay) + let next = create_testInvestigationNext(nextassay) + actual.UpdateBy(next) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" + Expect.equal actual.Publications next.Publications "Publications" + Expect.equal actual.Contacts next.Contacts "Contacts" + Expect.equal actual.Assays next.Assays "Assays" + Expect.equal actual.Studies next.Studies "Studies" + Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" + Expect.equal actual.Comments next.Comments "Comments" + Expect.equal actual.Remarks next.Remarks "Remarks" + testCase "UpdateBy, full replace empty" <| fun _ -> + let myassay = ArcAssay.init("MyAssays") + let actual = create_testInvestigation(myassay) + let next = create_testInvestigationNextEmpty() + actual.UpdateBy(next) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" + Expect.equal actual.Publications next.Publications "Publications" + Expect.equal actual.Contacts next.Contacts "Contacts" + Expect.equal actual.Assays next.Assays "Assays" + Expect.equal actual.Studies next.Studies "Studies" + Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" + Expect.equal actual.Comments next.Comments "Comments" + Expect.equal actual.Remarks next.Remarks "Remarks" + testCase "UpdateBy, replace existing" <| fun _ -> + let myassay = ArcAssay.init("MyAssays") + let nextassay = ArcAssay.init("nextassay") + let actual = create_testInvestigation(myassay) + let next = create_testInvestigationNext(nextassay) + actual.UpdateBy(next, true) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" + Expect.equal actual.Publications next.Publications "Publications" + Expect.equal actual.Contacts next.Contacts "Contacts" + Expect.equal actual.Assays next.Assays "Assays" + Expect.equal actual.Studies next.Studies "Studies" + Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" + Expect.equal actual.Comments next.Comments "Comments" + Expect.equal actual.Remarks next.Remarks "Remarks" + testCase "UpdateBy, replace existing empty" <| fun _ -> + let myassay = ArcAssay.init("MyAssays") + let actual = create_testInvestigation(myassay) + let next = create_testInvestigationNextEmpty() + let expected = create_testInvestigation(myassay) + actual.UpdateBy(next, true) + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title expected.Title "Title" + Expect.equal actual.Description expected.Description "Description" + Expect.equal actual.SubmissionDate expected.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate expected.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.OntologySourceReferences expected.OntologySourceReferences "OntologySourceReferences" + Expect.equal actual.Publications expected.Publications "Publications" + Expect.equal actual.Contacts expected.Contacts "Contacts" + Expect.equal actual.Assays.Count 1 "Count 1" + Expect.equal expected.Assays.Count 1 "Count 2" + TestingUtils.mySequenceEqual actual.Assays expected.Assays "Assays" + TestingUtils.mySequenceEqual actual.Studies expected.Studies "Studies" + Expect.equal actual.RegisteredStudyIdentifiers expected.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" + Expect.equal actual.Comments expected.Comments "Comments" + Expect.equal actual.Remarks expected.Remarks "Remarks" +] let main = testList "ArcInvestigation" [ @@ -554,4 +667,5 @@ let main = tests_Copy tests_Study tests_Assay + tests_UpdateBy ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 3a794f7c..1b9586f0 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -314,12 +314,184 @@ let tests_copy = () ] +let tests_UpdateBy = testList "UpdateBy" [ + let createFullStudy() = + let identifier = "MyIdentifier" + let title = "Study Title" + let description = "Study Description" + let submissionDate = "2023-07-19" + let publicReleaseDate = "2023-12-31" + let publications = [|Publication.create("Publication 1")|] + let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] + let studyDesignDescriptors = [|OntologyAnnotation.fromString("Design Descriptor")|] + let tables = ResizeArray([|ArcTable.init("Table 1")|]) + let assays = createExampleAssays() + let assay_identifiers = getAssayIdentifiers assays + let factors = [|Factor.create("Factor 1")|] + let comments = [|Comment.create("Comment 1")|] + + ArcStudy( + identifier = identifier, + title = title, + description = description, + submissionDate = submissionDate, + publicReleaseDate = publicReleaseDate, + publications = publications, + contacts = contacts, + studyDesignDescriptors = studyDesignDescriptors, + tables = tables, + registeredAssayIdentifiers = assay_identifiers, + factors = factors, + comments = comments + ) + testCase "UpdateBy, full replace" <| fun _ -> + let actual = createFullStudy() + let next = + ArcStudy( + identifier = "Next_identifier", + title = "Next_Title", + description = "Description", + submissionDate = "Next_SubmissionDate", + publicReleaseDate = "Next_PublicReleaseDate", + publications = [||], + contacts = [||], + studyDesignDescriptors = [||], + tables = ResizeArray(), + registeredAssayIdentifiers = ResizeArray(), + factors = [||], + comments = [||] + ) + actual.UpdateBy(next) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.Publications next.Publications "Publications" + Expect.equal actual.Contacts next.Contacts "Contacts" + Expect.equal actual.StudyDesignDescriptors next.StudyDesignDescriptors "StudyDesignDescriptors" + Expect.equal actual.Tables.Count next.Tables.Count "Tables.Count" // Ok + Expect.equal actual.Tables.Count 0 "Tables.Count = 0" // Ok + Expect.equal actual.Tables next.Tables "Tables" + Expect.equal actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.equal actual.Factors next.Factors "Factors" + Expect.equal actual.Comments next.Comments "Comments" + testCase "UpdateBy, replace existing, none replaced" <| fun _ -> + let actual = createFullStudy() + let next = ArcStudy.init("NextIdentifier") + let expected = createFullStudy() + actual.UpdateBy(next, true) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title expected.Title "Title" + Expect.equal actual.Description expected.Description "Description" + Expect.equal actual.SubmissionDate expected.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate expected.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.Publications expected.Publications "Publications" + Expect.equal actual.Contacts expected.Contacts "Contacts" + Expect.equal actual.StudyDesignDescriptors expected.StudyDesignDescriptors "StudyDesignDescriptors" + TestingUtils.mySequenceEqual actual.Tables expected.Tables "Tables" + TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers expected.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.equal actual.Factors expected.Factors "Factors" + Expect.equal actual.Comments expected.Comments "Comments" + testCase "UpdateBy, replace existing, all replaced" <| fun _ -> + let actual = createFullStudy() + let next = + ArcStudy( + identifier = "Next_identifier", + title = "Next_Title", + description = "Description", + submissionDate = "Next_SubmissionDate", + publicReleaseDate = "Next_PublicReleaseDate", + publications = [|Publication.create(Title="My Next Title")|], + contacts = [|Person.create(FirstName="NextKevin", LastName="NextFrey")|], + studyDesignDescriptors = [|OntologyAnnotation.fromString "Next OA"|], + tables = ResizeArray([ArcTable.init("NextTable")]), + registeredAssayIdentifiers = ResizeArray(["NextIdentifier"]), + factors = [|Factor.create(Name="NextFactor")|], + comments = [|Comment.create(Name="NextCommentName", Value="NextCommentValue")|] + ) + actual.UpdateBy(next, true) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.Publications next.Publications "Publications" + Expect.equal actual.Contacts next.Contacts "Contacts" + Expect.equal actual.StudyDesignDescriptors next.StudyDesignDescriptors "StudyDesignDescriptors" + TestingUtils.mySequenceEqual actual.Tables next.Tables "Tables" + TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.equal actual.Factors next.Factors "Factors" + Expect.equal actual.Comments next.Comments "Comments" + testCase "UpdateBy, replace existing, append" <| fun _ -> + let actual = createFullStudy() + let next = + ArcStudy( + identifier = "Next_identifier", + title = "Next_Title", + description = "Description", + submissionDate = "Next_SubmissionDate", + publicReleaseDate = "Next_PublicReleaseDate", + publications = [|Publication.create(Title="My Next Title")|], + contacts = [|Person.create(FirstName="NextKevin", LastName="NextFrey")|], + studyDesignDescriptors = [|OntologyAnnotation.fromString "Next OA"|], + tables = ResizeArray([ArcTable.init("NextTable")]) + ) + let original = createFullStudy() + actual.UpdateBy(next, true, true) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.Publications [|Publication.create("Publication 1"); Publication.create(Title="My Next Title")|] "Publications" + Expect.equal actual.Contacts [|Person.create(FirstName = "John", LastName = "Doe"); Person.create(FirstName="NextKevin", LastName="NextFrey")|] "Contacts" + Expect.equal actual.StudyDesignDescriptors [|OntologyAnnotation.fromString("Design Descriptor"); OntologyAnnotation.fromString "Next OA"|] "StudyDesignDescriptors" + TestingUtils.mySequenceEqual actual.Tables (ResizeArray([ArcTable.init("Table 1"); ArcTable.init("NextTable")])) "Tables" + TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers original.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.equal actual.Factors original.Factors "Factors" + Expect.equal actual.Comments original.Comments "Comments" + testCase "UpdateBy, replace all, append" <| fun _ -> + let actual = createFullStudy() + let next = + ArcStudy( + identifier = "Next_identifier", + title = "Next_Title", + description = "Description", + submissionDate = "Next_SubmissionDate", + publicReleaseDate = "Next_PublicReleaseDate", + publications = [|Publication.create(Title="My Next Title")|], + contacts = [|Person.create(FirstName="NextKevin", LastName="NextFrey")|], + studyDesignDescriptors = [|OntologyAnnotation.fromString "Next OA"|], + tables = ResizeArray([ArcTable.init("NextTable")]) + ) + let original = createFullStudy() + actual.UpdateBy(next, false, true) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + Expect.equal actual.Title next.Title "Title" + Expect.equal actual.Description next.Description "Description" + Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" + Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" + Expect.equal actual.Publications [|Publication.create("Publication 1"); Publication.create(Title="My Next Title")|] "Publications" + Expect.equal actual.Contacts [|Person.create(FirstName = "John", LastName = "Doe"); Person.create(FirstName="NextKevin", LastName="NextFrey")|] "Contacts" + Expect.equal actual.StudyDesignDescriptors [|OntologyAnnotation.fromString("Design Descriptor"); OntologyAnnotation.fromString "Next OA"|] "StudyDesignDescriptors" + TestingUtils.mySequenceEqual actual.Tables (ResizeArray([ArcTable.init("Table 1"); ArcTable.init("NextTable")])) "Tables" + TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers original.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.equal actual.Factors original.Factors "Factors" + Expect.equal actual.Comments original.Comments "Comments" +] let main = testList "ArcStudy" [ tests_copy tests_RegisteredAssays test_create + tests_UpdateBy ] From ed7be2e07daec26b19c54399b4ad2689a9ad646a Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Tue, 12 Sep 2023 16:19:25 +0200 Subject: [PATCH 11/17] finish up first version of updated arc read in --- src/ARCtrl/ARCtrl.fs | 61 +++++++++++------------ src/ISA/ISA/ArcTypes/ArcTable.fs | 6 +++ src/ISA/ISA/ArcTypes/ArcTableAux.fs | 10 ++++ src/ISA/ISA/ArcTypes/ArcTypes.fs | 70 +++++++++++++++++++-------- tests/ARCtrl/ARCtrl.Tests.fs | 2 +- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 13 +++-- 6 files changed, 101 insertions(+), 61 deletions(-) diff --git a/src/ARCtrl/ARCtrl.fs b/src/ARCtrl/ARCtrl.fs index 024cfa9a..2e831ea1 100644 --- a/src/ARCtrl/ARCtrl.fs +++ b/src/ARCtrl/ARCtrl.fs @@ -31,12 +31,7 @@ module ARCAux = let (studyNames,assayNames) = match isa with | Some inv -> - inv.Studies - |> Seq.fold (fun (studyNames,assayNames) s -> - Array.append studyNames [|s.Identifier|], - Array.append assayNames (s.Assays |> Seq.map (fun a -> a.Identifier) |> Array.ofSeq) - - ) ([||],[||]) + inv.StudyIdentifiers |> Seq.toArray, inv.AssayIdentifiers |> Seq.toArray | None -> ([||],[||]) let assays = FileSystemTree.createAssaysFolder (assayNames |> Array.map FileSystemTree.createAssayFolder) let studies = FileSystemTree.createStudiesFolder (studyNames |> Array.map FileSystemTree.createStudyFolder) @@ -166,32 +161,29 @@ type ARC(?isa : ISA.ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSyste /// get investigation from xlsx let investigation = ARCAux.getArcInvestigationFromContracts contracts /// get studies from xlsx - let studies = ARCAux.getArcStudiesFromContracts contracts + let studies = ARCAux.getArcStudiesFromContracts contracts |> Array.map fst /// get assays from xlsx let assays = ARCAux.getArcAssaysFromContracts contracts - investigation.Studies |> Seq.iter (fun registeredStudy -> + studies |> Seq.iter (fun study -> /// Try find registered study in parsed READ contracts - let studyOpt = studies |> Array.tryFind (fun s -> s.Identifier = registeredStudy.Identifier) - match studyOpt with - | Some study -> // This study element is parsed from FsWorkbook and has no regsitered assays, yet - - if enableLogging then printfn "Found study: %s" registeredStudy.Identifier - registeredStudy.Assays |> Seq.iter (fun registeredAssay -> - /// Try find registered assay in parsed READ contracts - let assayOpt = assays |> Array.tryFind (fun a -> a.Identifier = registeredAssay.Identifier) - match assayOpt with - | Some assay -> - if enableLogging then printfn "Found assay: %s - %s" registeredStudy.Identifier registeredAssay.Identifier - registeredAssay.AddTables(assay.Tables) - | None -> - if enableLogging then printfn "Unable to find registered assay '%s' in fullfilled READ contracts!" registeredAssay.Identifier - ) - study.Tables - |> Seq.iter (fun table -> registeredStudy.SetTable(table.Name , table)) + let registeredStudyOpt = investigation.Studies |> Seq.tryFind (fun s -> s.Identifier = study.Identifier) + match registeredStudyOpt with + | Some registeredStudy -> // This study element is parsed from FsWorkbook and has no regsitered assays, yet + registeredStudy.UpdateReferenceByStudyFile(study,true) + | None -> + investigation.AddRegisteredStudy(study) + ) + assays |> Seq.iter (fun assay -> + /// Try find registered study in parsed READ contracts + let registeredAssayOpt = investigation.Assays |> Seq.tryFind (fun a -> a.Identifier = assay.Identifier) + match registeredAssayOpt with + | Some registeredAssay -> // This study element is parsed from FsWorkbook and has no regsitered assays, yet + registeredAssay.UpdateReferenceByAssayFile(assay,true) | None -> - if enableLogging then printfn "Unable to find registered study '%s' in fullfilled READ contracts!" registeredStudy.Identifier + investigation.AddAssay(assay) ) + this.ISA <- Some investigation member this.UpdateFileSystem() = @@ -213,18 +205,19 @@ type ARC(?isa : ISA.ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSyste workbooks.Add (Path.InvestigationFileName, (DTOType.ISA_Investigation, ISA.Spreadsheet.ArcInvestigation.toFsWorkbook inv)) inv.Studies |> Seq.iter (fun s -> + workbooks.Add ( Identifier.Study.fileNameFromIdentifier s.Identifier, - (DTOType.ISA_Study, ISA.Spreadsheet.ArcStudy.toFsWorkbook s)) - s.Assays - |> Seq.iter (fun a -> - let key = Identifier.Assay.fileNameFromIdentifier a.Identifier - if workbooks.ContainsKey key |> not then - workbooks.Add ( - key, - (DTOType.ISA_Assay, ISA.Spreadsheet.ArcAssay.toFsWorkbook a)) + (DTOType.ISA_Study, ArcStudy.toFsWorkbook s) ) ) + inv.Assays + |> Seq.iter (fun a -> + workbooks.Add ( + Identifier.Assay.fileNameFromIdentifier a.Identifier, + (DTOType.ISA_Assay, ISA.Spreadsheet.ArcAssay.toFsWorkbook a)) + ) + | None -> workbooks.Add (Path.InvestigationFileName, (DTOType.ISA_Investigation, ISA.Spreadsheet.ArcInvestigation.toFsWorkbook (ArcInvestigation.create(Identifier.MISSING_IDENTIFIER)))) printfn "ARC contains no ISA part." diff --git a/src/ISA/ISA/ArcTypes/ArcTable.fs b/src/ISA/ISA/ArcTypes/ArcTable.fs index 4e970871..dc53f9f4 100644 --- a/src/ISA/ISA/ArcTypes/ArcTable.fs +++ b/src/ISA/ISA/ArcTypes/ArcTable.fs @@ -535,6 +535,12 @@ type ArcTable = |> fun rows -> ProcessParsing.alignByHeaders rows |> fun (headers, rows) -> ArcTable.create(name,headers,rows) + /// This method is meant to update an ArcTable stored as a protocol in a study or investigation file with the information from an ArcTable actually stored as an annotation table + member this.UpdateReferenceByAnnotationTable(table:ArcTable) = + ArcTableAux.Unchecked.extendToRowCount table.RowCount this.Headers this.Values + for c in table.Columns do + this.AddColumn(c.Header, cells = c.Cells,forceReplace = true) + /// Pretty printer override this.ToString() = [ diff --git a/src/ISA/ISA/ArcTypes/ArcTableAux.fs b/src/ISA/ISA/ArcTypes/ArcTableAux.fs index 30f1e607..58e749db 100644 --- a/src/ISA/ISA/ArcTypes/ArcTableAux.fs +++ b/src/ISA/ISA/ArcTypes/ArcTableAux.fs @@ -275,6 +275,16 @@ module Unchecked = for missingColumn,missingRow in missingKeys do setCellAt (missingColumn,missingRow,empty) values + /// Increases the table size to the given new row count and fills the new rows with the last value of the column + let extendToRowCount rowCount (headers: ResizeArray) (values:Dictionary) = + let columnCount = getColumnCount headers + let previousRowCount = getRowCount values + // iterate over columns + for columnIndex = 0 to columnCount - 1 do + let lastValue = values[columnIndex,previousRowCount-1] + for rowIndex = previousRowCount - 1 to rowCount - 1 do + setCellAt (columnIndex,rowIndex,lastValue) values + let addRow (index:int) (newCells:CompositeCell []) (headers: ResizeArray) (values:Dictionary) = /// Store start rowCount here, so it does not get changed midway through let rowCount = getRowCount values diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index e746fc55..07395580 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -530,6 +530,23 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno member internal this.RemoveFromInvestigation () = this.Investigation <- None + /// Updates given assay stored in an study or investigation file with values from an assay file. + member this.UpdateReferenceByAssayFile(assay:ArcAssay,?onlyReplaceExisting : bool) = + let onlyReplaceExisting = defaultArg onlyReplaceExisting false + let updateAlways = onlyReplaceExisting |> not + if assay.MeasurementType.IsSome || updateAlways then + this.MeasurementType <- assay.MeasurementType + if assay.TechnologyPlatform.IsSome || updateAlways then + this.TechnologyPlatform <- assay.TechnologyPlatform + if assay.TechnologyType.IsSome || updateAlways then + this.TechnologyType <- assay.TechnologyType + if assay.Tables.Count <> 0 || updateAlways then + this.Tables <- assay.Tables + if assay.Comments.Length <> 0 || updateAlways then + this.Comments <- assay.Comments + if assay.Performers.Length <> 0 || updateAlways then + this.Performers <- assay.Performers + /// Copies ArcAssay object without the pointer to the parent ArcInvestigation /// /// In order to copy the pointer to the parent ArcInvestigation as well, use the Copy() method of the ArcInvestigation instead. @@ -1055,7 +1072,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi this.Investigation <- None - /// Copies ArcStudy objec without the pointer to the parent ArcInvestigation + /// Copies ArcStudy object without the pointer to the parent ArcInvestigation /// /// This copy does only contain the identifiers of the registered ArcAssays and not the actual objects. /// @@ -1087,14 +1104,12 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi ) /// - /// Updates given study with another study, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. + /// Updates given study from an investigation file against a study from a study file. Identifier will never be updated. /// /// The study used for updating this study. /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** - /// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** - member this.UpdateBy(study:ArcStudy,?onlyReplaceExisting : bool,?appendSequences : bool) = + member this.UpdateReferenceByStudyFile(study:ArcStudy,?onlyReplaceExisting : bool) = let onlyReplaceExisting = defaultArg onlyReplaceExisting false - let appendSequences = defaultArg appendSequences false let updateAlways = onlyReplaceExisting |> not if study.Title.IsSome || updateAlways then this.Title <- study.Title @@ -1105,26 +1120,31 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi if study.PublicReleaseDate.IsSome || updateAlways then this.PublicReleaseDate <- study.PublicReleaseDate if study.Publications.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Publications study.Publications - this.Publications <- s + this.Publications <- study.Publications if study.Contacts.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Contacts study.Contacts - this.Contacts <- s + this.Contacts <- study.Contacts if study.StudyDesignDescriptors.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.StudyDesignDescriptors study.StudyDesignDescriptors - this.StudyDesignDescriptors <- s + this.StudyDesignDescriptors <- study.StudyDesignDescriptors if study.Tables.Count <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Tables study.Tables + let s = + study.Tables + |> Seq.append this.Tables + |> Seq.groupBy (fun t -> t.Name) + |> Seq.map (fun (_,ts) -> + if Seq.length ts = 2 then + (Seq.item 0 ts).UpdateReferenceByAnnotationTable (Seq.item 1 ts) + Seq.head ts + else + Seq.head ts + ) + |> ResizeArray this.Tables <- s if study.RegisteredAssayIdentifiers.Count <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendResizeArray appendSequences this.RegisteredAssayIdentifiers study.RegisteredAssayIdentifiers - this.RegisteredAssayIdentifiers <- s - if study.Factors.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Factors study.Factors - this.Factors <- s + this.RegisteredAssayIdentifiers <- study.RegisteredAssayIdentifiers + if study.Factors.Length <> 0 || updateAlways then + this.Factors <- study.Factors if study.Comments.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Comments study.Comments - this.Comments <- s + this.Comments <- study.Comments /// /// Creates an ISA-Json compatible Study from ArcStudy. @@ -1399,6 +1419,18 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin copy.RegisterStudy(studyIdentifier) copy + // - Study API - CRUD // + member this.AddRegisteredStudy (study: ArcStudy) = + this.AddStudy study + this.RegisterStudy(study.Identifier) + + static member addRegisteredStudy(study: ArcStudy) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + let study = study.Copy() + copy.AddRegisteredStudy(study) + copy + // - Study API - CRUD // member this.RemoveStudyAt(index: int) = this.Studies.RemoveAt(index) diff --git a/tests/ARCtrl/ARCtrl.Tests.fs b/tests/ARCtrl/ARCtrl.Tests.fs index c3922bc0..7d1c53d0 100644 --- a/tests/ARCtrl/ARCtrl.Tests.fs +++ b/tests/ARCtrl/ARCtrl.Tests.fs @@ -168,7 +168,7 @@ let private test_writeContracts = testList "write_contracts" [ let inv = ArcInvestigation("MyInvestigation", "BestTitle") let assay = ArcAssay("MyAssay") inv.InitStudy("Study1").AddRegisteredAssay(assay) |> ignore - inv.InitStudy("Study2").AddRegisteredAssay(assay) |> ignore + inv.InitStudy("Study2").RegisterAssay(assay.Identifier) |> ignore let arc = ARC(isa = inv) let contracts = arc.GetWriteContracts() let contractPathsString = contracts |> Array.map (fun c -> c.Path) |> String.concat ", " diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 1b9586f0..f9068e02 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -314,7 +314,7 @@ let tests_copy = () ] -let tests_UpdateBy = testList "UpdateBy" [ +let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ let createFullStudy() = let identifier = "MyIdentifier" let title = "Study Title" @@ -329,7 +329,6 @@ let tests_UpdateBy = testList "UpdateBy" [ let assay_identifiers = getAssayIdentifiers assays let factors = [|Factor.create("Factor 1")|] let comments = [|Comment.create("Comment 1")|] - ArcStudy( identifier = identifier, title = title, @@ -361,7 +360,7 @@ let tests_UpdateBy = testList "UpdateBy" [ factors = [||], comments = [||] ) - actual.UpdateBy(next) + actual.UpdateReferenceByStudyFile(next) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" Expect.equal actual.Title next.Title "Title" @@ -381,7 +380,7 @@ let tests_UpdateBy = testList "UpdateBy" [ let actual = createFullStudy() let next = ArcStudy.init("NextIdentifier") let expected = createFullStudy() - actual.UpdateBy(next, true) + actual.UpdateReferenceByStudyFile(next, true) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" Expect.equal actual.Title expected.Title "Title" @@ -412,7 +411,7 @@ let tests_UpdateBy = testList "UpdateBy" [ factors = [|Factor.create(Name="NextFactor")|], comments = [|Comment.create(Name="NextCommentName", Value="NextCommentValue")|] ) - actual.UpdateBy(next, true) + actual.UpdateReferenceByStudyFile(next, true) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" Expect.equal actual.Title next.Title "Title" @@ -441,7 +440,7 @@ let tests_UpdateBy = testList "UpdateBy" [ tables = ResizeArray([ArcTable.init("NextTable")]) ) let original = createFullStudy() - actual.UpdateBy(next, true, true) + actual.UpdateReferenceByStudyFile(next, true) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" Expect.equal actual.Title next.Title "Title" @@ -470,7 +469,7 @@ let tests_UpdateBy = testList "UpdateBy" [ tables = ResizeArray([ArcTable.init("NextTable")]) ) let original = createFullStudy() - actual.UpdateBy(next, false, true) + actual.UpdateReferenceByStudyFile(next, false) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" Expect.equal actual.Title next.Title "Title" From 47489d133fd647fc0aa230e1a442f93df43971aa Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Thu, 14 Sep 2023 23:19:38 +0200 Subject: [PATCH 12/17] add helper functions --- src/ISA/ISA/ArcTypes/ArcTable.fs | 47 +++- src/ISA/ISA/ArcTypes/ArcTypes.fs | 84 +++---- tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs | 228 +++++++++--------- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 24 +- tests/ISA/ISA.Tests/ArcTable.Tests.fs | 37 +++ 5 files changed, 254 insertions(+), 166 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTable.fs b/src/ISA/ISA/ArcTypes/ArcTable.fs index dc53f9f4..31a97861 100644 --- a/src/ISA/ISA/ArcTypes/ArcTable.fs +++ b/src/ISA/ISA/ArcTypes/ArcTable.fs @@ -32,6 +32,14 @@ type ArcTable = Values = System.Collections.Generic.Dictionary() } + static member initWithHeaders(name,headers : ResizeArray) = + ArcTable.create(name,headers,Dictionary()) + + static member createFromRows(name,headers : ResizeArray,rows : CompositeCell[][]) : ArcTable = + let t = ArcTable.initWithHeaders(name,headers) + t.AddRows(rows) + t + /// Will return true or false if table is valid. /// /// Set `raiseException` = `true` to raise exception. @@ -156,7 +164,7 @@ type ArcTable = column.Cells |> Array.iteri (fun rowIndex v -> Unchecked.setCellAt(columnIndex,rowIndex,v) this.Values) Unchecked.fillMissingCells this.Headers this.Values - static member updatetColumn (columnIndex:int, header: CompositeHeader, ?cells: CompositeCell []) = + static member updatedColumn (columnIndex:int, header: CompositeHeader, ?cells: CompositeCell []) = fun (table:ArcTable) -> let newTable = table.Copy() newTable.UpdateColumn(columnIndex, header, ?cells=cells) @@ -541,6 +549,43 @@ type ArcTable = for c in table.Columns do this.AddColumn(c.Header, cells = c.Cells,forceReplace = true) + static member SplitByColumnValues(columnIndex) = + fun (table : ArcTable) -> + let column = table.GetColumn(columnIndex) + let indexGroups = column.Cells |> Array.indexed |> Array.groupBy snd |> Array.map (fun (g,vs) -> vs |> Array.map fst) + indexGroups + |> Array.mapi (fun i indexGroup -> + let headers = table.Headers |> ResizeArray + let rows = + indexGroup + |> Array.map (fun i -> table.GetRow(i)) + ArcTable.createFromRows(table.Name,headers,rows) + ) + + static member SplitByColumnValuesByHeader(header : CompositeHeader) = + fun (table : ArcTable) -> + let index = table.Headers |> Seq.findIndex (fun x -> x = header) + ArcTable.SplitByColumnValues index table + + static member SplitByProtocolREF = + fun (table : ArcTable) -> + let index = table.Headers |> Seq.findIndex (fun x -> x = CompositeHeader.ProtocolREF) + ArcTable.SplitByColumnValues index table + + static member append (otherTable) = + fun (table : ArcTable) -> + let getList (t : ArcTable) = + [ + for row = 0 to t.RowCount - 1 do + [for col = 0 to t.ColumnCount - 1 do + yield t.Headers[col],t.Values[col,row] + ] + ] + let thisCells = getList table + let otherCells = getList otherTable + let alignedheaders,alignedCells = ArcTableAux.ProcessParsing.alignByHeaders (thisCells @ otherCells) + ArcTable.create(table.Name,alignedheaders,alignedCells) + /// Pretty printer override this.ToString() = [ diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 07395580..f0552d98 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -1605,48 +1605,48 @@ type ArcInvestigation(identifier : string, ?title : string, ?description : strin ) i - /// - /// Updates given investigation with another investigation, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. - /// - /// The investigation used for updating this investigation. - /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** - /// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** - member this.UpdateBy(inv:ArcInvestigation,?onlyReplaceExisting : bool,?appendSequences : bool) = - let onlyReplaceExisting = defaultArg onlyReplaceExisting false - let appendSequences = defaultArg appendSequences false - let updateAlways = onlyReplaceExisting |> not - if inv.Title.IsSome || updateAlways then - this.Title <- inv.Title - if inv.Description.IsSome || updateAlways then - this.Description <- inv.Description - if inv.SubmissionDate.IsSome || updateAlways then - this.SubmissionDate <- inv.SubmissionDate - if inv.PublicReleaseDate.IsSome || updateAlways then - this.PublicReleaseDate <- inv.PublicReleaseDate - if inv.OntologySourceReferences.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.OntologySourceReferences inv.OntologySourceReferences - this.OntologySourceReferences <- s - if inv.Publications.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Publications inv.Publications - this.Publications <- s - if inv.Contacts.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Contacts inv.Contacts - this.Contacts <- s - if inv.Assays.Count <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Assays inv.Assays - this.Assays <- s - if inv.Studies.Count <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Studies inv.Studies - this.Studies <- s - if inv.RegisteredStudyIdentifiers.Count <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendResizeArray appendSequences this.RegisteredStudyIdentifiers inv.RegisteredStudyIdentifiers - this.RegisteredStudyIdentifiers <- s - if inv.Comments.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Comments inv.Comments - this.Comments <- s - if inv.Remarks.Length <> 0 || updateAlways then - let s = ArcTypesAux.updateAppendArray appendSequences this.Remarks inv.Remarks - this.Remarks <- s + ///// + ///// Updates given investigation with another investigation, Identifier will never be updated. By default update is full replace. Optional Parameters can be used to specify update logic. + ///// + ///// The investigation used for updating this investigation. + ///// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** + ///// If true, this will append lists instead of replacing. Will return only distinct elements. Default: **false** + //member this.UpdateBy(inv:ArcInvestigation,?onlyReplaceExisting : bool,?appendSequences : bool) = + // let onlyReplaceExisting = defaultArg onlyReplaceExisting false + // let appendSequences = defaultArg appendSequences false + // let updateAlways = onlyReplaceExisting |> not + // if inv.Title.IsSome || updateAlways then + // this.Title <- inv.Title + // if inv.Description.IsSome || updateAlways then + // this.Description <- inv.Description + // if inv.SubmissionDate.IsSome || updateAlways then + // this.SubmissionDate <- inv.SubmissionDate + // if inv.PublicReleaseDate.IsSome || updateAlways then + // this.PublicReleaseDate <- inv.PublicReleaseDate + // if inv.OntologySourceReferences.Length <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendArray appendSequences this.OntologySourceReferences inv.OntologySourceReferences + // this.OntologySourceReferences <- s + // if inv.Publications.Length <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendArray appendSequences this.Publications inv.Publications + // this.Publications <- s + // if inv.Contacts.Length <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendArray appendSequences this.Contacts inv.Contacts + // this.Contacts <- s + // if inv.Assays.Count <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Assays inv.Assays + // this.Assays <- s + // if inv.Studies.Count <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendResizeArray appendSequences this.Studies inv.Studies + // this.Studies <- s + // if inv.RegisteredStudyIdentifiers.Count <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendResizeArray appendSequences this.RegisteredStudyIdentifiers inv.RegisteredStudyIdentifiers + // this.RegisteredStudyIdentifiers <- s + // if inv.Comments.Length <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendArray appendSequences this.Comments inv.Comments + // this.Comments <- s + // if inv.Remarks.Length <> 0 || updateAlways then + // let s = ArcTypesAux.updateAppendArray appendSequences this.Remarks inv.Remarks + // this.Remarks <- s /// Transform an ArcInvestigation to an ISA Json Investigation. member this.ToInvestigation() : Investigation = diff --git a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs index 895c1d5a..02b04026 100644 --- a/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcInvestigation.Tests.fs @@ -545,119 +545,119 @@ let tests_Assay = testList "CRUD Assay" [ ] ] -let tests_UpdateBy = testList "UpdateBy" [ - let create_testInvestigation(assay) = - ArcInvestigation.create( - "MyInvestigation", - "MyTitle", - "MyDescription", - "MySubmissionDate", - "MyPublicReleaseDate", - [|OntologySourceReference.create(Name="MyOntologySourceReference")|], - [|Publication.create(Title="MyPublication")|], - [|Person.create(FirstName="Kevin", LastName="Frey")|], - ResizeArray([assay]), - ResizeArray([ArcStudy.init("MyStudy")]), - ResizeArray(["MyStudy"]), - [|Comment.create(Name="MyComment")|], - [|Remark.create(1,"MyRemark")|] - ) - let create_testInvestigationNextEmpty() = - ArcInvestigation.init("NextEmptyInvestigation") - let create_testInvestigationNext(assay) = - ArcInvestigation.create( - "NextInvestigation", - "NextTitle", - "NextDescription", - "NextSubmissionDate", - "NextPublicReleaseDate", - [|OntologySourceReference.create(Name="NextOntologySourceReference")|], - [|Publication.create(Title="NextPublication")|], - [|Person.create(FirstName="Kevin", LastName="Frey")|], - ResizeArray([assay]), - ResizeArray([ArcStudy.init("NextStudy")]), - ResizeArray(["NextStudy"]), - [|Comment.create(Name="NextComment")|], - [|Remark.create(12, "NextRemark")|] - ) - testCase "UpdateBy, full replace" <| fun _ -> - let myassay = ArcAssay.init("MyAssays") - let nextassay = ArcAssay.init("nextassay") - let actual = create_testInvestigation(myassay) - let next = create_testInvestigationNext(nextassay) - actual.UpdateBy(next) - Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" - Expect.equal actual.Publications next.Publications "Publications" - Expect.equal actual.Contacts next.Contacts "Contacts" - Expect.equal actual.Assays next.Assays "Assays" - Expect.equal actual.Studies next.Studies "Studies" - Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" - Expect.equal actual.Comments next.Comments "Comments" - Expect.equal actual.Remarks next.Remarks "Remarks" - testCase "UpdateBy, full replace empty" <| fun _ -> - let myassay = ArcAssay.init("MyAssays") - let actual = create_testInvestigation(myassay) - let next = create_testInvestigationNextEmpty() - actual.UpdateBy(next) - Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" - Expect.equal actual.Publications next.Publications "Publications" - Expect.equal actual.Contacts next.Contacts "Contacts" - Expect.equal actual.Assays next.Assays "Assays" - Expect.equal actual.Studies next.Studies "Studies" - Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" - Expect.equal actual.Comments next.Comments "Comments" - Expect.equal actual.Remarks next.Remarks "Remarks" - testCase "UpdateBy, replace existing" <| fun _ -> - let myassay = ArcAssay.init("MyAssays") - let nextassay = ArcAssay.init("nextassay") - let actual = create_testInvestigation(myassay) - let next = create_testInvestigationNext(nextassay) - actual.UpdateBy(next, true) - Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" - Expect.equal actual.Publications next.Publications "Publications" - Expect.equal actual.Contacts next.Contacts "Contacts" - Expect.equal actual.Assays next.Assays "Assays" - Expect.equal actual.Studies next.Studies "Studies" - Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" - Expect.equal actual.Comments next.Comments "Comments" - Expect.equal actual.Remarks next.Remarks "Remarks" - testCase "UpdateBy, replace existing empty" <| fun _ -> - let myassay = ArcAssay.init("MyAssays") - let actual = create_testInvestigation(myassay) - let next = create_testInvestigationNextEmpty() - let expected = create_testInvestigation(myassay) - actual.UpdateBy(next, true) - Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title expected.Title "Title" - Expect.equal actual.Description expected.Description "Description" - Expect.equal actual.SubmissionDate expected.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate expected.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.OntologySourceReferences expected.OntologySourceReferences "OntologySourceReferences" - Expect.equal actual.Publications expected.Publications "Publications" - Expect.equal actual.Contacts expected.Contacts "Contacts" - Expect.equal actual.Assays.Count 1 "Count 1" - Expect.equal expected.Assays.Count 1 "Count 2" - TestingUtils.mySequenceEqual actual.Assays expected.Assays "Assays" - TestingUtils.mySequenceEqual actual.Studies expected.Studies "Studies" - Expect.equal actual.RegisteredStudyIdentifiers expected.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" - Expect.equal actual.Comments expected.Comments "Comments" - Expect.equal actual.Remarks expected.Remarks "Remarks" -] +//let tests_UpdateBy = testList "UpdateBy" [ +// let create_testInvestigation(assay) = +// ArcInvestigation.create( +// "MyInvestigation", +// "MyTitle", +// "MyDescription", +// "MySubmissionDate", +// "MyPublicReleaseDate", +// [|OntologySourceReference.create(Name="MyOntologySourceReference")|], +// [|Publication.create(Title="MyPublication")|], +// [|Person.create(FirstName="Kevin", LastName="Frey")|], +// ResizeArray([assay]), +// ResizeArray([ArcStudy.init("MyStudy")]), +// ResizeArray(["MyStudy"]), +// [|Comment.create(Name="MyComment")|], +// [|Remark.create(1,"MyRemark")|] +// ) +// let create_testInvestigationNextEmpty() = +// ArcInvestigation.init("NextEmptyInvestigation") +// let create_testInvestigationNext(assay) = +// ArcInvestigation.create( +// "NextInvestigation", +// "NextTitle", +// "NextDescription", +// "NextSubmissionDate", +// "NextPublicReleaseDate", +// [|OntologySourceReference.create(Name="NextOntologySourceReference")|], +// [|Publication.create(Title="NextPublication")|], +// [|Person.create(FirstName="Kevin", LastName="Frey")|], +// ResizeArray([assay]), +// ResizeArray([ArcStudy.init("NextStudy")]), +// ResizeArray(["NextStudy"]), +// [|Comment.create(Name="NextComment")|], +// [|Remark.create(12, "NextRemark")|] +// ) +// testCase "UpdateBy, full replace" <| fun _ -> +// let myassay = ArcAssay.init("MyAssays") +// let nextassay = ArcAssay.init("nextassay") +// let actual = create_testInvestigation(myassay) +// let next = create_testInvestigationNext(nextassay) +// actual.UpdateBy(next) +// Expect.notEqual actual.Identifier next.Identifier "Identifier" +// Expect.equal actual.Title next.Title "Title" +// Expect.equal actual.Description next.Description "Description" +// Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" +// Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" +// Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" +// Expect.equal actual.Publications next.Publications "Publications" +// Expect.equal actual.Contacts next.Contacts "Contacts" +// Expect.equal actual.Assays next.Assays "Assays" +// Expect.equal actual.Studies next.Studies "Studies" +// Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" +// Expect.equal actual.Comments next.Comments "Comments" +// Expect.equal actual.Remarks next.Remarks "Remarks" +// testCase "UpdateBy, full replace empty" <| fun _ -> +// let myassay = ArcAssay.init("MyAssays") +// let actual = create_testInvestigation(myassay) +// let next = create_testInvestigationNextEmpty() +// actual.UpdateBy(next) +// Expect.notEqual actual.Identifier next.Identifier "Identifier" +// Expect.equal actual.Title next.Title "Title" +// Expect.equal actual.Description next.Description "Description" +// Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" +// Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" +// Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" +// Expect.equal actual.Publications next.Publications "Publications" +// Expect.equal actual.Contacts next.Contacts "Contacts" +// Expect.equal actual.Assays next.Assays "Assays" +// Expect.equal actual.Studies next.Studies "Studies" +// Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" +// Expect.equal actual.Comments next.Comments "Comments" +// Expect.equal actual.Remarks next.Remarks "Remarks" +// testCase "UpdateBy, replace existing" <| fun _ -> +// let myassay = ArcAssay.init("MyAssays") +// let nextassay = ArcAssay.init("nextassay") +// let actual = create_testInvestigation(myassay) +// let next = create_testInvestigationNext(nextassay) +// actual.UpdateBy(next, true) +// Expect.notEqual actual.Identifier next.Identifier "Identifier" +// Expect.equal actual.Title next.Title "Title" +// Expect.equal actual.Description next.Description "Description" +// Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" +// Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" +// Expect.equal actual.OntologySourceReferences next.OntologySourceReferences "OntologySourceReferences" +// Expect.equal actual.Publications next.Publications "Publications" +// Expect.equal actual.Contacts next.Contacts "Contacts" +// Expect.equal actual.Assays next.Assays "Assays" +// Expect.equal actual.Studies next.Studies "Studies" +// Expect.equal actual.RegisteredStudyIdentifiers next.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" +// Expect.equal actual.Comments next.Comments "Comments" +// Expect.equal actual.Remarks next.Remarks "Remarks" +// testCase "UpdateBy, replace existing empty" <| fun _ -> +// let myassay = ArcAssay.init("MyAssays") +// let actual = create_testInvestigation(myassay) +// let next = create_testInvestigationNextEmpty() +// let expected = create_testInvestigation(myassay) +// actual.UpdateBy(next, true) +// Expect.notEqual actual.Identifier next.Identifier "Identifier" +// Expect.equal actual.Title expected.Title "Title" +// Expect.equal actual.Description expected.Description "Description" +// Expect.equal actual.SubmissionDate expected.SubmissionDate "SubmissionDate" +// Expect.equal actual.PublicReleaseDate expected.PublicReleaseDate "PublicReleaseDate" +// Expect.equal actual.OntologySourceReferences expected.OntologySourceReferences "OntologySourceReferences" +// Expect.equal actual.Publications expected.Publications "Publications" +// Expect.equal actual.Contacts expected.Contacts "Contacts" +// Expect.equal actual.Assays.Count 1 "Count 1" +// Expect.equal expected.Assays.Count 1 "Count 2" +// TestingUtils.mySequenceEqual actual.Assays expected.Assays "Assays" +// TestingUtils.mySequenceEqual actual.Studies expected.Studies "Studies" +// Expect.equal actual.RegisteredStudyIdentifiers expected.RegisteredStudyIdentifiers "RegisteredStudyIdentifiers" +// Expect.equal actual.Comments expected.Comments "Comments" +// Expect.equal actual.Remarks expected.Remarks "Remarks" +//] let main = testList "ArcInvestigation" [ @@ -667,5 +667,5 @@ let main = tests_Copy tests_Study tests_Assay - tests_UpdateBy + // tests_UpdateBy ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index f9068e02..49db1e37 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -315,6 +315,10 @@ let tests_copy = ] let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ + + let protocolREF = "MyProtocol" + let protocolDescription = "MyProtocolDescription" + let createFullStudy() = let identifier = "MyIdentifier" let title = "Study Title" @@ -324,7 +328,11 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ let publications = [|Publication.create("Publication 1")|] let contacts = [|Person.create(FirstName = "John", LastName = "Doe")|] let studyDesignDescriptors = [|OntologyAnnotation.fromString("Design Descriptor")|] - let tables = ResizeArray([|ArcTable.init("Table 1")|]) + let tables = + let refTable = ArcTable.init(protocolREF) + refTable.AddProtocolNameColumn [|protocolREF|] + refTable.AddProtocolDescriptionColumn [|protocolDescription|] + ResizeArray([|refTable|]) let assays = createExampleAssays() let assay_identifiers = getAssayIdentifiers assays let factors = [|Factor.create("Factor 1")|] @@ -343,7 +351,7 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ factors = factors, comments = comments ) - testCase "UpdateBy, full replace" <| fun _ -> + testCase "full replace, no tables" <| fun _ -> let actual = createFullStudy() let next = ArcStudy( @@ -370,13 +378,11 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ Expect.equal actual.Publications next.Publications "Publications" Expect.equal actual.Contacts next.Contacts "Contacts" Expect.equal actual.StudyDesignDescriptors next.StudyDesignDescriptors "StudyDesignDescriptors" - Expect.equal actual.Tables.Count next.Tables.Count "Tables.Count" // Ok - Expect.equal actual.Tables.Count 0 "Tables.Count = 0" // Ok - Expect.equal actual.Tables next.Tables "Tables" + Expect.equal actual.Tables.Count 1 "Tables.Count = 0" // Ok Expect.equal actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" Expect.equal actual.Factors next.Factors "Factors" Expect.equal actual.Comments next.Comments "Comments" - testCase "UpdateBy, replace existing, none replaced" <| fun _ -> + testCase "replace existing, none replaced" <| fun _ -> let actual = createFullStudy() let next = ArcStudy.init("NextIdentifier") let expected = createFullStudy() @@ -394,7 +400,7 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers expected.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" Expect.equal actual.Factors expected.Factors "Factors" Expect.equal actual.Comments expected.Comments "Comments" - testCase "UpdateBy, replace existing, all replaced" <| fun _ -> + testCase "replace existing, all replaced" <| fun _ -> let actual = createFullStudy() let next = ArcStudy( @@ -425,7 +431,7 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" Expect.equal actual.Factors next.Factors "Factors" Expect.equal actual.Comments next.Comments "Comments" - testCase "UpdateBy, replace existing, append" <| fun _ -> + testCase "replace existing, append" <| fun _ -> let actual = createFullStudy() let next = ArcStudy( @@ -454,7 +460,7 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers original.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" Expect.equal actual.Factors original.Factors "Factors" Expect.equal actual.Comments original.Comments "Comments" - testCase "UpdateBy, replace all, append" <| fun _ -> + testCase "full replace, append" <| fun _ -> let actual = createFullStudy() let next = ArcStudy( diff --git a/tests/ISA/ISA.Tests/ArcTable.Tests.fs b/tests/ISA/ISA.Tests/ArcTable.Tests.fs index 185dabc3..899c961d 100644 --- a/tests/ISA/ISA.Tests/ArcTable.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcTable.Tests.fs @@ -1947,7 +1947,43 @@ let private tests_AddRows = ) ] +let private tests_UpdateRefWithSheet = + testList "tests_UpdateRefWithSheet" [ + testCase "singleREFAndDescription" (fun () -> + let protocolREF = "MyProtocol" + let protocolDescription = "MyProtocolDescription" + let refTable = ArcTable.init("Table") + refTable.AddProtocolNameColumn [|protocolREF|] + refTable.AddProtocolDescriptionColumn [|protocolDescription|] + let valueTable = ArcTable.init("Table") + let columns = [| + column_input + column_output + column_component + |] + valueTable.AddColumns(columns) + let expectedRowCount = valueTable.RowCount + let expectedColumnCount = 5 + refTable.UpdateReferenceByAnnotationTable valueTable + Expect.equal valueTable.ColumnCount 3 "ColumnCount of value table should not change after update" + + Expect.equal refTable.RowCount expectedRowCount "RowCount of reference table should be the same as value table after update" + Expect.equal refTable.ColumnCount expectedColumnCount "ColumnCount of reference table should be the sum of value table and protocol table after update" + + TestingUtils.mySequenceEqual + (refTable.GetProtocolDescriptionColumn().Cells) + (Array.create 5 (CompositeCell.createFreeText protocolDescription)) + "ProtocolDescriptionColumn should be filled with protocol description" + TestingUtils.mySequenceEqual + (refTable.GetColumnByHeader column_component.Header).Cells + column_component.Cells + "Component column should have been taken as is" + ) + testCase "doubleREFAndDescription" (fun () -> + Expect.isTrue false "implement" + ) + ] let main = testList "ArcTable" [ @@ -1967,4 +2003,5 @@ let main = tests_AddRow tests_AddRows tests_validate + tests_UpdateRefWithSheet ] \ No newline at end of file From bf7b5cad3e4e8f131076a77d79dbf03bd6e06b6e Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Fri, 15 Sep 2023 15:48:41 +0200 Subject: [PATCH 13/17] add table updating tests and fix underlying functions --- src/ISA/ISA/ArcTypes/ArcTable.fs | 52 +++-- src/ISA/ISA/ArcTypes/ArcTableAux.fs | 36 ++- src/ISA/ISA/ArcTypes/ArcTables.fs | 42 +++- src/ISA/ISA/ArcTypes/ArcTypes.fs | 15 +- src/ISA/ISA/ArcTypes/CompositeHeader.fs | 5 + src/ISA/ISA/Update.fs | 12 + tests/ISA/ISA.Tests/ArcTable.Tests.fs | 56 ++++- tests/ISA/ISA.Tests/ArcTables.Tests.fs | 278 ++++++++++++++++++++++++ tests/ISA/ISA.Tests/ISA.Tests.fsproj | 1 + tests/ISA/ISA.Tests/Main.fs | 1 + 10 files changed, 454 insertions(+), 44 deletions(-) create mode 100644 tests/ISA/ISA.Tests/ArcTables.Tests.fs diff --git a/src/ISA/ISA/ArcTypes/ArcTable.fs b/src/ISA/ISA/ArcTypes/ArcTable.fs index 31a97861..01906955 100644 --- a/src/ISA/ISA/ArcTypes/ArcTable.fs +++ b/src/ISA/ISA/ArcTypes/ArcTable.fs @@ -540,12 +540,18 @@ type ArcTable = static member fromProcesses name (ps : Process list) : ArcTable = ps |> List.collect (fun p -> ProcessParsing.processToRows p) - |> fun rows -> ProcessParsing.alignByHeaders rows + |> fun rows -> ProcessParsing.alignByHeaders true rows |> fun (headers, rows) -> ArcTable.create(name,headers,rows) /// This method is meant to update an ArcTable stored as a protocol in a study or investigation file with the information from an ArcTable actually stored as an annotation table member this.UpdateReferenceByAnnotationTable(table:ArcTable) = - ArcTableAux.Unchecked.extendToRowCount table.RowCount this.Headers this.Values + let nonProtocolColumns = + this.Headers + |> Seq.indexed + |> Seq.choose (fun (i,h) -> if h.isProtocolColumn then None else Some i) + |> Seq.toArray + this.RemoveColumns nonProtocolColumns + ArcTableAux.Unchecked.extendToRowCount table.RowCount this.Headers this.Values for c in table.Columns do this.AddColumn(c.Header, cells = c.Cells,forceReplace = true) @@ -564,27 +570,33 @@ type ArcTable = static member SplitByColumnValuesByHeader(header : CompositeHeader) = fun (table : ArcTable) -> - let index = table.Headers |> Seq.findIndex (fun x -> x = header) - ArcTable.SplitByColumnValues index table + let index = table.Headers |> Seq.tryFindIndex (fun x -> x = header) + match index with + | Some i -> ArcTable.SplitByColumnValues i table + | None -> [|table.Copy()|] static member SplitByProtocolREF = fun (table : ArcTable) -> - let index = table.Headers |> Seq.findIndex (fun x -> x = CompositeHeader.ProtocolREF) - ArcTable.SplitByColumnValues index table - - static member append (otherTable) = - fun (table : ArcTable) -> - let getList (t : ArcTable) = - [ - for row = 0 to t.RowCount - 1 do - [for col = 0 to t.ColumnCount - 1 do - yield t.Headers[col],t.Values[col,row] - ] - ] - let thisCells = getList table - let otherCells = getList otherTable - let alignedheaders,alignedCells = ArcTableAux.ProcessParsing.alignByHeaders (thisCells @ otherCells) - ArcTable.create(table.Name,alignedheaders,alignedCells) + ArcTable.SplitByColumnValuesByHeader CompositeHeader.ProtocolREF table + + + /// Append the rows of another table to this one + /// + /// The headers of the other table will be aligned with the headers of this table + /// + /// The name of table 2 will be ignored + static member append table1 table2 = + let getList (t : ArcTable) = + [ + for row = 0 to t.RowCount - 1 do + [for col = 0 to t.ColumnCount - 1 do + yield t.Headers[col],t.Values[col,row] + ] + ] + let thisCells = getList table1 + let otherCells = getList table2 + let alignedheaders,alignedCells = ArcTableAux.ProcessParsing.alignByHeaders false (thisCells @ otherCells) + ArcTable.create(table1.Name,alignedheaders,alignedCells) /// Pretty printer override this.ToString() = diff --git a/src/ISA/ISA/ArcTypes/ArcTableAux.fs b/src/ISA/ISA/ArcTypes/ArcTableAux.fs index 58e749db..2cff917c 100644 --- a/src/ISA/ISA/ArcTypes/ArcTableAux.fs +++ b/src/ISA/ISA/ArcTypes/ArcTableAux.fs @@ -3,6 +3,9 @@ open ARCtrl.ISA open System.Collections.Generic + + + // Taken from FSharpAux.Core /// .Net Dictionary module Dictionary = @@ -749,7 +752,9 @@ module ProcessParsing = /// The values cant be directly taken as they are, as there is no guarantee that the headers are aligned /// /// This function aligns the headers and values by the main header string - let alignByHeaders (rows : ((CompositeHeader * CompositeCell) list) list) = + /// + /// If keepOrder is true, the order of values per row is kept intact, otherwise the values are allowed to be reordered + let alignByHeaders (keepOrder : bool) (rows : ((CompositeHeader * CompositeCell) list) list) = let headers : ResizeArray = ResizeArray() let values : Dictionary = Dictionary() let getFirstElem (rows : ('T list) list) : 'T = @@ -758,19 +763,34 @@ module ProcessParsing = if List.exists (List.isEmpty >> not) rows |> not then headers,values else + let firstElem = rows |> getFirstElem |> fst headers.Add firstElem let rows = rows |> List.mapi (fun rowI l -> - match l with - | [] -> [] - | (h,c)::t -> - if compositeHeaderEqual h firstElem then - values.Add((colI,rowI),c) - t - else + if keepOrder then + match l with + | [] -> [] + | (h,c)::t -> + if compositeHeaderEqual h firstElem then + values.Add((colI,rowI),c) + t + else + l + + else + let firstMatch,newL = l + |> Aux.List.tryPickAndRemove (fun (h,c) -> + if compositeHeaderEqual h firstElem then Some c + else None + ) + match firstMatch with + | Some m -> + values.Add((colI,rowI),m) + newL + | None -> newL ) loop (colI+1) rows loop 0 rows diff --git a/src/ISA/ISA/ArcTypes/ArcTables.fs b/src/ISA/ISA/ArcTypes/ArcTables.fs index 3c62fba0..ed4ea023 100644 --- a/src/ISA/ISA/ArcTypes/ArcTables.fs +++ b/src/ISA/ISA/ArcTypes/ArcTables.fs @@ -1,5 +1,7 @@ namespace ARCtrl.ISA +open System.Collections.Generic + module ArcTablesAux = ///// 👀 Please do not remove this here until i copied it to Swate ~Kevin F @@ -286,6 +288,11 @@ type ArcTables(thisTables:ResizeArray) = |> Seq.toList |> List.collect (fun t -> t.GetProcesses()) + static member ofSeq (tables : ArcTable seq) : ArcTables = + tables + |> ResizeArray + |> ArcTables + /// 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 @@ -297,8 +304,41 @@ type ArcTables(thisTables:ResizeArray) = |> List.map (fun (name,ps) -> ps |> List.collect (fun p -> ProcessParsing.processToRows p) - |> fun rows -> ProcessParsing.alignByHeaders rows + |> fun rows -> ProcessParsing.alignByHeaders true rows |> fun (headers, rows) -> ArcTable.create(name,headers,rows) ) |> ResizeArray + |> ArcTables + + static member updateReferenceTablesBySheets (referenceTables : ArcTables) (sheetTables : ArcTables) : ArcTables = + let referenceTableMap = + referenceTables.Tables |> Seq.map (fun t -> t.GetProtocolNameColumn().Cells.[0].AsFreeText, t) |> Map.ofSeq + sheetTables.Tables + |> Seq.collect ArcTable.SplitByProtocolREF + |> Seq.map (fun t -> + let k = + t.Headers |> Seq.tryFindIndex (fun x -> x = CompositeHeader.ProtocolREF) + |> Option.bind (fun i -> + t.TryGetCellAt(i,0) + ) + |> Option.bind (fun c -> + if c.AsFreeText = ""then None + else Some c.AsFreeText ) + |> Option.defaultValue t.Name + match Map.tryFind k referenceTableMap with + | Some rt -> + let rt = rt.Copy() + rt.UpdateReferenceByAnnotationTable t + ArcTable.create(t.Name, rt.Headers, rt.Values) + | None -> t + ) + |> Seq.groupBy (fun t -> t.Name) + |> Seq.map (fun (_,ts) -> + ts + |> Seq.reduce ArcTable.append + ) + |> Seq.map (fun t -> + ArcTableAux.Unchecked.fillMissingCells t.Headers t.Values + t) + |> ResizeArray |> ArcTables \ No newline at end of file diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index f0552d98..75374e40 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -1126,19 +1126,8 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi if study.StudyDesignDescriptors.Length <> 0 || updateAlways then this.StudyDesignDescriptors <- study.StudyDesignDescriptors if study.Tables.Count <> 0 || updateAlways then - let s = - study.Tables - |> Seq.append this.Tables - |> Seq.groupBy (fun t -> t.Name) - |> Seq.map (fun (_,ts) -> - if Seq.length ts = 2 then - (Seq.item 0 ts).UpdateReferenceByAnnotationTable (Seq.item 1 ts) - Seq.head ts - else - Seq.head ts - ) - |> ResizeArray - this.Tables <- s + let tables = ArcTables.updateReferenceTablesBySheets (ArcTables(this.Tables)) (ArcTables(study.Tables)) + this.Tables <- tables.Tables if study.RegisteredAssayIdentifiers.Count <> 0 || updateAlways then this.RegisteredAssayIdentifiers <- study.RegisteredAssayIdentifiers if study.Factors.Length <> 0 || updateAlways then diff --git a/src/ISA/ISA/ArcTypes/CompositeHeader.fs b/src/ISA/ISA/ArcTypes/CompositeHeader.fs index e1d90f24..20e98560 100644 --- a/src/ISA/ISA/ArcTypes/CompositeHeader.fs +++ b/src/ISA/ISA/ArcTypes/CompositeHeader.fs @@ -308,6 +308,11 @@ type CompositeHeader = | ProtocolVersion -> true | anythingElse -> false + member this.isProtocolColumn = + match this with + | ProtocolREF | ProtocolDescription | ProtocolUri | ProtocolVersion | ProtocolType -> true + | anythingElse -> false + member this.isPerformer = match this with | Performer -> true diff --git a/src/ISA/ISA/Update.fs b/src/ISA/ISA/Update.fs index ff685e13..b1147191 100644 --- a/src/ISA/ISA/Update.fs +++ b/src/ISA/ISA/Update.fs @@ -1,5 +1,17 @@ namespace ARCtrl.ISA.Aux +module List = + + let tryPickAndRemove (f : 'T -> 'U option) (lst : 'T list) = + let rec loop newList remainingList = + match remainingList with + | h::t -> + match f h with + | Some v -> Some v, newList @ t + | None -> loop (newList @ [h]) t + | _ -> None, newList + loop [] lst + module Dict = open System.Collections.Generic diff --git a/tests/ISA/ISA.Tests/ArcTable.Tests.fs b/tests/ISA/ISA.Tests/ArcTable.Tests.fs index 899c961d..f0e1ce62 100644 --- a/tests/ISA/ISA.Tests/ArcTable.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcTable.Tests.fs @@ -1980,8 +1980,60 @@ let private tests_UpdateRefWithSheet = column_component.Cells "Component column should have been taken as is" ) - testCase "doubleREFAndDescription" (fun () -> - Expect.isTrue false "implement" + testCase "OverwriteDescription" (fun () -> + let protocolREF = "MyProtocol" + let protocolDescription = "MyProtocolDescription" + let newProtocolDescription = "Improved ProtocolDescription" + let refTable = ArcTable.init("Table") + refTable.AddProtocolNameColumn [|protocolREF|] + refTable.AddProtocolDescriptionColumn [|protocolDescription|] + let valueTable = ArcTable.init("Table") + let columns = [| + column_input + column_output + column_component + |] + valueTable.AddColumns(columns) + valueTable.AddProtocolDescriptionColumn (Array.create 5 newProtocolDescription) + let expectedRowCount = valueTable.RowCount + let expectedColumnCount = 5 + refTable.UpdateReferenceByAnnotationTable valueTable + + Expect.equal valueTable.ColumnCount 4 "ColumnCount of value table should not change after update" + + Expect.equal refTable.RowCount expectedRowCount "RowCount of reference table should be the same as value table after update" + Expect.equal refTable.ColumnCount expectedColumnCount "ColumnCount of reference table should be the sum of value table and protocol table after update" + + TestingUtils.mySequenceEqual + (refTable.GetProtocolDescriptionColumn().Cells) + (Array.create 5 (CompositeCell.createFreeText newProtocolDescription)) + "ProtocolDescriptionColumn should be filled with protocol description" + TestingUtils.mySequenceEqual + (refTable.GetColumnByHeader column_component.Header).Cells + column_component.Cells + "Component column should have been taken as is" + ) + testCase "DropParams" (fun () -> + let protocolREF = "MyProtocol" + let protocolDescription = "MyProtocolDescription" + let refTable = ArcTable.init("Table") + refTable.AddProtocolNameColumn [|protocolREF|] + refTable.AddProtocolDescriptionColumn [|protocolDescription|] + refTable.AddColumn(CompositeHeader.Parameter oa_species, [|CompositeCell.createTerm oa_chlamy|]) + let valueTable = ArcTable.init("Table") + let columns = [| + column_input + column_output + column_component + |] + valueTable.AddColumns(columns) + let expectedRowCount = valueTable.RowCount + let expectedColumnCount = 5 + refTable.UpdateReferenceByAnnotationTable valueTable + + Expect.equal refTable.RowCount expectedRowCount "RowCount of reference table should be the same as value table after update" + Expect.equal refTable.ColumnCount expectedColumnCount "ColumnCount of reference table should be the sum of value table and protocol table after update minus the param columns" + ) ] diff --git a/tests/ISA/ISA.Tests/ArcTables.Tests.fs b/tests/ISA/ISA.Tests/ArcTables.Tests.fs new file mode 100644 index 00000000..c2e0a863 --- /dev/null +++ b/tests/ISA/ISA.Tests/ArcTables.Tests.fs @@ -0,0 +1,278 @@ +module ArcTables.Tests + +open ARCtrl.ISA + +#if FABLE_COMPILER +open Fable.Mocha +#else +open Expecto +#endif + +open TestingUtils +module TestObjects = + let TableName = "Test" + let oa_species = OntologyAnnotation.fromString("species", "GO", "GO:0123456") + let oa_chlamy = OntologyAnnotation.fromString("Chlamy", "NCBI", "NCBI:0123456") + let oa_instrumentModel = OntologyAnnotation.fromString("instrument model", "MS", "MS:0123456") + let oa_SCIEXInstrumentModel = OntologyAnnotation.fromString("SCIEX instrument model", "MS", "MS:654321") + let oa_temperature = OntologyAnnotation.fromString("temperature","NCIT","NCIT:0123210") + + + let protocolName1 = "Protocol 1" + let protocolName2 = "Protocol 2" + let descriptionValue1 = "Protocol is good" + let descriptionValue2 = "Protocol is bad" + let versionValue1 = "1.0.0" + + let sheetWithNoREF() = + let t = ArcTable.init(protocolName1) + let inputHeader = CompositeHeader.Input IOType.Sample + let paramHeader = CompositeHeader.Parameter oa_species + let paramValue = CompositeCell.createTerm oa_chlamy + t.AddColumns + [| + CompositeColumn.create(inputHeader, Array.init 2 (fun i -> CompositeCell.createFreeText $"{i}")) + CompositeColumn.create(paramHeader, Array.create 2 paramValue) + |] + t + + let sheetWithREF() = + let t = ArcTable.init("Simple") + let inputHeader = CompositeHeader.Input IOType.Sample + let paramHeader = CompositeHeader.Parameter oa_species + let paramValue = CompositeCell.createTerm oa_chlamy + t.AddColumns + [| + CompositeColumn.create(inputHeader, Array.init 2 (fun i -> CompositeCell.createFreeText $"{i}")) + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 2 (CompositeCell.createFreeText protocolName1)) + CompositeColumn.create(paramHeader, Array.create 2 paramValue) + |] + t + + let sheetWithREFAndFactor() = + let t = ArcTable.init("SimpleWithFactor") + let factorHeader = CompositeHeader.Factor oa_instrumentModel + let factorValue = CompositeCell.createTerm oa_SCIEXInstrumentModel + t.AddColumns + [| + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 3 (CompositeCell.createFreeText protocolName1)) + CompositeColumn.create(factorHeader, Array.create 3 factorValue) + |] + t + + let sheetWithTwoProtocolsOneRef() = + let t = ArcTable.init("Simple") + let inputHeader = CompositeHeader.Input IOType.Sample + let paramHeader = CompositeHeader.Parameter oa_species + let paramValue = CompositeCell.createTerm oa_chlamy + t.AddColumns + [| + CompositeColumn.create(inputHeader, Array.init 4 (fun i -> CompositeCell.createFreeText $"{i}")) + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 2 (CompositeCell.createFreeText protocolName1)) + CompositeColumn.create(paramHeader, Array.create 4 paramValue) + |] + t + + let sheetWithTwoProtocolsTwoRefs() = + let t = ArcTable.init("Simple") + let inputHeader = CompositeHeader.Input IOType.Sample + let paramHeader = CompositeHeader.Parameter oa_species + let paramValue = CompositeCell.createTerm oa_chlamy + let protocolREFColumn = + Array.create 2 (CompositeCell.createFreeText protocolName2) + |> Array.append (Array.create 2 (CompositeCell.createFreeText protocolName1)) + t.AddColumns + [| + CompositeColumn.create(inputHeader, Array.init 4 (fun i -> CompositeCell.createFreeText $"{i}")) + CompositeColumn.create(CompositeHeader.ProtocolREF, protocolREFColumn) + CompositeColumn.create(paramHeader, Array.create 4 paramValue) + |] + t + + + let descriptionRefTable() = + let t = ArcTable.init("SimpleRef") + + t.AddColumns + [| + CompositeColumn.create(CompositeHeader.ProtocolDescription, Array.create 1 (CompositeCell.createFreeText descriptionValue1)) + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 1 (CompositeCell.createFreeText protocolName1)) + |] + t + + let descriptionRefTable2() = + let t = ArcTable.init("Whatever") + + t.AddColumns + [| + CompositeColumn.create(CompositeHeader.ProtocolDescription, Array.create 1 (CompositeCell.createFreeText descriptionValue2)) + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 1 (CompositeCell.createFreeText protocolName2)) + |] + t + + let versionRefTable() = + let t = ArcTable.init("SecondRef") + + t.AddColumns + [| + CompositeColumn.create(CompositeHeader.ProtocolVersion, Array.create 1 (CompositeCell.createFreeText versionValue1)) + CompositeColumn.create(CompositeHeader.ProtocolREF, Array.create 1 (CompositeCell.createFreeText protocolName2)) + |] + t + +open TestObjects + +let updateReferenceWithSheet = + + testList "UpdateReferenceWithSheet" [ + + testCase "NoReference" (fun () -> + let tableOfInterest = sheetWithNoREF() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount tableOfInterest.ColumnCount "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + Expect.isFalse (obj.ReferenceEquals(resultTable,tableOfInterest)) "Should not be same object" + ) + testCase "NoMatchingReference" (fun () -> + let tableOfInterest = sheetWithREF() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [descriptionRefTable2()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount tableOfInterest.ColumnCount "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + Expect.isFalse (obj.ReferenceEquals(resultTable,tableOfInterest)) "Should not be same object" + ) + testCase "SimpleWithProtocolREF" (fun () -> + let tableOfInterest = sheetWithREF() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [descriptionRefTable()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount (tableOfInterest.ColumnCount + 1) "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + mySequenceEqual + (resultTable.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (Array.create 2 (CompositeCell.createFreeText descriptionValue1)) + "Description value was not taken correctly" + ) + testCase "SimpleWithNoProtocolREF" (fun () -> + let tableOfInterest = sheetWithNoREF() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [descriptionRefTable()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount (tableOfInterest.ColumnCount + 2) "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + mySequenceEqual + (resultTable.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (Array.create 2 (CompositeCell.createFreeText descriptionValue1)) + "Description value was not taken correctly" + ) + testCase "TwoTablesWithSameProtocol" (fun () -> + let tableOfInterest1 = sheetWithREF() + let tableOfInterest2 = sheetWithREFAndFactor() + let tables = ArcTables.ofSeq [tableOfInterest1;tableOfInterest2] + let refTables = ArcTables.ofSeq [descriptionRefTable()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + + let resultTable1 = result.[0] + Expect.equal resultTable1.Name tableOfInterest1.Name "Should be same table name" + Expect.equal resultTable1.ColumnCount (tableOfInterest1.ColumnCount + 1) "Should be same number of columns" + Expect.equal tableOfInterest1.RowCount tableOfInterest1.RowCount "Should be same number of rows" + mySequenceEqual + (resultTable1.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (Array.create 2 (CompositeCell.createFreeText descriptionValue1)) + "Description value was not taken correctly" + mySequenceEqual + (resultTable1.GetColumnByHeader (CompositeHeader.Parameter oa_species)).Cells + (Array.create 2 (CompositeCell.createTerm oa_chlamy)) + "Check for previous param correctness" + + let resultTable2 = result.[1] + Expect.equal resultTable2.Name tableOfInterest2.Name "Should be same table name" + Expect.equal resultTable2.ColumnCount (tableOfInterest2.ColumnCount + 1) "Should be same number of columns" + Expect.equal tableOfInterest2.RowCount tableOfInterest2.RowCount "Should be same number of rows" + mySequenceEqual + (resultTable2.GetProtocolDescriptionColumn()).Cells + (Array.create 3 (CompositeCell.createFreeText descriptionValue1)) + "Description value was not taken correctly" + mySequenceEqual + (resultTable2.GetColumnByHeader (CompositeHeader.Factor oa_instrumentModel)).Cells + (Array.create 3 (CompositeCell.createTerm oa_SCIEXInstrumentModel)) + "Check for previous param correctness" + + ) + testCase "TableWithTwoProtocolsOneRef" (fun () -> + let tableOfInterest = sheetWithTwoProtocolsOneRef() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [descriptionRefTable();descriptionRefTable2()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount (tableOfInterest.ColumnCount + 1) "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + + let expectedDescription = + Array.create 2 (CompositeCell.emptyFreeText) + |> Array.append (Array.create 2 (CompositeCell.createFreeText descriptionValue1)) + mySequenceEqual + (resultTable.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (expectedDescription) + "Description value was not taken correctly" + mySequenceEqual + (resultTable.GetColumnByHeader (CompositeHeader.Parameter oa_species)).Cells + (Array.create 4 (CompositeCell.createTerm oa_chlamy)) + "Check for previous param correctness" + + ) + testCase "TableWithTwoProtocolsTwoRefs" (fun () -> + let tableOfInterest = sheetWithTwoProtocolsTwoRefs() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [descriptionRefTable();descriptionRefTable2()] + let result = ArcTables.updateReferenceTablesBySheets refTables tables + + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount (tableOfInterest.ColumnCount + 1) "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + + let expectedDescription = + Array.create 2 (CompositeCell.createFreeText descriptionValue2) + |> Array.append (Array.create 2 (CompositeCell.createFreeText descriptionValue1)) + mySequenceEqual + (resultTable.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (expectedDescription) + "Description value was not taken correctly" + mySequenceEqual + (resultTable.GetColumnByHeader (CompositeHeader.Parameter oa_species)).Cells + (Array.create 4 (CompositeCell.createTerm oa_chlamy)) + "Check for previous param correctness" + ) + ] + + +let main = + testList "ArcTableTests" [ + updateReferenceWithSheet + ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ISA.Tests.fsproj b/tests/ISA/ISA.Tests/ISA.Tests.fsproj index 1cb3d262..11e33496 100644 --- a/tests/ISA/ISA.Tests/ISA.Tests.fsproj +++ b/tests/ISA/ISA.Tests/ISA.Tests.fsproj @@ -14,6 +14,7 @@ + diff --git a/tests/ISA/ISA.Tests/Main.fs b/tests/ISA/ISA.Tests/Main.fs index b821bcec..9b7cefa9 100644 --- a/tests/ISA/ISA.Tests/Main.fs +++ b/tests/ISA/ISA.Tests/Main.fs @@ -15,6 +15,7 @@ let all = testSequenced <| testList "All" [ CompositeHeader.Tests.main CompositeCell.Tests.main CompositeColumn.Tests.main + ArcTables.Tests.main ArcTable.Tests.main ArcAssay.Tests.main ArcStudy.Tests.main From 317be82ca586a8e4ad851cfbec2f51569c027424 Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Sun, 17 Sep 2023 23:23:19 +0200 Subject: [PATCH 14/17] fix spreadsheet reader parsing empty ontology annotations --- src/ISA/ISA.Spreadsheet/Metadata/Assays.fs | 12 +++---- src/ISA/ISA.Spreadsheet/Metadata/Contacts.fs | 32 +++++++++---------- .../Metadata/DesignDescriptors.fs | 9 +++--- src/ISA/ISA.Spreadsheet/Metadata/Factors.fs | 8 ++--- .../Metadata/OntologySourceReference.fs | 16 +++++----- src/ISA/ISA.Spreadsheet/Metadata/Protocols.fs | 20 ++++++------ .../ISA.Spreadsheet/Metadata/Publication.fs | 20 ++++++------ .../ISA.Spreadsheet.Tests/AssayFileTests.fs | 9 ++++++ 8 files changed, 68 insertions(+), 58 deletions(-) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Assays.fs b/src/ISA/ISA.Spreadsheet/Metadata/Assays.fs index c853ce4f..5b8ebd56 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Assays.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Assays.fs @@ -24,13 +24,13 @@ module Assays = let fromString measurementType measurementTypeTermSourceREF measurementTypeTermAccessionNumber technologyType technologyTypeTermSourceREF technologyTypeTermAccessionNumber technologyPlatform fileName comments : ArcAssay = - let measurementType = OntologyAnnotation.fromString(measurementType,?tan = measurementTypeTermAccessionNumber,?tsr = measurementTypeTermSourceREF) - let technologyType = OntologyAnnotation.fromString(technologyType,?tan = technologyTypeTermAccessionNumber,?tsr = technologyTypeTermSourceREF) + let measurementType = OntologyAnnotation.fromString(?termName = measurementType,?tan = measurementTypeTermAccessionNumber,?tsr = measurementTypeTermSourceREF) + let technologyType = OntologyAnnotation.fromString(?termName = technologyType,?tan = technologyTypeTermAccessionNumber,?tsr = technologyTypeTermSourceREF) ArcAssay.make (fileName) (Option.fromValueWithDefault OntologyAnnotation.empty measurementType) (Option.fromValueWithDefault OntologyAnnotation.empty technologyType) - (Option.fromValueWithDefault "" technologyPlatform |> Option.map ArcAssay.decomposeTechnologyPlatform) + (technologyPlatform |> Option.map ArcAssay.decomposeTechnologyPlatform) (ResizeArray()) [||] (comments) @@ -50,13 +50,13 @@ module Assays = |> Array.ofList fromString - (matrix.TryGetValueDefault("",(measurementTypeLabel,i))) + (matrix.TryGetValue(measurementTypeLabel,i)) (matrix.TryGetValue((measurementTypeTermSourceREFLabel,i))) (matrix.TryGetValue((measurementTypeTermAccessionNumberLabel,i))) - (matrix.TryGetValueDefault("",(technologyTypeLabel,i))) + (matrix.TryGetValue(technologyTypeLabel,i)) (matrix.TryGetValue((technologyTypeTermSourceREFLabel,i))) (matrix.TryGetValue((technologyTypeTermAccessionNumberLabel,i))) - (matrix.TryGetValueDefault("",(technologyPlatformLabel,i))) + (matrix.TryGetValue(technologyPlatformLabel,i)) (matrix.TryGetValueDefault(Identifier.createMissingIdentifier(),(fileNameLabel,i)) |> Identifier.Assay.identifierFromFileName) comments ) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Contacts.fs b/src/ISA/ISA.Spreadsheet/Metadata/Contacts.fs index 4385afbb..1799dfbf 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Contacts.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Contacts.fs @@ -26,14 +26,14 @@ module Contacts = Person.make None None - (Option.fromValueWithDefault "" lastName ) - (Option.fromValueWithDefault "" firstName ) - (Option.fromValueWithDefault "" midInitials) - (Option.fromValueWithDefault "" email ) - (Option.fromValueWithDefault "" phone ) - (Option.fromValueWithDefault "" fax ) - (Option.fromValueWithDefault "" address ) - (Option.fromValueWithDefault "" affiliation) + (lastName ) + (firstName ) + (midInitials) + (email ) + (phone ) + (fax ) + (address ) + (affiliation) (Option.fromValueWithDefault [||] roles ) (Option.fromValueWithDefault [||] comments ) |> Person.setOrcidFromComments @@ -51,14 +51,14 @@ module Contacts = Comment.fromString k (matrix.TryGetValueDefault("",(k,i)))) |> Array.ofList fromString - (matrix.TryGetValueDefault("",(lastNameLabel,i))) - (matrix.TryGetValueDefault("",(firstNameLabel,i))) - (matrix.TryGetValueDefault("",(midInitialsLabel,i))) - (matrix.TryGetValueDefault("",(emailLabel,i))) - (matrix.TryGetValueDefault("",(phoneLabel,i))) - (matrix.TryGetValueDefault("",(faxLabel,i))) - (matrix.TryGetValueDefault("",(addressLabel,i))) - (matrix.TryGetValueDefault("",(affiliationLabel,i))) + (matrix.TryGetValue(lastNameLabel,i)) + (matrix.TryGetValue(firstNameLabel,i)) + (matrix.TryGetValue(midInitialsLabel,i)) + (matrix.TryGetValue(emailLabel,i)) + (matrix.TryGetValue(phoneLabel,i)) + (matrix.TryGetValue(faxLabel,i)) + (matrix.TryGetValue(addressLabel,i)) + (matrix.TryGetValue(affiliationLabel,i)) (matrix.TryGetValueDefault("",(rolesLabel,i))) (matrix.TryGetValueDefault("",(rolesTermAccessionNumberLabel,i))) (matrix.TryGetValueDefault("",(rolesTermSourceREFLabel,i))) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/DesignDescriptors.fs b/src/ISA/ISA.Spreadsheet/Metadata/DesignDescriptors.fs index 7a6a7b4d..c6e2744f 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/DesignDescriptors.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/DesignDescriptors.fs @@ -26,12 +26,13 @@ module DesignDescriptors = |> List.map (fun k -> Comment.fromString k (matrix.TryGetValueDefault("",(k,i)))) |> Array.ofList + |> Option.fromValueWithDefault [||] OntologyAnnotation.fromString( - (matrix.TryGetValueDefault("",(designTypeLabel,i))), - (matrix.TryGetValueDefault("",(designTypeTermSourceREFLabel,i))), - (matrix.TryGetValueDefault("",(designTypeTermAccessionNumberLabel,i))), - comments + ?termName = matrix.TryGetValue(designTypeLabel,i), + ?tsr = matrix.TryGetValue(designTypeTermSourceREFLabel,i), + ?tan = matrix.TryGetValue(designTypeTermAccessionNumberLabel,i), + ?comments = comments ) ) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Factors.fs b/src/ISA/ISA.Spreadsheet/Metadata/Factors.fs index 373b2843..5ea7d090 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Factors.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Factors.fs @@ -15,10 +15,10 @@ module Factors = let labels = [nameLabel;factorTypeLabel;typeTermAccessionNumberLabel;typeTermSourceREFLabel] let fromString name designType typeTermSourceREF typeTermAccessionNumber comments = - let factorType = OntologyAnnotation.fromString(designType,?tan = typeTermAccessionNumber, ?tsr = typeTermSourceREF) + let factorType = OntologyAnnotation.fromString(?termName = designType,?tan = typeTermAccessionNumber, ?tsr = typeTermSourceREF) Factor.make None - (Option.fromValueWithDefault "" name) + (name) (Option.fromValueWithDefault OntologyAnnotation.empty factorType) (Option.fromValueWithDefault [||] comments) @@ -37,8 +37,8 @@ module Factors = |> Array.ofList fromString - (matrix.TryGetValueDefault("",(nameLabel,i))) - (matrix.TryGetValueDefault("",(factorTypeLabel,i))) + (matrix.TryGetValue(nameLabel,i)) + (matrix.TryGetValue(factorTypeLabel,i)) (matrix.TryGetValue((typeTermSourceREFLabel,i))) (matrix.TryGetValue((typeTermAccessionNumberLabel,i))) comments diff --git a/src/ISA/ISA.Spreadsheet/Metadata/OntologySourceReference.fs b/src/ISA/ISA.Spreadsheet/Metadata/OntologySourceReference.fs index 651561e1..63df6929 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/OntologySourceReference.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/OntologySourceReference.fs @@ -17,10 +17,10 @@ module OntologySourceReference = let fromString description file name version comments = OntologySourceReference.make - (Option.fromValueWithDefault "" description) - (Option.fromValueWithDefault "" file) - (Option.fromValueWithDefault "" name) - (Option.fromValueWithDefault "" version) + (description) + (file) + (name) + (version) (Option.fromValueWithDefault [||] comments) let fromSparseTable (matrix : SparseTable) = @@ -38,10 +38,10 @@ module OntologySourceReference = |> Array.ofList fromString - (matrix.TryGetValueDefault("",(descriptionLabel,i))) - (matrix.TryGetValueDefault("",(fileLabel,i))) - (matrix.TryGetValueDefault("",(nameLabel,i))) - (matrix.TryGetValueDefault("",(versionLabel,i))) + (matrix.TryGetValue(descriptionLabel,i)) + (matrix.TryGetValue(fileLabel,i)) + (matrix.TryGetValue(nameLabel,i)) + (matrix.TryGetValue(versionLabel,i)) comments ) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Protocols.fs b/src/ISA/ISA.Spreadsheet/Metadata/Protocols.fs index aaf3cebb..4132198b 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Protocols.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Protocols.fs @@ -30,17 +30,17 @@ module Protocols = ] let fromString name protocolType typeTermAccessionNumber typeTermSourceREF description uri version parametersName parametersTermAccessionNumber parametersTermSourceREF componentsName componentsType componentsTypeTermAccessionNumber componentsTypeTermSourceREF comments = - let protocolType = OntologyAnnotation.fromString(protocolType,?tan = typeTermAccessionNumber,?tsr = typeTermSourceREF) + let protocolType = OntologyAnnotation.fromString(?termName = protocolType,?tan = typeTermAccessionNumber,?tsr = typeTermSourceREF) let parameters = ProtocolParameter.fromAggregatedStrings ';' parametersName parametersTermSourceREF parametersTermAccessionNumber |> List.ofArray let components = Component.fromAggregatedStrings ';' componentsName componentsType componentsTypeTermSourceREF componentsTypeTermAccessionNumber Protocol.make None - (Option.fromValueWithDefault "" name |> Option.map URI.fromString) + (name |> Option.map URI.fromString) (Option.fromValueWithDefault OntologyAnnotation.empty protocolType) - (Option.fromValueWithDefault "" description) - (Option.fromValueWithDefault "" uri |> Option.map URI.fromString) - (Option.fromValueWithDefault "" version) + (description) + (uri |> Option.map URI.fromString) + (version) (Option.fromValueWithDefault [] parameters) (Option.fromValueWithDefault [] components) (Option.fromValueWithDefault [] comments) @@ -60,13 +60,13 @@ module Protocols = Comment.fromString k (matrix.TryGetValueDefault("",(k,i)))) fromString - (matrix.TryGetValueDefault("",(nameLabel,i))) - (matrix.TryGetValueDefault("",(protocolTypeLabel,i))) + (matrix.TryGetValue(nameLabel,i)) + (matrix.TryGetValue(protocolTypeLabel,i)) (matrix.TryGetValue(typeTermAccessionNumberLabel,i)) (matrix.TryGetValue(typeTermSourceREFLabel,i)) - (matrix.TryGetValueDefault("",(descriptionLabel,i))) - (matrix.TryGetValueDefault("",(uriLabel,i))) - (matrix.TryGetValueDefault("",(versionLabel,i))) + (matrix.TryGetValue(descriptionLabel,i)) + (matrix.TryGetValue(uriLabel,i)) + (matrix.TryGetValue(versionLabel,i)) (matrix.TryGetValueDefault("",(parametersNameLabel,i))) (matrix.TryGetValueDefault("",(parametersTermAccessionNumberLabel,i))) (matrix.TryGetValueDefault("",(parametersTermSourceREFLabel,i))) diff --git a/src/ISA/ISA.Spreadsheet/Metadata/Publication.fs b/src/ISA/ISA.Spreadsheet/Metadata/Publication.fs index 183d73b0..fb1a1b64 100644 --- a/src/ISA/ISA.Spreadsheet/Metadata/Publication.fs +++ b/src/ISA/ISA.Spreadsheet/Metadata/Publication.fs @@ -18,12 +18,12 @@ module Publications = let labels = [pubMedIDLabel;doiLabel;authorListLabel;titleLabel;statusLabel;statusTermAccessionNumberLabel;statusTermSourceREFLabel] let fromString pubMedID doi author title status statusTermSourceREF statusTermAccessionNumber comments = - let status = OntologyAnnotation.fromString(status,?tan = statusTermAccessionNumber,?tsr = statusTermSourceREF) + let status = OntologyAnnotation.fromString(?termName = status,?tan = statusTermAccessionNumber,?tsr = statusTermSourceREF) Publication.make - (Option.fromValueWithDefault "" pubMedID |> Option.map URI.fromString) - (Option.fromValueWithDefault "" doi) - (Option.fromValueWithDefault "" author) - (Option.fromValueWithDefault "" title) + (pubMedID |> Option.map URI.fromString) + (doi) + (author) + (title) (Option.fromValueWithDefault OntologyAnnotation.empty status) (Option.fromValueWithDefault [||] comments) @@ -42,11 +42,11 @@ module Publications = |> Array.ofList fromString - (matrix.TryGetValueDefault("",(pubMedIDLabel,i))) - (matrix.TryGetValueDefault("",(doiLabel,i))) - (matrix.TryGetValueDefault("",(authorListLabel,i))) - (matrix.TryGetValueDefault("",(titleLabel,i))) - (matrix.TryGetValueDefault("",(statusLabel,i))) + (matrix.TryGetValue(pubMedIDLabel,i)) + (matrix.TryGetValue(doiLabel,i)) + (matrix.TryGetValue(authorListLabel,i)) + (matrix.TryGetValue(titleLabel,i)) + (matrix.TryGetValue(statusLabel,i)) (matrix.TryGetValue((statusTermSourceREFLabel,i))) (matrix.TryGetValue((statusTermAccessionNumberLabel,i))) comments diff --git a/tests/ISA/ISA.Spreadsheet.Tests/AssayFileTests.fs b/tests/ISA/ISA.Spreadsheet.Tests/AssayFileTests.fs index 44000340..de211a5d 100644 --- a/tests/ISA/ISA.Spreadsheet.Tests/AssayFileTests.fs +++ b/tests/ISA/ISA.Spreadsheet.Tests/AssayFileTests.fs @@ -123,6 +123,15 @@ let testMetaDataFunctions = Expect.isOk readingSuccess (Result.getMessage readingSuccess) ) + + testCase "ReaderCorrectnessEmpty" (fun () -> + + let assay = ArcAssay.fromMetadataSheet TestObjects.Assay.assayMetadataEmpty + Expect.isNone assay.MeasurementType "MeasurementType should not be set" + Expect.isNone assay.TechnologyPlatform "TechnologyPlatform should not be set" + Expect.isNone assay.TechnologyType "TechnologyType should not be set" + ) + testCase "ReaderSuccessEmptyObsoleteSheetName" (fun () -> let readingSuccess = From ef0ec6b5291273aa5617db7747b3bc9cf58be0ca Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Mon, 18 Sep 2023 00:07:06 +0200 Subject: [PATCH 15/17] test and fix arc read in with updating of objects against each other --- src/ARCtrl/ARCtrl.fs | 16 +++- src/ISA/ISA.Spreadsheet/ArcStudy.fs | 14 ++- src/ISA/ISA/ArcTypes/ArcTables.fs | 23 +++-- src/ISA/ISA/ArcTypes/ArcTypes.fs | 14 ++- tests/ARCtrl/ARCtrl.Tests.fs | 51 +++++++++- tests/ARCtrl/TestObjects/ISAContracts.fs | 55 ++++++++++- tests/ISA/ISA.Tests/ArcStudy.Tests.fs | 117 +++++++++-------------- tests/ISA/ISA.Tests/ArcTables.Tests.fs | 35 +++++-- 8 files changed, 219 insertions(+), 106 deletions(-) diff --git a/src/ARCtrl/ARCtrl.fs b/src/ARCtrl/ARCtrl.fs index 2e831ea1..ca1f7522 100644 --- a/src/ARCtrl/ARCtrl.fs +++ b/src/ARCtrl/ARCtrl.fs @@ -165,16 +165,16 @@ type ARC(?isa : ISA.ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSyste /// get assays from xlsx let assays = ARCAux.getArcAssaysFromContracts contracts - studies |> Seq.iter (fun study -> + studies |> Array.iter (fun study -> /// Try find registered study in parsed READ contracts let registeredStudyOpt = investigation.Studies |> Seq.tryFind (fun s -> s.Identifier = study.Identifier) match registeredStudyOpt with - | Some registeredStudy -> // This study element is parsed from FsWorkbook and has no regsitered assays, yet + | Some registeredStudy -> registeredStudy.UpdateReferenceByStudyFile(study,true) | None -> - investigation.AddRegisteredStudy(study) + investigation.AddStudy(study) ) - assays |> Seq.iter (fun assay -> + assays |> Array.iter (fun assay -> /// Try find registered study in parsed READ contracts let registeredAssayOpt = investigation.Assays |> Seq.tryFind (fun a -> a.Identifier = assay.Identifier) match registeredAssayOpt with @@ -182,8 +182,14 @@ type ARC(?isa : ISA.ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSyste registeredAssay.UpdateReferenceByAssayFile(assay,true) | None -> investigation.AddAssay(assay) + let assay = investigation.Assays |> Seq.find (fun a -> a.Identifier = assay.Identifier) + let updatedTables = + assay.StudiesRegisteredIn + |> Array.fold (fun tables study -> + ArcTables.updateReferenceTablesBySheets(ArcTables(study.Tables),tables,false) + ) (ArcTables(assay.Tables)) + assay.Tables <- updatedTables.Tables ) - this.ISA <- Some investigation member this.UpdateFileSystem() = diff --git a/src/ISA/ISA.Spreadsheet/ArcStudy.fs b/src/ISA/ISA.Spreadsheet/ArcStudy.fs index a775841c..bbf1921a 100644 --- a/src/ISA/ISA.Spreadsheet/ArcStudy.fs +++ b/src/ISA/ISA.Spreadsheet/ArcStudy.fs @@ -59,11 +59,15 @@ module Extensions = let sheets = doc.GetWorksheets() |> Seq.choose ArcTable.tryFromFsWorksheet - if sheets |> Seq.isEmpty then - studyMetadata - else - studyMetadata.Tables <- ResizeArray(sheets) - studyMetadata + if sheets |> Seq.isEmpty |> not then + let updatedTables = + ArcTables.updateReferenceTablesBySheets( + (ArcTables studyMetadata.Tables), + (ArcTables (ResizeArray sheets)), + keepUnusedRefTables = true + ) + studyMetadata.Tables <- updatedTables.Tables + studyMetadata ,assays static member toFsWorkbook (study : ArcStudy,?assays : ArcAssay list) = diff --git a/src/ISA/ISA/ArcTypes/ArcTables.fs b/src/ISA/ISA/ArcTypes/ArcTables.fs index ed4ea023..cba453d9 100644 --- a/src/ISA/ISA/ArcTypes/ArcTables.fs +++ b/src/ISA/ISA/ArcTypes/ArcTables.fs @@ -310,12 +310,15 @@ type ArcTables(thisTables:ResizeArray) = |> ResizeArray |> ArcTables - static member updateReferenceTablesBySheets (referenceTables : ArcTables) (sheetTables : ArcTables) : ArcTables = + static member updateReferenceTablesBySheets (referenceTables : ArcTables,sheetTables : ArcTables,?keepUnusedRefTables : bool) : ArcTables = + let keepUnusedRefTables = Option.defaultValue false keepUnusedRefTables + let usedTables = HashSet() let referenceTableMap = referenceTables.Tables |> Seq.map (fun t -> t.GetProtocolNameColumn().Cells.[0].AsFreeText, t) |> Map.ofSeq sheetTables.Tables - |> Seq.collect ArcTable.SplitByProtocolREF - |> Seq.map (fun t -> + |> Seq.toArray + |> Array.collect ArcTable.SplitByProtocolREF + |> Array.map (fun t -> let k = t.Headers |> Seq.tryFindIndex (fun x -> x = CompositeHeader.ProtocolREF) |> Option.bind (fun i -> @@ -327,18 +330,26 @@ type ArcTables(thisTables:ResizeArray) = |> Option.defaultValue t.Name match Map.tryFind k referenceTableMap with | Some rt -> + usedTables.Add(k) |> ignore let rt = rt.Copy() rt.UpdateReferenceByAnnotationTable t ArcTable.create(t.Name, rt.Headers, rt.Values) | None -> t ) - |> Seq.groupBy (fun t -> t.Name) - |> Seq.map (fun (_,ts) -> + |> Array.groupBy (fun t -> t.Name) + |> Array.map (fun (_,ts) -> ts |> Seq.reduce ArcTable.append ) - |> Seq.map (fun t -> + |> Array.map (fun t -> ArcTableAux.Unchecked.fillMissingCells t.Headers t.Values t) + |> fun s -> + if keepUnusedRefTables then + Seq.append + (referenceTableMap |> Seq.choose (fun (kv) -> if usedTables.Contains kv.Key then None else Some kv.Value)) + s + else + s |> ResizeArray |> ArcTables \ No newline at end of file diff --git a/src/ISA/ISA/ArcTypes/ArcTypes.fs b/src/ISA/ISA/ArcTypes/ArcTypes.fs index 75374e40..d1f2aa07 100644 --- a/src/ISA/ISA/ArcTypes/ArcTypes.fs +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -125,6 +125,16 @@ type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?techno member this.TableNames with get() = ArcTables(this.Tables).TableNames + member this.StudiesRegisteredIn + with get () = + match this.Investigation with + | Some i -> + i.Studies + |> Seq.filter (fun s -> s.RegisteredAssayIdentifiers |> Seq.contains this.Identifier) + |> Seq.toArray + | None -> [||] + + // - Table API - // // remark should this return ArcTable? member this.AddTable(table:ArcTable, ?index: int) : unit = ArcTables(this.Tables).AddTable(table, ?index = index) @@ -1108,7 +1118,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi /// /// The study used for updating this study. /// If true, this will only update fields which are `Some` or non-empty lists. Default: **false** - member this.UpdateReferenceByStudyFile(study:ArcStudy,?onlyReplaceExisting : bool) = + member this.UpdateReferenceByStudyFile(study:ArcStudy,?onlyReplaceExisting : bool,?keepUnusedRefTables) = let onlyReplaceExisting = defaultArg onlyReplaceExisting false let updateAlways = onlyReplaceExisting |> not if study.Title.IsSome || updateAlways then @@ -1126,7 +1136,7 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi if study.StudyDesignDescriptors.Length <> 0 || updateAlways then this.StudyDesignDescriptors <- study.StudyDesignDescriptors if study.Tables.Count <> 0 || updateAlways then - let tables = ArcTables.updateReferenceTablesBySheets (ArcTables(this.Tables)) (ArcTables(study.Tables)) + let tables = ArcTables.updateReferenceTablesBySheets(ArcTables(this.Tables),ArcTables(study.Tables),?keepUnusedRefTables = keepUnusedRefTables) this.Tables <- tables.Tables if study.RegisteredAssayIdentifiers.Count <> 0 || updateAlways then this.RegisteredAssayIdentifiers <- study.RegisteredAssayIdentifiers diff --git a/tests/ARCtrl/ARCtrl.Tests.fs b/tests/ARCtrl/ARCtrl.Tests.fs index 7d1c53d0..c8ed85a8 100644 --- a/tests/ARCtrl/ARCtrl.Tests.fs +++ b/tests/ARCtrl/ARCtrl.Tests.fs @@ -8,7 +8,7 @@ open Expecto open ARCtrl open ARCtrl.ISA - +open TestObjects.ISAContracts let private test_model = testList "model" [ testCase "create" <| fun _ -> let arc = ARC() @@ -74,11 +74,52 @@ let private test_isaFromContracts = testList "read_contracts" [ Expect.equal assay1.Identifier TestObjects.Assay.assayIdentifier "assay 1 identifier should have been read from assay contract" Expect.equal assay1.TableCount 1 "assay 1 should have read one table" Expect.equal assay2.TableCount 0 "assay 2 should have read no tables" - Expect.equal assay3.TableCount 0 "assay 3 should have read no tables" - - + Expect.equal assay3.TableCount 0 "assay 3 should have read no tables" + ) + // Assay Table protocol get's updated by protocol metadata stored in study + testCase "assayTableGetsUpdated" (fun () -> + let iContract = UpdateAssayWithStudyProtocol.investigationReadContract + let sContract = UpdateAssayWithStudyProtocol.studyReadContract + let aContract = UpdateAssayWithStudyProtocol.assayReadContract + let arc = ARC() + arc.SetISAFromContracts([|iContract; sContract; aContract|]) + Expect.isSome arc.ISA "isa should be filled out" + let inv = arc.ISA.Value + Expect.equal inv.Identifier UpdateAssayWithStudyProtocol.investigationIdentifier "investigation identifier should have been read from investigation contract" + + Expect.equal inv.Studies.Count 1 "should have read one study" + let study = inv.Studies.[0] + + Expect.equal study.TableCount 1 "study should have read one table" + let studyTable = study.Tables.[0] + Expect.equal studyTable.ColumnCount 2 "study column number should be unchanged" + TestingUtils.mySequenceEqual + (studyTable.GetProtocolDescriptionColumn()).Cells + [CompositeCell.createFreeText UpdateAssayWithStudyProtocol.description] + "Description value was not kept correctly" + TestingUtils.mySequenceEqual + (studyTable.GetProtocolNameColumn()).Cells + [CompositeCell.createFreeText UpdateAssayWithStudyProtocol.protocolName] + "Protocol ref value was not kept correctly" + + Expect.equal study.RegisteredAssayCount 1 "study should have read one assay" + let assay = study.RegisteredAssays.[0] + Expect.equal assay.TableCount 1 "assay should have read one table" + let assayTable = assay.Tables.[0] + Expect.equal assayTable.ColumnCount 3 "assay column number should be updated" + TestingUtils.mySequenceEqual + (assayTable.GetProtocolNameColumn()).Cells + (Array.create 2 (CompositeCell.createFreeText UpdateAssayWithStudyProtocol.protocolName)) + "Protocol ref value was not kept correctly" + TestingUtils.mySequenceEqual + (assayTable.GetColumnByHeader(UpdateAssayWithStudyProtocol.inputHeader)).Cells + (Array.create 2 UpdateAssayWithStudyProtocol.inputCell) + "Protocol ref value was not kept correctly" + TestingUtils.mySequenceEqual + (assayTable.GetProtocolDescriptionColumn()).Cells + (Array.create 2 (CompositeCell.createFreeText UpdateAssayWithStudyProtocol.description)) + "Description value was not taken correctly" ) - ] let private test_writeContracts = testList "write_contracts" [ diff --git a/tests/ARCtrl/TestObjects/ISAContracts.fs b/tests/ARCtrl/TestObjects/ISAContracts.fs index d3f8d5c1..21f7c487 100644 --- a/tests/ARCtrl/TestObjects/ISAContracts.fs +++ b/tests/ARCtrl/TestObjects/ISAContracts.fs @@ -4,6 +4,7 @@ open FsSpreadsheet open ARCtrl.Contract open ARCtrl open ARCtrl.ISA +open ARCtrl.ISA.Spreadsheet module SimpleISA = let assayMetadataWorksheet = TestObjects.Assay.assayMetadata @@ -73,4 +74,56 @@ module SimpleISA = Operation.READ, path = Path.InvestigationFileName, dtoType = DTOType.ISA_Investigation, - dto = DTO.Spreadsheet Investigation.fullInvestigation) \ No newline at end of file + dto = DTO.Spreadsheet Investigation.fullInvestigation) + + +module UpdateAssayWithStudyProtocol = + + let assayIdentifier = "MyAssay" + let studyIdentifier = "MyStudy" + let investigationIdentifier = "MyInvestigation" + + let protocolName = "MyProtocol" + let inputHeader = CompositeHeader.Input IOType.Sample + let inputCell = (CompositeCell.createFreeText "inputValueName") + let description = "MyDescription" + + let assay = ArcAssay(assayIdentifier) + let study = ArcStudy(studyIdentifier) + let investigation = ArcInvestigation(investigationIdentifier) + investigation.AddRegisteredStudy(study) + study.AddRegisteredAssay assay + + let t = ArcTable.init("AssayProtocol") + t.AddProtocolNameColumn(Array.create 2 protocolName) + t.AddColumn(inputHeader, Array.create 2 inputCell) + assay.AddTable t + + let refT = ArcTable.init("StudyProtocol") + refT.AddProtocolNameColumn(Array.create 1 protocolName) + refT.AddProtocolDescriptionColumn(Array.create 1 description) + study.AddTable refT + + let assayWB = ArcAssay.toFsWorkbook assay + let assayReadContract = + Contract.create( + Operation.READ, + path = Identifier.Assay.fileNameFromIdentifier assayIdentifier, + dtoType = DTOType.ISA_Assay, + dto = DTO.Spreadsheet assayWB) + + let studyWB = ArcStudy.toFsWorkbook study + let studyReadContract = + Contract.create( + Operation.READ, + path = Identifier.Study.fileNameFromIdentifier studyIdentifier, + dtoType = DTOType.ISA_Study, + dto = DTO.Spreadsheet studyWB) + + let investigationWB = ArcInvestigation.toFsWorkbook investigation + let investigationReadContract = + Contract.create( + Operation.READ, + path = Path.InvestigationFileName, + dtoType = DTOType.ISA_Investigation, + dto = DTO.Spreadsheet investigationWB) \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs index 49db1e37..5cf2ec20 100644 --- a/tests/ISA/ISA.Tests/ArcStudy.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcStudy.Tests.fs @@ -351,37 +351,43 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ factors = factors, comments = comments ) - testCase "full replace, no tables" <| fun _ -> - let actual = createFullStudy() + testCase "tablesAreUpdated" <| fun _ -> + let tableOfInterest = ArcTables.Tests.TestObjects.sheetWithTwoProtocolsTwoRefs() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [ArcTables.Tests.TestObjects.descriptionRefTable();ArcTables.Tests.TestObjects.descriptionRefTable2()] + let actual = + ArcStudy( + "ReferenceStudy", + tables = refTables.Tables + ) let next = ArcStudy( identifier = "Next_identifier", - title = "Next_Title", - description = "Description", - submissionDate = "Next_SubmissionDate", - publicReleaseDate = "Next_PublicReleaseDate", - publications = [||], - contacts = [||], - studyDesignDescriptors = [||], - tables = ResizeArray(), - registeredAssayIdentifiers = ResizeArray(), - factors = [||], - comments = [||] + tables = tables.Tables ) actual.UpdateReferenceByStudyFile(next) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.Publications next.Publications "Publications" - Expect.equal actual.Contacts next.Contacts "Contacts" - Expect.equal actual.StudyDesignDescriptors next.StudyDesignDescriptors "StudyDesignDescriptors" - Expect.equal actual.Tables.Count 1 "Tables.Count = 0" // Ok - Expect.equal actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" - Expect.equal actual.Factors next.Factors "Factors" - Expect.equal actual.Comments next.Comments "Comments" + + let result = actual.Tables + Expect.equal result.Count tables.Count "Should be same number of tables" + let resultTable = result.[0] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount (tableOfInterest.ColumnCount + 1) "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + + let expectedDescription = + Array.create 2 (CompositeCell.createFreeText ArcTables.Tests.TestObjects.descriptionValue2) + |> Array.append (Array.create 2 (CompositeCell.createFreeText ArcTables.Tests.TestObjects.descriptionValue1)) + TestingUtils.mySequenceEqual + (resultTable.GetColumnByHeader CompositeHeader.ProtocolDescription).Cells + (expectedDescription) + "Description value was not taken correctly" + TestingUtils.mySequenceEqual + (resultTable.GetColumnByHeader (CompositeHeader.Parameter ArcTables.Tests.TestObjects.oa_species)).Cells + (Array.create 4 (CompositeCell.createTerm ArcTables.Tests.TestObjects.oa_chlamy)) + "Check for previous param correctness" + testCase "replace existing, none replaced" <| fun _ -> let actual = createFullStudy() let next = ArcStudy.init("NextIdentifier") @@ -431,64 +437,27 @@ let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers next.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" Expect.equal actual.Factors next.Factors "Factors" Expect.equal actual.Comments next.Comments "Comments" - testCase "replace existing, append" <| fun _ -> - let actual = createFullStudy() - let next = - ArcStudy( - identifier = "Next_identifier", - title = "Next_Title", - description = "Description", - submissionDate = "Next_SubmissionDate", - publicReleaseDate = "Next_PublicReleaseDate", - publications = [|Publication.create(Title="My Next Title")|], - contacts = [|Person.create(FirstName="NextKevin", LastName="NextFrey")|], - studyDesignDescriptors = [|OntologyAnnotation.fromString "Next OA"|], - tables = ResizeArray([ArcTable.init("NextTable")]) - ) - let original = createFullStudy() - actual.UpdateReferenceByStudyFile(next, true) - Expect.notEqual actual next "not equal" - Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.Publications [|Publication.create("Publication 1"); Publication.create(Title="My Next Title")|] "Publications" - Expect.equal actual.Contacts [|Person.create(FirstName = "John", LastName = "Doe"); Person.create(FirstName="NextKevin", LastName="NextFrey")|] "Contacts" - Expect.equal actual.StudyDesignDescriptors [|OntologyAnnotation.fromString("Design Descriptor"); OntologyAnnotation.fromString "Next OA"|] "StudyDesignDescriptors" - TestingUtils.mySequenceEqual actual.Tables (ResizeArray([ArcTable.init("Table 1"); ArcTable.init("NextTable")])) "Tables" - TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers original.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" - Expect.equal actual.Factors original.Factors "Factors" - Expect.equal actual.Comments original.Comments "Comments" - testCase "full replace, append" <| fun _ -> + testCase "full replace, empty" <| fun _ -> let actual = createFullStudy() let next = ArcStudy( - identifier = "Next_identifier", - title = "Next_Title", - description = "Description", - submissionDate = "Next_SubmissionDate", - publicReleaseDate = "Next_PublicReleaseDate", - publications = [|Publication.create(Title="My Next Title")|], - contacts = [|Person.create(FirstName="NextKevin", LastName="NextFrey")|], - studyDesignDescriptors = [|OntologyAnnotation.fromString "Next OA"|], - tables = ResizeArray([ArcTable.init("NextTable")]) + identifier = "Next_identifier" ) let original = createFullStudy() actual.UpdateReferenceByStudyFile(next, false) Expect.notEqual actual next "not equal" Expect.notEqual actual.Identifier next.Identifier "Identifier" - Expect.equal actual.Title next.Title "Title" - Expect.equal actual.Description next.Description "Description" - Expect.equal actual.SubmissionDate next.SubmissionDate "SubmissionDate" - Expect.equal actual.PublicReleaseDate next.PublicReleaseDate "PublicReleaseDate" - Expect.equal actual.Publications [|Publication.create("Publication 1"); Publication.create(Title="My Next Title")|] "Publications" - Expect.equal actual.Contacts [|Person.create(FirstName = "John", LastName = "Doe"); Person.create(FirstName="NextKevin", LastName="NextFrey")|] "Contacts" - Expect.equal actual.StudyDesignDescriptors [|OntologyAnnotation.fromString("Design Descriptor"); OntologyAnnotation.fromString "Next OA"|] "StudyDesignDescriptors" - TestingUtils.mySequenceEqual actual.Tables (ResizeArray([ArcTable.init("Table 1"); ArcTable.init("NextTable")])) "Tables" - TestingUtils.mySequenceEqual actual.RegisteredAssayIdentifiers original.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" - Expect.equal actual.Factors original.Factors "Factors" - Expect.equal actual.Comments original.Comments "Comments" + Expect.isNone actual.Title "Title" + Expect.isNone actual.Description "Description" + Expect.isNone actual.SubmissionDate "SubmissionDate" + Expect.isNone actual.PublicReleaseDate "PublicReleaseDate" + Expect.isEmpty actual.Publications "Publications" + Expect.isEmpty actual.Contacts "Contacts" + Expect.isEmpty actual.StudyDesignDescriptors "StudyDesignDescriptors" + Expect.isEmpty actual.Tables "Tables" + Expect.isEmpty actual.RegisteredAssayIdentifiers "RegisteredAssayIdentifiers" + Expect.isEmpty actual.Factors "Factors" + Expect.isEmpty actual.Comments "Comments" ] let main = diff --git a/tests/ISA/ISA.Tests/ArcTables.Tests.fs b/tests/ISA/ISA.Tests/ArcTables.Tests.fs index c2e0a863..9fb1eaaf 100644 --- a/tests/ISA/ISA.Tests/ArcTables.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcTables.Tests.fs @@ -130,7 +130,7 @@ let updateReferenceWithSheet = let tableOfInterest = sheetWithNoREF() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -143,7 +143,7 @@ let updateReferenceWithSheet = let tableOfInterest = sheetWithREF() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [descriptionRefTable2()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -152,11 +152,30 @@ let updateReferenceWithSheet = Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" Expect.isFalse (obj.ReferenceEquals(resultTable,tableOfInterest)) "Should not be same object" ) + testCase "NoMatchingReference_keepReference" (fun () -> + let tableOfInterest = sheetWithREF() + let refTable = descriptionRefTable2() + let tables = ArcTables.ofSeq [tableOfInterest] + let refTables = ArcTables.ofSeq [refTable] + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables,true) + + Expect.equal result.Count (tables.Count + 1) "Should be same number of tables" + let resultRefTable = result.[0] + Expect.equal resultRefTable.Name refTable.Name "Should be same table name" + Expect.equal resultRefTable.ColumnCount refTable.ColumnCount "Should be same number of columns" + Expect.equal resultRefTable.RowCount refTable.RowCount "Should be same number of rows" + + let resultTable = result.[1] + Expect.equal resultTable.Name tableOfInterest.Name "Should be same table name" + Expect.equal resultTable.ColumnCount tableOfInterest.ColumnCount "Should be same number of columns" + Expect.equal resultTable.RowCount tableOfInterest.RowCount "Should be same number of rows" + Expect.isFalse (obj.ReferenceEquals(resultTable,tableOfInterest)) "Should not be same object" + ) testCase "SimpleWithProtocolREF" (fun () -> let tableOfInterest = sheetWithREF() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [descriptionRefTable()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -172,7 +191,7 @@ let updateReferenceWithSheet = let tableOfInterest = sheetWithNoREF() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [descriptionRefTable()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -189,7 +208,7 @@ let updateReferenceWithSheet = let tableOfInterest2 = sheetWithREFAndFactor() let tables = ArcTables.ofSeq [tableOfInterest1;tableOfInterest2] let refTables = ArcTables.ofSeq [descriptionRefTable()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" @@ -224,7 +243,7 @@ let updateReferenceWithSheet = let tableOfInterest = sheetWithTwoProtocolsOneRef() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [descriptionRefTable();descriptionRefTable2()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -249,7 +268,7 @@ let updateReferenceWithSheet = let tableOfInterest = sheetWithTwoProtocolsTwoRefs() let tables = ArcTables.ofSeq [tableOfInterest] let refTables = ArcTables.ofSeq [descriptionRefTable();descriptionRefTable2()] - let result = ArcTables.updateReferenceTablesBySheets refTables tables + let result = ArcTables.updateReferenceTablesBySheets(refTables,tables) Expect.equal result.Count tables.Count "Should be same number of tables" let resultTable = result.[0] @@ -273,6 +292,6 @@ let updateReferenceWithSheet = let main = - testList "ArcTableTests" [ + testList "ArcTablesTests" [ updateReferenceWithSheet ] \ No newline at end of file From 03f1dd223c224007425b58de32e2d294c93dff7f Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Mon, 18 Sep 2023 11:30:34 +0200 Subject: [PATCH 16/17] some comments and small adjustments --- src/ISA/ISA/ArcTypes/ArcTable.fs | 48 +++++++++++++++++---------- src/ISA/ISA/ArcTypes/ArcTables.fs | 5 ++- src/ISA/ISA/Update.fs | 2 +- tests/ISA/ISA.Tests/ArcTable.Tests.fs | 6 ++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/ISA/ISA/ArcTypes/ArcTable.fs b/src/ISA/ISA/ArcTypes/ArcTable.fs index 01906955..6f21091c 100644 --- a/src/ISA/ISA/ArcTypes/ArcTable.fs +++ b/src/ISA/ISA/ArcTypes/ArcTable.fs @@ -32,11 +32,11 @@ type ArcTable = Values = System.Collections.Generic.Dictionary() } - static member initWithHeaders(name,headers : ResizeArray) = + static member createFromHeaders(name,headers : ResizeArray) = ArcTable.create(name,headers,Dictionary()) static member createFromRows(name,headers : ResizeArray,rows : CompositeCell[][]) : ArcTable = - let t = ArcTable.initWithHeaders(name,headers) + let t = ArcTable.createFromHeaders(name,headers) t.AddRows(rows) t @@ -147,6 +147,7 @@ type ArcTable = newTable // - Column API - // + /// Replaces the header and cells of a column at given index. member this.UpdateColumn (columnIndex:int, header: CompositeHeader, ?cells: CompositeCell []) = SanityChecks.validateColumnIndex columnIndex this.ColumnCount false let column = CompositeColumn.create(header, ?cells=cells) @@ -164,20 +165,21 @@ type ArcTable = column.Cells |> Array.iteri (fun rowIndex v -> Unchecked.setCellAt(columnIndex,rowIndex,v) this.Values) Unchecked.fillMissingCells this.Headers this.Values - static member updatedColumn (columnIndex:int, header: CompositeHeader, ?cells: CompositeCell []) = + /// Replaces the header and cells of a column at given index. + static member updateColumn (columnIndex:int, header: CompositeHeader, ?cells: CompositeCell []) = fun (table:ArcTable) -> let newTable = table.Copy() newTable.UpdateColumn(columnIndex, header, ?cells=cells) newTable // - Column API - // - member this.InsertColumn (header:CompositeHeader, index: int, ?cells: CompositeCell []) = + member this.InsertColumn (index: int, header:CompositeHeader, ?cells: CompositeCell []) = this.AddColumn(header, index = index,?cells = cells, forceReplace = false) - static member insertColumn (header:CompositeHeader, index: int, ?cells: CompositeCell []) = + static member insertColumn (index: int, header:CompositeHeader, ?cells: CompositeCell []) = fun (table: ArcTable) -> let newTable = table.Copy() - newTable.InsertColumn(header, index, ?cells = cells) + newTable.InsertColumn(index, header, ?cells = cells) newTable // - Column API - // @@ -261,6 +263,10 @@ type ArcTable = let index = this.Headers |> Seq.findIndex (fun x -> x = header) this.GetColumn(index) + static member getColumnByHeader (header:CompositeHeader) = + fun (table:ArcTable) -> + table.GetColumnByHeader(header) + // - Row API - // member this.AddRow (?cells: CompositeCell [], ?index: int) : unit = let index = defaultArg index this.RowCount @@ -543,18 +549,7 @@ type ArcTable = |> fun rows -> ProcessParsing.alignByHeaders true rows |> fun (headers, rows) -> ArcTable.create(name,headers,rows) - /// This method is meant to update an ArcTable stored as a protocol in a study or investigation file with the information from an ArcTable actually stored as an annotation table - member this.UpdateReferenceByAnnotationTable(table:ArcTable) = - let nonProtocolColumns = - this.Headers - |> Seq.indexed - |> Seq.choose (fun (i,h) -> if h.isProtocolColumn then None else Some i) - |> Seq.toArray - this.RemoveColumns nonProtocolColumns - ArcTableAux.Unchecked.extendToRowCount table.RowCount this.Headers this.Values - for c in table.Columns do - this.AddColumn(c.Header, cells = c.Cells,forceReplace = true) - + /// Splits the table rowWise into a collection of tables, so that each new table has only one value for the given column static member SplitByColumnValues(columnIndex) = fun (table : ArcTable) -> let column = table.GetColumn(columnIndex) @@ -568,6 +563,7 @@ type ArcTable = ArcTable.createFromRows(table.Name,headers,rows) ) + /// Splits the table rowWise into a collection of tables, so that each new table has only one value for the given column static member SplitByColumnValuesByHeader(header : CompositeHeader) = fun (table : ArcTable) -> let index = table.Headers |> Seq.tryFindIndex (fun x -> x = header) @@ -575,11 +571,27 @@ type ArcTable = | Some i -> ArcTable.SplitByColumnValues i table | None -> [|table.Copy()|] + /// Splits the table rowWise into a collection of tables, so that each new table has only one value for the ProtocolREF column static member SplitByProtocolREF = fun (table : ArcTable) -> ArcTable.SplitByColumnValuesByHeader CompositeHeader.ProtocolREF table + /// This method is meant to update an ArcTable stored as a protocol in a study or investigation file with the information from an ArcTable actually stored as an annotation table + static member updateReferenceByAnnotationTable (refTable:ArcTable) (annotationTable:ArcTable) = + let refTable = refTable.Copy() + let annotationTable = annotationTable.Copy() + let nonProtocolColumns = + refTable.Headers + |> Seq.indexed + |> Seq.choose (fun (i,h) -> if h.isProtocolColumn then None else Some i) + |> Seq.toArray + refTable.RemoveColumns nonProtocolColumns + ArcTableAux.Unchecked.extendToRowCount annotationTable.RowCount refTable.Headers refTable.Values + for c in annotationTable.Columns do + refTable.AddColumn(c.Header, cells = c.Cells,forceReplace = true) + refTable + /// Append the rows of another table to this one /// /// The headers of the other table will be aligned with the headers of this table diff --git a/src/ISA/ISA/ArcTypes/ArcTables.fs b/src/ISA/ISA/ArcTypes/ArcTables.fs index cba453d9..c071fbbd 100644 --- a/src/ISA/ISA/ArcTypes/ArcTables.fs +++ b/src/ISA/ISA/ArcTypes/ArcTables.fs @@ -331,9 +331,8 @@ type ArcTables(thisTables:ResizeArray) = match Map.tryFind k referenceTableMap with | Some rt -> usedTables.Add(k) |> ignore - let rt = rt.Copy() - rt.UpdateReferenceByAnnotationTable t - ArcTable.create(t.Name, rt.Headers, rt.Values) + let updatedTable = ArcTable.updateReferenceByAnnotationTable rt t + ArcTable.create(t.Name, updatedTable.Headers, updatedTable.Values) | None -> t ) |> Array.groupBy (fun t -> t.Name) diff --git a/src/ISA/ISA/Update.fs b/src/ISA/ISA/Update.fs index b1147191..593e87f0 100644 --- a/src/ISA/ISA/Update.fs +++ b/src/ISA/ISA/Update.fs @@ -1,6 +1,6 @@ namespace ARCtrl.ISA.Aux -module List = +module internal List = let tryPickAndRemove (f : 'T -> 'U option) (lst : 'T list) = let rec loop newList remainingList = diff --git a/tests/ISA/ISA.Tests/ArcTable.Tests.fs b/tests/ISA/ISA.Tests/ArcTable.Tests.fs index f0e1ce62..272febe8 100644 --- a/tests/ISA/ISA.Tests/ArcTable.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcTable.Tests.fs @@ -1964,7 +1964,7 @@ let private tests_UpdateRefWithSheet = valueTable.AddColumns(columns) let expectedRowCount = valueTable.RowCount let expectedColumnCount = 5 - refTable.UpdateReferenceByAnnotationTable valueTable + let refTable = ArcTable.updateReferenceByAnnotationTable refTable valueTable Expect.equal valueTable.ColumnCount 3 "ColumnCount of value table should not change after update" @@ -1997,7 +1997,7 @@ let private tests_UpdateRefWithSheet = valueTable.AddProtocolDescriptionColumn (Array.create 5 newProtocolDescription) let expectedRowCount = valueTable.RowCount let expectedColumnCount = 5 - refTable.UpdateReferenceByAnnotationTable valueTable + let refTable = ArcTable.updateReferenceByAnnotationTable refTable valueTable Expect.equal valueTable.ColumnCount 4 "ColumnCount of value table should not change after update" @@ -2029,7 +2029,7 @@ let private tests_UpdateRefWithSheet = valueTable.AddColumns(columns) let expectedRowCount = valueTable.RowCount let expectedColumnCount = 5 - refTable.UpdateReferenceByAnnotationTable valueTable + let refTable = ArcTable.updateReferenceByAnnotationTable refTable valueTable Expect.equal refTable.RowCount expectedRowCount "RowCount of reference table should be the same as value table after update" Expect.equal refTable.ColumnCount expectedColumnCount "ColumnCount of reference table should be the sum of value table and protocol table after update minus the param columns" From f812f4cdc915c32db0c7260d99feee0f8da922c2 Mon Sep 17 00:00:00 2001 From: Heinrich Lukas Weil Date: Mon, 18 Sep 2023 12:45:01 +0200 Subject: [PATCH 17/17] fix merge inconsistencies --- src/ARCtrl/ARCtrl.fs | 2 +- tests/ARCtrl/ARCtrl.Tests.fs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ARCtrl/ARCtrl.fs b/src/ARCtrl/ARCtrl.fs index ca1f7522..74f294d5 100644 --- a/src/ARCtrl/ARCtrl.fs +++ b/src/ARCtrl/ARCtrl.fs @@ -257,7 +257,7 @@ type ARC(?isa : ISA.ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSyste let registeredAssays = registeredStudies - |> Array.map (fun s -> s.Assays.ToArray()) // to-do: s.RegisteredAssays + |> Array.map (fun s -> s.RegisteredAssays.ToArray()) // to-do: s.RegisteredAssays |> Array.concat let includeRootFiles : Set = diff --git a/tests/ARCtrl/ARCtrl.Tests.fs b/tests/ARCtrl/ARCtrl.Tests.fs index c8ed85a8..4a3755f0 100644 --- a/tests/ARCtrl/ARCtrl.Tests.fs +++ b/tests/ARCtrl/ARCtrl.Tests.fs @@ -315,9 +315,9 @@ let private ``payload_file_filters`` = studyTable.AppendColumn(CompositeHeader.FreeText "Some File", [|CompositeCell.createFreeText "xd/some_file_that_lies_in_slashxd.txt"|]) studyTable.AppendColumn(CompositeHeader.ProtocolREF, [|CompositeCell.createFreeText "study_protocol.pdf"|]) studyTable.AppendColumn(CompositeHeader.Output (IOType.RawDataFile), [|CompositeCell.createFreeText "registered_study_output.txt"|]) - study.AddAssay(assay) + study.AddRegisteredAssay(assay) - inv.AddStudy(study) + inv.AddRegisteredStudy(study) let fs = Folder("root",[|