Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug excel enable conversion of data and free text columns of input / output #550

Merged
merged 7 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 170 additions & 63 deletions src/Client/OfficeInterop/OfficeInterop.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ module OfficeInteropExtensions =
name = name
)

/// <summary>
/// Delete an existing column at the given index
/// </summary>
/// <param name="index"></param>
/// <param name="excelTable"></param>
/// <param name="name"></param>
/// <param name="rowCount"></param>
/// <param name="value"></param>
static member deleteColumn (index: float) (excelTable: Table) =
let col = excelTable.columns.getItemAt index
col.delete()

/// <summary>
/// Add only the row values of the column you are adding
/// </summary>
Expand Down Expand Up @@ -225,7 +237,6 @@ module OfficeInteropExtensions =
member this.ToExcelValues() =

let table = this

// Cancel if there are no columns
if table.Columns.Length = 0 then
ResizeArray()
Expand Down Expand Up @@ -558,7 +569,7 @@ let tryGetActiveAnnotationTable (context: RequestContext) =
let _ = context.workbook.load(propertyNames=U2.Case2 (ResizeArray[|"tables"|]))
let activeWorksheet = context.workbook.worksheets.getActiveWorksheet().load(U2.Case1 "position")
let tables = context.workbook.tables
let _ = tables.load(propertyNames=U2.Case2 (ResizeArray[|"items"; "worksheet"; "name"; "position"; "values"|]))
let _ = tables.load(propertyNames=U2.Case2 (ResizeArray[|"items"; "worksheet"; "name"; "position"; "style"; "values"|]))

let! table = context.sync().``then``(fun _ ->
let activeWorksheetPosition = activeWorksheet.position
Expand Down Expand Up @@ -1448,7 +1459,7 @@ let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
arcTable.Value.Join(tableToAdd, ?index=index, ?joinOptions=options, forceReplace=true)

let tableValues = arcTable.Value.ToExcelValues() |> Array.ofSeq
let (headers, body) = Array.ofSeq(tableValues.[0]), tableValues.[1..]
let (headers, _) = Array.ofSeq(tableValues.[0]), tableValues.[1..]

let newTableRange = excelTable.getRange()

Expand Down Expand Up @@ -1494,18 +1505,9 @@ let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
newTable.columns.load(propertyNames = U2.Case2 (ResizeArray["name"; "items"])) |> ignore
newBodyRange.load(propertyNames = U2.Case2 (ResizeArray["name"; "columnCount"; "values"]))

do! context.sync().``then``(fun _ ->

newBodyRange.values <- ResizeArray body
newBodyRange.format.autofitColumns()
newBodyRange.format.autofitRows()
do! context.sync().``then``(fun _ -> ())

newTable.columns.items
|> Array.ofSeq
|> Array.iter (fun column ->
if ARCtrl.Spreadsheet.ArcTable.helperColumnStrings |> List.exists (fun cName -> column.name.StartsWith cName) then
column.getRange().columnHidden <- true)
)
do! ExcelHelper.adoptTableFormats(newTable, context, true)

return [InteropLogging.Msg.create InteropLogging.Warning $"Joined template {tableToAdd.Name} to table {excelTable.name}!"]
else
Expand Down Expand Up @@ -1604,6 +1606,33 @@ let getSelectedBuildingBlock (table: Table) (context: RequestContext) =
)
}

/// <summary>
/// Returns a ResizeArray of indices and header names for the selected building block
/// The indices are rebased to the excel annotation table.
/// </summary>
/// <param name="columns"></param>
/// <param name="selectedIndex"></param>
let getSelectedBuildingBlockCell (table: Table) (context: RequestContext) =
promise {

let selectedRange = context.workbook.getSelectedRange().load(U2.Case2 (ResizeArray[|"columnIndex"; "rowIndex";|]))

let headerRange = table.getHeaderRowRange()
let _ = headerRange.load(U2.Case2 (ResizeArray [|"columnCount"; "columnIndex"; "rowIndex"; "values";|])) |> ignore

return! context.sync().``then``(fun _ ->
let rebasedIndex = selectedRange.columnIndex - headerRange.columnIndex |> int
if rebasedIndex < 0 || rebasedIndex >= (int headerRange.columnCount) then
failwith "Cannot select building block outside of annotation table!"
let headers: string [] = [|for v in headerRange.values.[0] do v.Value :?> string|]
let selectedHeader = rebasedIndex, headers.[rebasedIndex]
let buildingBlockGroups = groupToBuildingBlocks headers
let selectedBuildingBlock =
buildingBlockGroups.Find(fun bb -> bb.Contains selectedHeader)
selectedBuildingBlock, selectedRange.rowIndex
)
}

/// <summary>
/// Select a building block, shifted by adaptedIndex from the selected building block
/// </summary>
Expand Down Expand Up @@ -1650,6 +1679,10 @@ let removeSelectedAnnotationBlock () =
log $"delete column {i}"
column.delete()

do! context.sync().``then``(fun _ -> ())

do! ExcelHelper.adoptTableFormats(excelTable, context, true)

return [InteropLogging.Msg.create InteropLogging.Info $"The building block associated with column {snd (selectedBuildingBlock.Item 0)} has been deleted"]
| Result.Error msgs -> return msgs
}
Expand All @@ -1658,12 +1691,10 @@ let removeSelectedAnnotationBlock () =
/// <summary>
/// Get the main column of the arc table of the selected building block of the active annotation table
/// </summary>
let getArcMainColumn (excelTable:Table) (context: RequestContext)=
let getArcMainColumn (excelTable: Table) (arcTable: ArcTable) (context: RequestContext)=
promise {
let! selectedBlock = getSelectedBuildingBlock excelTable context

let! arcTable = ArcTable.tryGetFromExcelTable(excelTable, context)

let protoHeaders = excelTable.getHeaderRowRange()
let _ = protoHeaders.load(U2.Case2 (ResizeArray(["values"])))

Expand All @@ -1684,11 +1715,11 @@ let getArcMainColumn (excelTable:Table) (context: RequestContext)=
else failwith "Could not find a fitting arc table index"

let targetColumn =
let potColumn = arcTable.Value.GetColumn arcTableIndex
let potColumn = arcTable.GetColumn arcTableIndex
if columnName.Contains(potColumn.Header.ToString()) then potColumn
else failwith "Could not find a fitting arc table index with matchin name"

return targetColumn
return (targetColumn, arcTableIndex)
}

/// <summary>
Expand All @@ -1701,59 +1732,63 @@ let tryGetArcMainColumnFromFrontEnd () =

match result with
| Result.Ok table ->
let! column = getArcMainColumn table context
let! arcTable = ArcTable.tryGetFromExcelTable(table, context)
let! column = getArcMainColumn table arcTable.Value context
return Some column
| Result.Error _ -> return None
}
)

/// <summary>
/// Delete columns of at the given indices of the table
/// </summary>
/// <param name="selectedColumns"></param>
/// <param name="excelTable"></param>
let deleteSelectedExcelColumns (selectedColumns: seq<int>) (excelTable: Table) =
// iterate DESCENDING to avoid index shift
for i in Seq.sortByDescending (fun x -> x) selectedColumns do
let column = excelTable.columns.getItemAt(i)
log $"delete column {i}"
column.delete()

/// <summary>
/// Convert the body of the given column into free text
/// </summary>
/// <param name="column"></param>
let convertToFreeTextColumn (column: CompositeColumn) =
let freeTextCells = column.Cells |> Array.map (fun c -> c.ToFreeTextCell())
freeTextCells
|> Array.iteri (fun i _ -> column.Cells.[i] <- freeTextCells.[i])
let convertToFreeTextCell (arcTable: ArcTable) (columnIndex: int) (column: CompositeColumn) rowIndex =
let freeTextCell = column.Cells.[rowIndex].ToFreeTextCell()
arcTable.UpdateCellAt(columnIndex, rowIndex, freeTextCell, true)

/// <summary>
/// Convert the body of the given column into data
/// </summary>
/// <param name="column"></param>
let convertToDataColumn (column: CompositeColumn) =
let dataCells = column.Cells |> Array.map (fun c -> c.ToDataCell())
dataCells
|> Array.iteri (fun i _ -> column.Cells.[i] <- dataCells.[i])
let convertToDataCell (arcTable: ArcTable) (columnIndex: int) (column: CompositeColumn) rowIndex =
let header = column.Header
let dataCell = column.Cells.[rowIndex].ToDataCell()
let newHeader =
match header with
| header when header.isOutput -> CompositeHeader.Output IOType.Data
| header when header.isInput -> CompositeHeader.Input IOType.Data
| _ -> CompositeHeader.FreeText (header.ToString())
arcTable.UpdateHeader(columnIndex, newHeader, false)
arcTable.UpdateCellAt(columnIndex, rowIndex, dataCell, true)

/// <summary>
/// Convert the body of the given column into unit
/// </summary>
/// <param name="column"></param>
let convertToUnitColumn (column: CompositeColumn) =
let unitCells = column.Cells |> Array.map (fun c -> c.ToUnitizedCell())
unitCells
|> Array.iteri (fun i _ -> column.Cells.[i] <- unitCells.[i])
let convertToUnitCell (column: CompositeColumn) rowIndex =
let unitCell = column.Cells.[rowIndex].ToUnitizedCell()
column.Cells.[rowIndex] <- unitCell

/// <summary>
/// Convert the body of the given column into term
/// </summary>
/// <param name="column"></param>
let convertToTermColumn (column: CompositeColumn) =
let termCells = column.Cells |> Array.map (fun c -> c.ToTermCell())
termCells
|> Array.iteri (fun i _ -> column.Cells.[i] <- termCells.[i])

/// <summary>
/// Delete columns of at the given indices of the table
/// </summary>
/// <param name="selectedColumns"></param>
/// <param name="excelTable"></param>
let deleteSelectedExcelColumns (selectedColumns: seq<int>) (excelTable: Table) =
// iterate DESCENDING to avoid index shift
for i in Seq.sortByDescending (fun x -> x) selectedColumns do
let column = excelTable.columns.getItemAt(i)
log $"delete column {i}"
column.delete()
let convertToTermCell (column: CompositeColumn) rowIndex =
let termCell = column.Cells.[rowIndex].ToTermCell()
column.Cells.[rowIndex] <- termCell

/// <summary>
/// Add a building block at a specified position to the given table
Expand All @@ -1762,7 +1797,7 @@ let deleteSelectedExcelColumns (selectedColumns: seq<int>) (excelTable: Table) =
/// <param name="newBB"></param>
/// <param name="table"></param>
/// <param name="context"></param>
let addBuildingBlockAt (excelIndex: int) (newBB: CompositeColumn) (table: Table) (context: RequestContext)=
let addBuildingBlockAt (excelIndex: int) (newBB: CompositeColumn) (table: Table) (context: RequestContext) =
promise {
let headers = table.getHeaderRowRange()
let _ = headers.load(U2.Case2 (ResizeArray [|"values"|]))
Expand Down Expand Up @@ -1791,6 +1826,35 @@ let addBuildingBlockAt (excelIndex: int) (newBB: CompositeColumn) (table: Table)
|> List.iteri (fun ci header -> ExcelHelper.addColumnAndRows (float (excelIndex + ci)) table header bodyValues.[ci] |> ignore)
}

let getSelectedCellType () =
Excel.run(fun context ->
promise {
let! result = tryGetActiveAnnotationTable context

match result with
| Result.Ok excelTable ->
let! _, rowIndex = getSelectedBuildingBlockCell excelTable context
let! arcTable = ArcTable.tryGetFromExcelTable(excelTable, context)
let! (arcMainColumn, _) = getArcMainColumn excelTable arcTable.Value context

if rowIndex > 0 then

let result =
match arcMainColumn with
| amc when amc.Cells.[(int rowIndex) - 1].isUnitized -> Some CompositeCellDiscriminate.Unitized
| amc when amc.Cells.[(int rowIndex) - 1].isTerm -> Some CompositeCellDiscriminate.Term
| amc when amc.Cells.[(int rowIndex) - 1].isData -> Some CompositeCellDiscriminate.Data
| amc when amc.Header.isInput && amc.Header.IsDataColumn && amc.Cells.[(int rowIndex) - 1].isFreeText -> Some CompositeCellDiscriminate.Text
Freymaurer marked this conversation as resolved.
Show resolved Hide resolved
| amc when amc.Header.isOutput && amc.Header.IsDataColumn && amc.Cells.[(int rowIndex) - 1].isFreeText -> Some CompositeCellDiscriminate.Text
| _ -> None

return result
else return None

| Result.Error _ -> return None
}
)

/// <summary>
/// This function is used to convert building blocks that can be converted. Data building blocks can be converted into free text, free text into data,
/// terms into units and units into terms
Expand All @@ -1802,30 +1866,73 @@ let convertBuildingBlock () =

match result with
| Result.Ok excelTable ->
let! selectedBuildingBlock = getSelectedBuildingBlock excelTable context
let excelMainColumnIndex = fst selectedBuildingBlock.[0]
let! arcMainColumn = getArcMainColumn excelTable context
let! selectedBuildingBlock, rowIndex = getSelectedBuildingBlockCell excelTable context
let! arcTable = ArcTable.tryGetFromExcelTable(excelTable, context)

let! (arcMainColumn, arcIndex) = getArcMainColumn excelTable arcTable.Value context

let msgText =
match arcMainColumn with
| amc when amc.Header.isInput -> $"Input column {snd selectedBuildingBlock.[0]} cannot be converted"
| amc when amc.Header.isOutput -> $"Output column {snd selectedBuildingBlock.[0]} cannot be converted"
| _ -> ""
if rowIndex = 0 then "Headers cannot be converted"
else ""

match arcMainColumn with
| amc when amc.Cells.[0].isUnitized -> convertToTermColumn amc
| amc when amc.Cells.[0].isTerm -> convertToUnitColumn amc
| amc when amc.Cells.[0].isData -> convertToDataColumn amc
| amc when amc.Cells.[0].isFreeText -> convertToFreeTextColumn amc
| amc when amc.Cells.[(int rowIndex) - 1].isUnitized ->
convertToTermCell amc ((int rowIndex) - 1)
arcTable.Value.UpdateColumn(arcIndex, arcMainColumn.Header, arcMainColumn.Cells)
| amc when amc.Cells.[(int rowIndex) - 1].isTerm ->
convertToUnitCell amc ((int rowIndex) - 1)
arcTable.Value.UpdateColumn(arcIndex, arcMainColumn.Header, arcMainColumn.Cells)
| amc when amc.Cells.[(int rowIndex) - 1].isData -> convertToFreeTextCell arcTable.Value arcIndex amc ((int rowIndex) - 1)
| amc when amc.Cells.[(int rowIndex) - 1].isFreeText -> convertToDataCell arcTable.Value arcIndex amc ((int rowIndex) - 1)
| _ -> ()

deleteSelectedExcelColumns (selectedBuildingBlock |> Seq.map (fun (i, _) -> i)) excelTable
let name = excelTable.name
let style = excelTable.style

let newTableRange = excelTable.getRange()

let newExcelTableValues = arcTable.Value.ToExcelValues()
let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["values"; "columnCount"; "rowCount"]))

do! context.sync().``then``(fun _ ->
let difference = (newExcelTableValues.Item 0).Count - (int newTableRange.columnCount)

match difference with
| diff when diff > 0 ->
for i = 0 to diff - 1 do
ExcelHelper.addColumn (newTableRange.columnCount + (float i)) excelTable (i.ToString()) (int newTableRange.rowCount) "" |> ignore
| diff when diff < 0 ->
for i = 0 downto diff + 1 do
ExcelHelper.deleteColumn 0 excelTable
| _ -> ()
)

let newTableRange = excelTable.getRange()
let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["values"; "columnCount"]))

do! context.sync().``then``(fun _ ->
excelTable.delete()
newTableRange.values <- newExcelTableValues
)

let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["values"; "worksheet"]))

do! context.sync().``then``(fun _ -> ())

do! addBuildingBlockAt excelMainColumnIndex arcMainColumn excelTable context
let activeSheet = newTableRange.worksheet
let _ = activeSheet.load(U2.Case2 (ResizeArray[|"tables"|]))

do! ExcelHelper.adoptTableFormats(excelTable, context, true)
do! context.sync().``then``(fun _ ->
let newTable = activeSheet.tables.add(U2.Case1 newTableRange, true)
newTable.name <- name
newTable.style <- style
)

let! newTable = tryGetActiveAnnotationTable context

match newTable with
| Result.Ok table -> do! ExcelHelper.adoptTableFormats(table, context, true)
| _ -> ()

let msg =
if String.IsNullOrEmpty(msgText) then $"Converted building block of {snd selectedBuildingBlock.[0]} to unit"
Expand Down
Loading