Skip to content

Commit

Permalink
Merge pull request #424 from nfdi4plants/fixes
Browse files Browse the repository at this point in the history
Various fixes and small additions
  • Loading branch information
HLWeil authored Aug 6, 2024
2 parents 7c9e070 + d1b9f9b commit dcfa9b5
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 198 deletions.
11 changes: 7 additions & 4 deletions src/ARCtrl/ARC.fs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ type ARC(?isa : ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSystem) =
let workbooks = System.Collections.Generic.Dictionary<string, DTOType*FsWorkbook>()
match this.ISA with
| Some inv ->
let investigationConverter = Spreadsheet.ArcInvestigation.toFsWorkbook
let investigationConverter = ArcInvestigation.toFsWorkbook
workbooks.Add (InvestigationFileName, (DTOType.ISA_Investigation, investigationConverter inv))
inv.StaticHash <- inv.GetLightHashCode()
inv.Studies
Expand Down Expand Up @@ -365,7 +365,7 @@ type ARC(?isa : ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSystem) =

| None ->
//printfn "ARC contains no ISA part."
workbooks.Add (InvestigationFileName, (DTOType.ISA_Investigation, Spreadsheet.ArcInvestigation.toFsWorkbook (ArcInvestigation.create(Identifier.MISSING_IDENTIFIER))))
workbooks.Add (InvestigationFileName, (DTOType.ISA_Investigation, ArcInvestigation.toFsWorkbook (ArcInvestigation.create(Identifier.MISSING_IDENTIFIER))))

// Iterates over filesystem and creates a write contract for every file. If possible, include DTO.
_fs.Tree.ToFilePaths(true)
Expand Down Expand Up @@ -566,11 +566,14 @@ type ARC(?isa : ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSystem) =
let isa = ARCtrl.Json.Decode.fromJsonString ARCtrl.Json.ARC.ROCrate.decoder s
ARC(?isa = isa)

member this.ToROCrateJsonString(?spaces) =
ARCtrl.Json.ARC.ROCrate.encoder (Option.get _isa)
|> ARCtrl.Json.Encode.toJsonString (ARCtrl.Json.Encode.defaultSpaces spaces)

/// exports in json-ld format
static member toROCrateJsonString(?spaces) =
fun (obj:ARC) ->
ARCtrl.Json.ARC.ROCrate.encoder (Option.get obj.ISA)
|> ARCtrl.Json.Encode.toJsonString (ARCtrl.Json.Encode.defaultSpaces spaces)
obj.ToROCrateJsonString(?spaces = spaces)

/// <summary>
/// Returns the write contract for the input ValidationPackagesConfig object.
Expand Down
12 changes: 9 additions & 3 deletions src/Core/ArcTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,13 @@ module ArcTypesAux =
[<AttachMembers>]
type ArcAssay(identifier: string, ?measurementType : OntologyAnnotation, ?technologyType : OntologyAnnotation, ?technologyPlatform : OntologyAnnotation, ?tables: ResizeArray<ArcTable>, ?datamap : DataMap, ?performers : ResizeArray<Person>, ?comments : ResizeArray<Comment>) =
inherit ArcTables(defaultArg tables <| ResizeArray())

let performers = defaultArg performers <| ResizeArray()
let comments = defaultArg comments <| ResizeArray()
let mutable identifier : string = identifier
let mutable identifier : string =
let identifier = identifier.Trim()
Helper.Identifier.checkValidCharacters identifier
identifier
let mutable investigation : ArcInvestigation option = None
let mutable measurementType : OntologyAnnotation option = measurementType
let mutable technologyType : OntologyAnnotation option = technologyType
Expand Down Expand Up @@ -530,7 +533,10 @@ type ArcStudy(identifier : string, ?title, ?description, ?submissionDate, ?publi
let registeredAssayIdentifiers = defaultArg registeredAssayIdentifiers <| ResizeArray()
let comments = defaultArg comments <| ResizeArray()

let mutable identifier = identifier
let mutable identifier : string =
let identifier = identifier.Trim()
Helper.Identifier.checkValidCharacters identifier
identifier
let mutable investigation : ArcInvestigation option = None
let mutable title : string option = title
let mutable description : string option = description
Expand Down
7 changes: 5 additions & 2 deletions src/Core/Table/CompositeColumn.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ARCtrl
namespace ARCtrl

open Fable.Core

Expand Down Expand Up @@ -75,4 +75,7 @@ type CompositeColumn = {
if termCellCount >= unitCellCount then
CompositeCell.emptyTerm
else
CompositeCell.emptyUnitized
CompositeCell.emptyUnitized

member this.IsUnique =
this.Header.IsUnique
69 changes: 5 additions & 64 deletions src/Core/Table/CompositeHeader.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ARCtrl
namespace ARCtrl

open Fable.Core
open Fable.Core.JsInterop
Expand Down Expand Up @@ -92,25 +92,6 @@ type IOType =
| Some s -> IOType.ofString s |> Some
| None -> None

member this.GetUITooltip() = IOType.getUITooltip(U2.Case1 this)

static member getUITooltip(iotype: U2<IOType,string>) =
match iotype with
| U2.Case1 Source | U2.Case2 "Source" ->
"The source value must be a unique identifier for an organism or a sample."
| U2.Case1 Sample | U2.Case2 "Sample" ->
"The Sample Name column describes specifc laboratory samples with a unique identifier."
| U2.Case1 Data | U2.Case2 "RawDataFile" ->
"The Raw Data File column defines untransformed and unprocessed data files."
| U2.Case1 Data | U2.Case2 "DerivedDataFile" ->
"The Derived Data File column defines transformed and/or processed data files."
| U2.Case1 Data | U2.Case2 "ImageFile" ->
"Placeholder"
| U2.Case1 Material | U2.Case2 "Material" ->
"Placeholder"
| U2.Case1 (FreeText _) | U2.Case2 "FreeText" ->
"Placeholder"
| _ -> failwith $"Unable to parse combination to existing IOType: `{iotype}`"
#if FABLE_COMPILER

static member source() = IOType.Source
Expand Down Expand Up @@ -446,50 +427,10 @@ type CompositeHeader =
| Output io | Input io -> Some io
| _ -> None

member this.GetUITooltip() =
// https://fable.io/docs/javascript/features.html#u2-u3--u9
CompositeHeader.getUITooltip (U2.Case1
this)

// https://fable.io/docs/javascript/features.html#u2-u3--u9
// U2 is an erased union type, allowing seemless integration into js syntax
/// <summary>
/// Can pass header as `U2.Case1 compositeHeader` or `U2.Case2 string` or (requires `open Fable.Core.JsInterop`) `!^compositeHeader` or `!^string`
/// </summary>
/// <param name="header"></param>
static member getUITooltip(header:U2<CompositeHeader,string>) =
match header with
| U2.Case1 (Component _) | U2.Case2 "Component" ->
"Component columns are used to describe physical components of a experiment, e.g. instrument names, software names, and reagents names."
| U2.Case1 (Characteristic _) | U2.Case2 "Characteristic" ->
"Characteristic columns are used for study descriptions and describe inherent properties of the source material, e.g. a certain strain or organism part."
| U2.Case1 (Factor _)| U2.Case2 "Factor" ->
"Use Factor columns to describe independent variables that result in a specific output of your experiment, e.g. the light intensity under which an organism was grown."
| U2.Case1 (Parameter _) | U2.Case2 "Parameter" ->
"Parameter columns describe steps in your experimental workflow, e.g. the centrifugation time or the temperature used for your assay."
| U2.Case1 (ProtocolType) | U2.Case2 "ProtocolType" ->
"Defines the protocol type according to your preferred endpoint repository."
| U2.Case1 (ProtocolDescription) | U2.Case2 "ProtocolDescription" ->
"Describe the protocol in free text."
| U2.Case1 (ProtocolUri) | U2.Case2 "ProtocolUri" ->
"Web or local address where the in-depth protocol is stored."
| U2.Case1 (ProtocolVersion) | U2.Case2 "ProtocolVersion" ->
"Defines the protocol version."
| U2.Case1 (ProtocolREF) | U2.Case2 "ProtocolREF" ->
"Defines the protocol name."
| U2.Case1 (Performer) | U2.Case2 "Performer" ->
"Defines the protocol performer."
| U2.Case1 (Date) | U2.Case2 "Date" ->
"Defines the date the protocol was performed."
| U2.Case1 (Input _) | U2.Case2 "Input" ->
"Only one input column per table. E.g. experimental samples or files."
| U2.Case1 (Output _) |U2.Case2 "Output" ->
"Only one output column per table. E.g. experimental samples or files."
| U2.Case1 (FreeText _) | U2.Case2 "FreeText" ->
"Placeholder"
| U2.Case1 (Comment _) | U2.Case2 "Comment" ->
"Comment"
| _ -> failwith $"Unable to parse combination to existing CompositeHeader: `{header}`"
member this.IsUnique =
match this with
| ProtocolType | ProtocolREF | ProtocolDescription | ProtocolUri | ProtocolVersion | Performer | Date | Input _ | Output _ -> true
| _ -> false

#if FABLE_COMPILER

Expand Down
10 changes: 7 additions & 3 deletions src/Spreadsheet/AnnotationTable/ArcTable.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module ARCtrl.Spreadsheet.ArcTable
module ARCtrl.Spreadsheet.ArcTable

open ARCtrl
open ARCtrl.Helper
Expand Down Expand Up @@ -121,7 +121,7 @@ let tryFromFsWorksheet (sheet : FsWorksheet) =
| err -> failwithf "Could not parse table with name \"%s\":\n%s" sheet.Name err.Message


let toFsWorksheet (table : ArcTable) =
let toFsWorksheet (index : int option) (table : ArcTable) =
/// This dictionary is used to add spaces at the end of duplicate headers.
let stringCount = System.Collections.Generic.Dictionary<string,string>()
let ws = FsWorksheet(table.Name)
Expand All @@ -137,7 +137,11 @@ let toFsWorksheet (table : ArcTable) =
|> List.collect CompositeColumn.toStringCellColumns
let maxRow = columns.Head.Length
let maxCol = columns.Length
let fsTable = ws.Table("annotationTable",FsRangeAddress(FsAddress(1,1),FsAddress(maxRow,maxCol)))
let name =
match index with
| Some i -> $"{annotationTablePrefix}{i}"
| None -> annotationTablePrefix
let fsTable = ws.Table(name,FsRangeAddress(FsAddress(1,1),FsAddress(maxRow,maxCol)))
columns
|> List.iteri (fun colI col ->
col
Expand Down
48 changes: 29 additions & 19 deletions src/Spreadsheet/ArcAssay.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ module ArcAssay =
let [<Literal>] assaysPrefix = "Assay"
let [<Literal>] contactsPrefix = "Assay Person"

let [<Literal>] obsoleteMetaDataSheetName = "Assay"
let [<Literal>] metaDataSheetName = "isa_assay"
let [<Literal>] obsoleteMetadataSheetName = "Assay"
let [<Literal>] metadataSheetName = "isa_assay"

let toMetadataSheet (assay : ArcAssay) : FsWorksheet =
let toRows (assay:ArcAssay) =
Expand All @@ -25,7 +25,7 @@ module ArcAssay =
yield SparseRow.fromValues [contactsLabel]
yield! Contacts.toRows (Some contactsPrefix) (List.ofSeq assay.Performers)
}
let sheet = FsWorksheet(metaDataSheetName)
let sheet = FsWorksheet(metadataSheetName)
assay
|> toRows
|> Seq.iteri (fun rowI r -> SparseRow.writeToSheet (rowI + 1) r sheet)
Expand Down Expand Up @@ -76,6 +76,15 @@ module ArcAssay =
with
| err -> failwithf "Failed while parsing metadatasheet: %s" err.Message

let isMetadataSheetName (name:string) =
name = metadataSheetName || name = obsoleteMetadataSheetName

let isMetadataSheet (sheet : FsWorksheet) =
isMetadataSheetName sheet.Name

let tryGetMetadataSheet (doc:FsWorkbook) =
doc.GetWorksheets()
|> Seq.tryFind isMetadataSheet

[<AutoOpen>]
module ArcAssayExtensions =
Expand All @@ -86,27 +95,22 @@ module ArcAssayExtensions =
static member fromFsWorkbook (doc:FsWorkbook) : ArcAssay =
try
// Reading the "Assay" metadata sheet. Here metadata
let assayMetaData =

match doc.TryGetWorksheetByName ArcAssay.metaDataSheetName with
let assayMetadata =
match ArcAssay.tryGetMetadataSheet doc with
| Option.Some sheet ->
ArcAssay.fromMetadataSheet sheet
| None ->
match doc.TryGetWorksheetByName ArcAssay.obsoleteMetaDataSheetName with
| Option.Some sheet ->
ArcAssay.fromMetadataSheet sheet
| None ->
printfn "Cannot retrieve metadata: Assay file does not contain \"%s\" or \"%s\" sheet." ArcAssay.metaDataSheetName ArcAssay.obsoleteMetaDataSheetName
ArcAssay.create(Identifier.createMissingIdentifier())
printfn "Cannot retrieve metadata: Assay file does not contain \"%s\" or \"%s\" sheet." ArcAssay.metadataSheetName ArcAssay.obsoleteMetadataSheetName
ArcAssay.create(Identifier.createMissingIdentifier())
let sheets = doc.GetWorksheets()
let annotationTables =
sheets |> Seq.choose ArcTable.tryFromFsWorksheet
let datamapSheet =
sheets |> Seq.tryPick DataMapTable.tryFromFsWorksheet
if annotationTables |> Seq.isEmpty |> not then
assayMetaData.Tables <- ResizeArray annotationTables
assayMetaData.DataMap <- datamapSheet
assayMetaData
assayMetadata.Tables <- ResizeArray annotationTables
assayMetadata.DataMap <- datamapSheet
assayMetadata
with
| err -> failwithf "Could not parse assay: \n%s" err.Message

Expand All @@ -121,14 +125,20 @@ module ArcAssayExtensions =
static member toFsWorkbook (assay : ArcAssay, ?datamapSheet: bool) =
let datamapSheet = defaultArg datamapSheet true
let doc = new FsWorkbook()
let metaDataSheet = ArcAssay.toMetadataSheet (assay)
doc.AddWorksheet metaDataSheet
let metadataSheet = ArcAssay.toMetadataSheet (assay)
doc.AddWorksheet metadataSheet

if datamapSheet then
assay.DataMap
|> Option.iter (DataMapTable.toFsWorksheet >> doc.AddWorksheet)

assay.Tables
|> Seq.iter (ArcTable.toFsWorksheet >> doc.AddWorksheet)
|> Seq.iteri (fun i -> ArcTable.toFsWorksheet (Some i) >> doc.AddWorksheet)

doc

doc
/// Write an assay to a spreadsheet
///
/// If datamapSheet is true, the datamap will be written to a worksheet inside assay workbook.
member this.ToFsWorkbook (?datamapSheet: bool) =
ArcAssay.toFsWorkbook (this, ?datamapSheet = datamapSheet)
67 changes: 42 additions & 25 deletions src/Spreadsheet/ArcInvestigation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ module ArcInvestigation =
let [<Literal>] publicationsLabelPrefix = "Investigation Publication"
let [<Literal>] contactsLabelPrefix = "Investigation Person"

let [<Literal>] metaDataSheetName = "isa_investigation"
let [<Literal>] metaDataSheetName_deprecated = "Investigation"
let [<Literal>] metadataSheetName = "isa_investigation"
let [<Literal>] obsoleteMetadataSheetName = "Investigation"


type InvestigationInfo =
Expand Down Expand Up @@ -202,28 +202,45 @@ module ArcInvestigation =
|> insertRemarks (List.ofSeq investigation.Remarks)
|> seq

let fromFsWorkbook (doc:FsWorkbook) =
try
match doc.TryGetWorksheetByName metaDataSheetName with
| Some sheet -> sheet
| None ->
match doc.TryGetWorksheetByName metaDataSheetName_deprecated with
let isMetadataSheetName (name : string) =
name = metadataSheetName || name = obsoleteMetadataSheetName

let isMetadataSheet (sheet : FsWorksheet) =
isMetadataSheetName sheet.Name

let tryGetMetadataSheet (doc:FsWorkbook) =
doc.GetWorksheets()
|> Seq.tryFind isMetadataSheet


[<AutoOpen>]
module ArcInvestigationExtensions =

open ArcInvestigation

type ArcInvestigation with

static member fromFsWorkbook (doc:FsWorkbook) =
try
match ArcInvestigation.tryGetMetadataSheet doc with
| Some sheet -> sheet
| None -> failwith "Could not find metadata sheet with sheetname \"isa_investigation\" or deprecated sheetname \"Investigation\""
|> FsWorksheet.getRows
|> Seq.map SparseRow.fromFsRow
|> fromRows
with
| err -> failwithf "Could not read investigation from spreadsheet: %s" err.Message

let toFsWorkbook (investigation:ArcInvestigation) : FsWorkbook =
try
let wb = new FsWorkbook()
let sheet = FsWorksheet(metaDataSheetName)
investigation
|> toRows
|> Seq.iteri (fun rowI r -> SparseRow.writeToSheet (rowI + 1) r sheet)
wb.AddWorksheet(sheet)
wb
with
| err -> failwithf "Could not write investigation to spreadsheet: %s" err.Message
|> FsWorksheet.getRows
|> Seq.map SparseRow.fromFsRow
|> fromRows
with
| err -> failwithf "Could not read investigation from spreadsheet: %s" err.Message

static member toFsWorkbook (investigation:ArcInvestigation) : FsWorkbook =
try
let wb = new FsWorkbook()
let sheet = FsWorksheet(metadataSheetName)
investigation
|> toRows
|> Seq.iteri (fun rowI r -> SparseRow.writeToSheet (rowI + 1) r sheet)
wb.AddWorksheet(sheet)
wb
with
| err -> failwithf "Could not write investigation to spreadsheet: %s" err.Message

member this.ToFsWorkbook() = ArcInvestigation.toFsWorkbook this
Loading

0 comments on commit dcfa9b5

Please sign in to comment.