diff --git a/src/ARCtrl/ARCtrl.fs b/src/ARCtrl/ARCtrl.fs index d790798a..74f294d5 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 @@ -30,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) @@ -165,31 +161,34 @@ 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 |> Array.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 -> + registeredStudy.UpdateReferenceByStudyFile(study,true) + | None -> + investigation.AddStudy(study) + ) + 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 + | 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) + 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 @@ -212,18 +211,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." @@ -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/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.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..bbf1921a 100644 --- a/src/ISA/ISA.Spreadsheet/ArcStudy.fs +++ b/src/ISA/ISA.Spreadsheet/ArcStudy.fs @@ -1,69 +1,81 @@ -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 |> not then + let updatedTables = + ArcTables.updateReferenceTablesBySheets( + (ArcTables studyMetadata.Tables), + (ArcTables (ResizeArray sheets)), + keepUnusedRefTables = true + ) + studyMetadata.Tables <- updatedTables.Tables + 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/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/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/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/ArcTable.fs b/src/ISA/ISA/ArcTypes/ArcTable.fs index 4e970871..6f21091c 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 createFromHeaders(name,headers : ResizeArray) = + ArcTable.create(name,headers,Dictionary()) + + static member createFromRows(name,headers : ResizeArray,rows : CompositeCell[][]) : ArcTable = + let t = ArcTable.createFromHeaders(name,headers) + t.AddRows(rows) + t + /// Will return true or false if table is valid. /// /// Set `raiseException` = `true` to raise exception. @@ -139,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) @@ -156,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 updatetColumn (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 - // @@ -253,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 @@ -532,9 +546,70 @@ 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) + /// 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) + 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) + ) + + /// 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) + match index with + | 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 + /// + /// 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 30f1e607..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 = @@ -275,6 +278,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 @@ -739,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 = @@ -748,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..c071fbbd 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,51 @@ 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,?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.toArray + |> Array.collect ArcTable.SplitByProtocolREF + |> Array.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 -> + usedTables.Add(k) |> ignore + let updatedTable = ArcTable.updateReferenceByAnnotationTable rt t + ArcTable.create(t.Name, updatedTable.Headers, updatedTable.Values) + | None -> t + ) + |> Array.groupBy (fun t -> t.Name) + |> Array.map (fun (_,ts) -> + ts + |> Seq.reduce ArcTable.append + ) + |> 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 new file mode 100644 index 00000000..d1f2aa07 --- /dev/null +++ b/src/ISA/ISA/ArcTypes/ArcTypes.fs @@ -0,0 +1,1696 @@ +namespace rec ARCtrl.ISA + +open Fable.Core +open ARCtrl.ISA.Aux + +module ArcTypesAux = + + open System.Collections.Generic + + 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 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." + | 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 -> + 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 + let registeredAssays = ResizeArray(study.RegisteredAssayIdentifiers) + for registeredAssay in registeredAssays do + 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 []) = + 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 + + 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) + + 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,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 + ) + + /// + /// 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. + /// + /// 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 + + /// 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. + 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, ?registeredAssayIdentifiers: ResizeArray, ?factors, ?comments) = + let publications = defaultArg publications [||] + let contacts = defaultArg contacts [||] + let studyDesignDescriptors = defaultArg studyDesignDescriptors [||] + let tables = defaultArg tables <| ResizeArray() + let registeredAssayIdentifiers = defaultArg registeredAssayIdentifiers <| 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 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, ?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 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. + /// + 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.RegisteredAssayIdentifiers.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.RegisteredAssayCount + with get() = this.RegisteredAssayIdentifiers.Count + + member this.RegisteredAssays + 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 // + /// + /// Add assay to investigation and register it to study. + /// + /// + member this.AddRegisteredAssay(assay: ArcAssay) = + let inv = ArcTypesAux.SanityChecks.validateRegisteredInvestigation this.Investigation + inv.AddAssay(assay) + inv.RegisterAssay(this.Identifier,assay.Identifier) + + static member addRegisteredAssay(assay: ArcAssay) = + fun (study:ArcStudy) -> + let newStudy = study.Copy() + newStudy.AddRegisteredAssay(assay) + newStudy + + // - Assay API - CRUD // + member this.InitRegisteredAssay(assayIdentifier: string) = + let assay = ArcAssay(assayIdentifier) + this.AddRegisteredAssay(assay) + assay + + static member initRegisteredAssay(assayIdentifier: string) = + fun (study:ArcStudy) -> + let copy = study.Copy() + 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) + + static member registerAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.RegisterAssay(assayIdentifier) + copy + + // - Assay API - CRUD // + member this.DeregisterAssay(assayIdentifier: string) = + this.RegisteredAssayIdentifiers.Remove(assayIdentifier) |> ignore + + static member deregisterAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.DeregisterAssay(assayIdentifier) + copy + + // - 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 getRegisteredAssay(assayIdentifier: string) = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.GetRegisteredAssay(assayIdentifier) + + // - Assay API - CRUD // + static member getRegisteredAssays() = + fun (study: ArcStudy) -> + let copy = study.Copy() + copy.RegisteredAssays + + /// + /// 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.RegisteredAssays + | None -> + 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() + copy.GetRegisteredAssaysOrIdentifier() + + //////////////////////////////////// + // - 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,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 + + + /// 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. + /// + /// In order to copy the ArcAssays as well, use the Copy() method of the ArcInvestigation. + member this.Copy() : ArcStudy = + let nextTables = ResizeArray() + let nextAssayIdentifiers = ResizeArray(this.RegisteredAssayIdentifiers) + for table in this.Tables do + let copy = table.Copy() + nextTables.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, + registeredAssayIdentifiers = nextAssayIdentifiers, + factors = nextFactors, + comments = nextComments + ) + + /// + /// 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** + member this.UpdateReferenceByStudyFile(study:ArcStudy,?onlyReplaceExisting : bool,?keepUnusedRefTables) = + let onlyReplaceExisting = defaultArg onlyReplaceExisting false + let updateAlways = onlyReplaceExisting |> not + if study.Title.IsSome || updateAlways then + this.Title <- study.Title + if study.Description.IsSome || updateAlways then + this.Description <- study.Description + if study.SubmissionDate.IsSome || updateAlways then + this.SubmissionDate <- study.SubmissionDate + if study.PublicReleaseDate.IsSome || updateAlways then + this.PublicReleaseDate <- study.PublicReleaseDate + if study.Publications.Length <> 0 || updateAlways then + this.Publications <- study.Publications + if study.Contacts.Length <> 0 || updateAlways then + this.Contacts <- study.Contacts + 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),?keepUnusedRefTables = keepUnusedRefTables) + this.Tables <- tables.Tables + if study.RegisteredAssayIdentifiers.Count <> 0 || updateAlways then + this.RegisteredAssayIdentifiers <- study.RegisteredAssayIdentifiers + if study.Factors.Length <> 0 || updateAlways then + this.Factors <- study.Factors + if study.Comments.Length <> 0 || updateAlways then + this.Comments <- study.Comments + + /// + /// 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 = + 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) + let assays = + arcAssays |> Option.defaultValue (this.GetRegisteredAssaysOrIdentifier()) + |> List.ofSeq |> List.map (fun a -> a.ToAssay()) + 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 |> Option.fromValueWithDefault []), + ?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 * 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) |> Option.defaultValue (ResizeArray()) + let assaysIdentifiers = assays |> Seq.map (fun a -> a.Identifier) |> 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, + ?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, ?registeredStudyIdentifiers : ResizeArray, ?comments : Comment [], ?remarks : Remark []) as this = + + let ontologySourceReferences = defaultArg ontologySourceReferences [||] + let publications = defaultArg publications [||] + let contacts = defaultArg contacts [||] + 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 [||] + + 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 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,?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) (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 + + member this.AssayIdentifiers + with get(): string [] = this.Assays |> Seq.map (fun (x:ArcAssay) -> x.Identifier) |> Array.ofSeq + + // - 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) = + 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)) + assay.Investigation <- Some(this) + 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.RegisteredStudies + with get() = + this.RegisteredStudyIdentifiers + |> Seq.map (fun identifier -> this.GetStudy identifier) + |> ResizeArray + + 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 + study.Investigation <- Some this + 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 (studyIdentifier: string) = + let study = ArcStudy.init(studyIdentifier) + this.AddStudy(study) + study + + static member initStudy(studyIdentifier: string) = + fun (inv: ArcInvestigation) -> + let copy = inv.Copy() + 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.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) + + 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) + study.Investigation <- Some this + 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 + this.SetStudyAt(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.validateAssayRegisterInInvestigation assayIdentifier (this.Assays |> Seq.map (fun a -> a.Identifier)) + ArcTypesAux.SanityChecks.validateUniqueAssayIdentifier assayIdentifier study.RegisteredAssayIdentifiers + 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 // + 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. + /// + 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()) + let nextStudyIdentifiers = ResizeArray(this.RegisteredStudyIdentifiers) + let i = ArcInvestigation( + this.Identifier, + ?title = this.Title, + ?description = this.Description, + ?submissionDate = this.SubmissionDate, + ?publicReleaseDate = this.PublicReleaseDate, + studies = nextStudies, + assays = nextAssays, + registeredStudyIdentifiers = nextStudyIdentifiers, + ontologySourceReferences = nextOntologySourceReferences, + publications = nextPublications, + contacts = nextContacts, + comments = nextComments, + remarks = nextRemarks + ) + 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 = + 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 + 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 studiesRaw, assaysRaw = + i.Studies + |> Option.defaultValue [] + |> 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, + ?title = i.Title, + ?description = i.Description, + ?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) + ) + i \ No newline at end of file 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..593e87f0 100644 --- a/src/ISA/ISA/Update.fs +++ b/src/ISA/ISA/Update.fs @@ -1,5 +1,17 @@ namespace ARCtrl.ISA.Aux +module internal 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/ARCtrl/ARCtrl.Tests.fs b/tests/ARCtrl/ARCtrl.Tests.fs index 12298f8f..4a3755f0 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() @@ -67,18 +67,59 @@ 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" - 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" [ @@ -97,7 +138,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 +169,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 +208,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").RegisterAssay(assay.Identifier) |> ignore let arc = ARC(isa = inv) let contracts = arc.GetWriteContracts() let contractPathsString = contracts |> Array.map (fun c -> c.Path) |> String.concat ", " @@ -242,7 +283,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" @@ -274,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",[| 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.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 = 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/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 5cf0c867..02b04026 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,7 +127,9 @@ 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 registeredStudyIdentifiers = ResizeArray(["Study 1"]) let comments = [|Comment.create("Comment 1")|] let remarks = [|Remark.create(1, "Remark 1")|] @@ -129,7 +143,9 @@ let private test_create = ontologySourceReferences publications contacts + assays studies + registeredStudyIdentifiers comments remarks @@ -141,11 +157,112 @@ 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" + 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" [ + 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.RegisteredAssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayIdentifiers.[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.RegisteredAssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayIdentifiers.[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.RegisteredAssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" + i.DeregisterAssay(s.Identifier,a.Identifier) + Expect.equal s.RegisteredAssayCount 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.RegisteredAssayCount 1 "registered assay count" + Expect.equal s.RegisteredAssayIdentifiers.[0] a.Identifier "identifier" + s.DeregisterAssay(a.Identifier) + 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") + 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.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.RegisteredAssayCount 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.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.RegisteredAssayCount 1 "registered assay count 2" + i.DeregisterMissingAssays() + Expect.equal s.RegisteredAssayCount 0 "registered assay count 3" +] + let tests_MutableFields = testList "MutableFields" [ testCase "ensure investigation" <| fun _ -> let i = ArcInvestigation.init("MyInvestigation") @@ -159,6 +276,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.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.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" + Expect.equal i.Assays.[0].TableCount 1 "table count from investigation" ] let tests_Copy = testList "Copy" [ @@ -297,21 +431,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.RegisteredAssayCount 2 "Registered AssayCount" let s2 = i.Studies.Item 1 Expect.equal s2.Title (Some "Study 2 Title") "Study 2 Title" testList "AddAssay" [ @@ -320,22 +457,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 + Expect.equal s.RegisteredAssayCount 3 "AssayCount" + 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 + Expect.equal s.RegisteredAssayCount 3 "AssayCount" + let actual = i.GetAssayAt 2 Expect.equal actual expected "equal" ] testList "SetAssay" [ @@ -344,92 +483,189 @@ 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.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" 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.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() 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.RegisteredAssayCount 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" ] ] +//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" [ test_create + test_RegisteredAssays tests_MutableFields tests_Copy tests_Study tests_Assay + // tests_UpdateBy ] \ No newline at end of file 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..5cf2ec20 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, + registeredAssayIdentifiers = 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.RegisteredAssayIdentifiers 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, + registeredAssayIdentifiers = 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.RegisteredAssayIdentifiers assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" Expect.equal actual.Comments comments "Comments" @@ -107,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 _ -> @@ -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,31 +155,141 @@ 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.RegisteredAssayIdentifiers assay_identifiers "Assays" Expect.equal actual.Factors factors "Factors" 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 + let createTestAssay() = + ArcAssay.init(_assay_identifier) + testList "no parent" [ + testCase "RegisterAssay" <| fun _ -> + let study = createTestStudy() + study.RegisterAssay(_assay_identifier) + Expect.equal study.RegisteredAssayCount 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.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.RegisteredAssayCount 1 "registered assay count" + study.DeregisterAssay(_assay_identifier) + Expect.equal study.RegisteredAssayCount 0 "registered assay count 2" + testCase "InitAssay" <| fun _ -> + let study = createTestStudy() + 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.AddRegisteredAssay(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.RegisteredAssayCount 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.RegisteredAssays + 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.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.InitRegisteredAssay(_assay_identifier) + Expect.equal i.AssayCount 1 "AssayCount" + Expect.equal i.StudyCount 1 "StudyCount" + 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.AddRegisteredAssay(assay) + Expect.equal i.AssayCount 1 "AssayCount" + Expect.equal i.StudyCount 1 "StudyCount" + 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.AddRegisteredAssay(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 = testList "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" + Expect.equal study.RegisteredAssayCount 1 "AssayCount" + let assayIdentifier = study.RegisteredAssayIdentifiers.[0] + Expect.equal assayIdentifier _assay_identifier "_assay_identifier" testCase "test mutable fields" <| fun _ -> let newDesciption = Some "New Description" let newPublicReleaseDate = Some "01.01.2000" @@ -184,51 +301,171 @@ 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" - let assay = study.GetAssayAt(0) - Expect.equal assay.Identifier _assay_identifier "_assay_identifier" - Expect.equal assay.TechnologyPlatform (Some _assay_technologyPlatform) "_assay_technologyPlatform" + 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" - 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" + Expect.equal copy.RegisteredAssayCount 1 "copy AssayCount" + let assayIdentifier = study.RegisteredAssayIdentifiers.[0] + Expect.equal assayIdentifier _assay_identifier "copy _assay_identifier" () ] +let tests_UpdateBy = testList "UpdateReferenceByStudyFile" [ + + let protocolREF = "MyProtocol" + let protocolDescription = "MyProtocolDescription" + + 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 = + 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")|] + 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 "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", + tables = tables.Tables + ) + actual.UpdateReferenceByStudyFile(next) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + + 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") + let expected = createFullStudy() + actual.UpdateReferenceByStudyFile(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 "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.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 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 "full replace, empty" <| fun _ -> + let actual = createFullStudy() + let next = + ArcStudy( + identifier = "Next_identifier" + ) + let original = createFullStudy() + actual.UpdateReferenceByStudyFile(next, false) + Expect.notEqual actual next "not equal" + Expect.notEqual actual.Identifier next.Identifier "Identifier" + 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 = testList "ArcStudy" [ tests_copy + tests_RegisteredAssays test_create + tests_UpdateBy ] diff --git a/tests/ISA/ISA.Tests/ArcTable.Tests.fs b/tests/ISA/ISA.Tests/ArcTable.Tests.fs index 185dabc3..272febe8 100644 --- a/tests/ISA/ISA.Tests/ArcTable.Tests.fs +++ b/tests/ISA/ISA.Tests/ArcTable.Tests.fs @@ -1947,7 +1947,95 @@ 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 + let refTable = ArcTable.updateReferenceByAnnotationTable refTable 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 "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 + let refTable = ArcTable.updateReferenceByAnnotationTable refTable 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 + 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" + + ) + ] let main = testList "ArcTable" [ @@ -1967,4 +2055,5 @@ let main = tests_AddRow tests_AddRows tests_validate + tests_UpdateRefWithSheet ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/ArcTables.Tests.fs b/tests/ISA/ISA.Tests/ArcTables.Tests.fs new file mode 100644 index 00000000..9fb1eaaf --- /dev/null +++ b/tests/ISA/ISA.Tests/ArcTables.Tests.fs @@ -0,0 +1,297 @@ +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 "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) + + 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 "ArcTablesTests" [ + updateReferenceWithSheet + ] \ No newline at end of file diff --git a/tests/ISA/ISA.Tests/Fable.Tests.fs b/tests/ISA/ISA.Tests/Fable.Tests.fs index db5e0623..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()) [||] [||] + 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" ) 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