diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index 99a2ad41..6d722f9f 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -1,6 +1,5 @@ module OfficeInterop.Core -open System.Collections.Generic open Fable.Core open ExcelJS.Fable open Excel @@ -14,6 +13,91 @@ open OfficeInterop open OfficeInterop.HelperFunctions open BuildingBlockFunctions +open ARCtrl +open ARCtrl.Spreadsheet + +module OfficeInteropExtensions = + + open ARCtrl.Spreadsheet.ArcTable + + type ArcTable with + + /// + /// WIP + /// + /// + /// + /// + static member ofStringSeqs(name:string, headers:#seq, rows:#seq<#seq>) = + + let columns = + Seq.append [headers] rows + |> Seq.transpose + + let columnsList = + columns + |> Seq.toArray + |> Array.map (Seq.toArray) + + + let compositeColumns = ArcTable.composeColumns columnsList + + let arcTable = + ArcTable.init name + |> ArcTable.addColumns(compositeColumns,skipFillMissing = true) + |> Some + + arcTable + + /// + /// Transforms ArcTable to excel compatible "values", row major + /// + member this.ToExcelValues() = + + let table = this + + // Cancel if there are no columns + if table.Columns.Length = 0 then + ResizeArray() + else + let columns = + table.Columns + |> List.ofArray + |> List.sortBy classifyColumnOrder + |> List.collect CompositeColumn.toStringCellColumns + |> Seq.transpose + |> Seq.map (fun x -> + x |> Seq.map (box >> Some) + |> ResizeArray + ) + |> ResizeArray + + columns + + //|> List.iteri (fun colI col -> + // col + // |> List.iteri (fun rowI stringCell -> + // let value = + // if rowI = 0 then + + // match Dictionary.tryGet stringCell stringCount with + // | Some spaces -> + // stringCount.[stringCell] <- spaces + " " + // stringCell + " " + spaces + // | None -> + // stringCount.Add(stringCell,"") + // stringCell + // else stringCell + // let address = FsAddress(rowI+1,colI+1) + // fsTable.Cell(address, ws.CellCollection).SetValueAs value + // ) + //) + //ws + + let x = 0 + +open OfficeInteropExtensions + // Reoccuring Comment Defitinitions // 'annotationTables' -> For a workbook (NOT! worksheet) all tables must have unique names. Therefore not all our tables can be called 'annotationTable'. @@ -165,8 +249,7 @@ let private createAnnotationTableAtRange (isDark:bool, tryUseLastOutput:bool, ra "TableStyleMedium7" // The next part loads relevant information from the excel objects and allows us to access them after 'context.sync()' - - let tableRange = range + let tableRange = range.getColumn(0) let _ = tableRange.load(U2.Case2 (ResizeArray(["rowIndex"; "columnIndex"; "rowCount";"address"; "isEntireColumn"; "worksheet"]))) let activeSheet = tableRange.worksheet @@ -186,7 +269,7 @@ let private createAnnotationTableAtRange (isDark:bool, tryUseLastOutput:bool, ra let! allTableNames = getAllTableNames context // sync with proxy objects after loading values from excel - let! table,newTableLogging = context.sync().``then``( fun _ -> + let! table, newTableLogging = context.sync().``then``( fun _ -> // Filter all names of tables on the active worksheet for names starting with "annotationTable". let annoTables = @@ -209,48 +292,39 @@ let private createAnnotationTableAtRange (isDark:bool, tryUseLastOutput:bool, ra // Ref. 1 r.enableEvents <- false - // We do not want to create annotation tables of any size. The recommended workflow is to use the addBuildingBlock functionality. - // Therefore we recreate the tableRange but with a columncount of 2. The 2 Basic columns in any annotation table. - // "Source Name" | "Sample Name" - let adaptedRange = - let rowCount = - if useExistingPrevOutput then - (float prevTableOutput.Length + 1.) - elif tableRange.isEntireColumn then - 21. - elif tableRange.rowCount <= 2. then - 2. - else - tableRange.rowCount - activeSheet.getRangeByIndexes(tableRange.rowIndex,tableRange.columnIndex,rowCount,2.) - // Create table in current worksheet - let annotationTable = activeSheet.tables.add(U2.Case1 adaptedRange,true) - - // Update annotationTable column headers - (annotationTable.columns.getItemAt 0.).name <- BuildingBlockType.Source.toString - (annotationTable.columns.getItemAt 1.).name <- BuildingBlockType.Sample.toString - - if useExistingPrevOutput then - let newColValues = prevTableOutput |> Array.map (fun cell -> ResizeArray[|Option.bind (box >> Some) cell.Value|] ) |> ResizeArray - let col1 = (annotationTable.columns.getItemAt 0.) - let body = col1.getDataBodyRange() - body.values <- newColValues - + // Create new annotationTable name let newName = findNewTableName allTableNames + + let inMemoryTable = ArcTable.init(newName) + + let newCells = Array.init (int tableRange.rowCount - 1) (fun _ -> CompositeCell.emptyFreeText) + + inMemoryTable.AddColumn(CompositeHeader.Input IOType.Source, newCells) + + let tableStrings = inMemoryTable.ToExcelValues() + + let annotationTable = activeSheet.tables.add(U2.Case1 tableRange, true) + // Update annotationTable name annotationTable.name <- newName - + + tableRange.values <- tableStrings + // Update annotationTable style annotationTable.style <- style + if useExistingPrevOutput then + let newColValues = prevTableOutput |> Array.map (fun cell -> ResizeArray[|Option.bind (box >> Some) cell.Value|] ) |> ResizeArray + let col1 = (annotationTable.columns.getItemAt 0.) + let body = col1.getDataBodyRange() + body.values <- newColValues + // Fit widths and heights of cols and rows to value size. (In this case the new column headers). activeSheet.getUsedRange().format.autofitColumns() activeSheet.getUsedRange().format.autofitRows() - // let annoTableName = allTableNames |> Array.filter (fun x -> x.StartsWith "annotationTable") - r.enableEvents <- true // Return info message @@ -268,7 +342,7 @@ let createAnnotationTable (isDark:bool, tryUseLastOutput:bool) = Excel.run (fun context -> let selectedRange = context.workbook.getSelectedRange() promise { - let! newTableLogging = createAnnotationTableAtRange (isDark,tryUseLastOutput,selectedRange,context) + let! newTableLogging = createAnnotationTableAtRange (isDark, tryUseLastOutput, selectedRange, context) // Interop logging expects list of logs return [snd newTableLogging] diff --git a/src/Client/SidebarComponents/AnnotationTableMissingWarning.fs b/src/Client/SidebarComponents/AnnotationTableMissingWarning.fs index 175028f1..ae657175 100644 --- a/src/Client/SidebarComponents/AnnotationTableMissingWarning.fs +++ b/src/Client/SidebarComponents/AnnotationTableMissingWarning.fs @@ -24,7 +24,7 @@ let annotationTableMissingWarningComponent (model:Model) (dispatch: Msg-> unit) Bulma.button.button [ Bulma.button.isFullWidth prop.onClick (fun e -> SpreadsheetInterface.CreateAnnotationTable e.ctrlKey |> Messages.InterfaceMsg |> dispatch) - prop.text "create annotation table" + prop.text "Create Annotation Table" ] ] ] diff --git a/src/Client/Update/OfficeInteropUpdate.fs b/src/Client/Update/OfficeInteropUpdate.fs index 4eb7337d..1f8e94f2 100644 --- a/src/Client/Update/OfficeInteropUpdate.fs +++ b/src/Client/Update/OfficeInteropUpdate.fs @@ -100,9 +100,9 @@ module OfficeInterop = let cmd = Cmd.OfPromise.either OfficeInterop.Core.createAnnotationTable - (false,tryUsePrevOutput) - (curry GenericInteropLogs (AnnotationtableCreated |> OfficeInteropMsg |> Cmd.ofMsg) >> DevMsg) - (curry GenericError Cmd.none >> DevMsg) + (false, tryUsePrevOutput) + (curry GenericInteropLogs (AnnotationtableCreated |> OfficeInteropMsg |> Cmd.ofMsg) >> DevMsg) //success + (curry GenericError Cmd.none >> DevMsg) //error state, model,cmd | AnnotationtableCreated ->