diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index 58b5c054..460513a6 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -1619,6 +1619,59 @@ let getSelectedBuildingBlock (table: Table) (context: RequestContext) = ) } +/// +/// Returns a ResizeArray of indices and header names for the selected building block +/// The indices are rebased to the excel annotation table. +/// +/// +/// +let getBuildingBlockByColumnIndex (table: Table) (excelColumnIndex: float) (context: RequestContext) = + promise { + let headerRange = table.getHeaderRowRange() + let _ = headerRange.load(U2.Case2 (ResizeArray [|"columnIndex"; "values"; "columnCount"|])) |> ignore + + return! context.sync().``then``(fun _ -> + let rebasedIndex = excelColumnIndex - 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 + ) + } + +/// +/// Get the main column of the arc table of the selected building block of the active annotation table +/// +let getArcIndex (excelTable: Table) (excelColumnIndex: float) (context: RequestContext) = + promise { + let! selectedBlock = getBuildingBlockByColumnIndex excelTable excelColumnIndex context + + let protoHeaders = excelTable.getHeaderRowRange() + let _ = protoHeaders.load(U2.Case2 (ResizeArray(["values"]))) + + do! context.sync().``then``(fun _ -> ()) + + let headers = protoHeaders.values.Item 0 |> Array.ofSeq |> Array.map (fun c -> c.ToString()) + + let arcTableIndices = (groupToBuildingBlocks headers) |> Array.ofSeq |> Array.map (fun i -> i |> Array.ofSeq) + + let arcTableIndex = + let potResult = arcTableIndices |> Array.mapi (fun i c -> i, c |> Array.tryFind (fun (_, s) -> s = snd selectedBlock.[0])) + let result = potResult |> Array.filter (fun (_, c) -> c.IsSome) |> Array.map (fun (i, c) -> i, c.Value) + Array.tryHead result + + let arcTableIndex = + if arcTableIndex.IsSome then + fst arcTableIndex.Value + else failwith "Could not find a fitting arc table index" + + return arcTableIndex + } + /// /// Returns a ResizeArray of indices and header names for the selected building block /// The indices are rebased to the excel annotation table. @@ -2141,6 +2194,50 @@ let validateSelectedAndNeighbouringBuildingBlocks () = } ) +let getTermData names = + promise { + let terms = + names + |> List.map (fun name -> + TermQuery.create(name, searchMode=Database.FullTextSearch.Exact) + ) + |> Array.ofSeq + let! result = Async.StartAsPromise (Api.ontology.searchTerms terms) + + return + result + |> Array.map (fun item -> Array.tryHead item.results) + } + +let updateSelectedBuildingBlocks (excelTable: Table) (arcTable: ArcTable) (propertyColumns: array) (indexedTerms: list) = + promise { + let headers = ARCtrl.Spreadsheet.ArcTable.helperColumnStrings |> Array.ofSeq + + for pi in 0..propertyColumns.Length-1 do + let pIndex, pcv = propertyColumns.[pi] + let values = Array.create (arcTable.RowCount + 1) "" + indexedTerms + |> List.iter (fun (mainIndex, potTerm) -> + match potTerm with + | Some term -> + match pcv.[0] with + | header when header = headers.[2] -> //Unit + values.[mainIndex] <- term.Name + | header when header.Contains(headers.[0]) -> //Term Source REF + values.[mainIndex] <- term.FK_Ontology + | header when header.Contains(headers.[1]) -> //Term Accession Number + values.[mainIndex] <- term.Accession + | _ -> () + | None -> values.[mainIndex] <- pcv.[mainIndex] + + let bodyValues = + values + |> Array.map (box >> Some) + |> Array.map (fun c -> ResizeArray[c]) + |> ResizeArray + excelTable.columns.items.[pIndex].values <- bodyValues + ) + } /// /// Validates the arc table of the currently selected work sheet /// When the validations returns an error, an error is returned to the user @@ -2164,14 +2261,13 @@ let rectifyTermColumns () = let arcTable = arcTable.Value let columns = arcTable.Columns - let _ = excelTable.columns.load(propertyNames = U2.Case2 (ResizeArray[|"items"; "values"; "rowCount"|])) + let _ = excelTable.columns.load(propertyNames = U2.Case2 (ResizeArray[|"items"; "rowCount"; "values";|])) do! context.sync().``then``(fun _ -> ()) let items = excelTable.columns.items do! context.sync().``then``(fun _ -> ()) let termAndUnitHeaders = columns |> Array.choose (fun item -> if item.Header.IsTermColumn then Some (item.Header.ToString()) else None) - let columns = items |> Array.ofSeq @@ -2231,25 +2327,35 @@ let rectifyTermColumns () = |> Array.map (fun cv -> cv, String.IsNullOrEmpty(cv)) ) + let mutable names = [] + let mutable indices = [] + //Check whether value of property colum is fitting for value of main column and adapt if not //Delete values of property columns when main column is empty - propertyColumns - |> Array.iter (fun (pIndex, pcv) -> + for pi in 0..propertyColumns.Length-1 do + let pIndex, pcv = propertyColumns.[pi] let values = Array.create (arcTable.RowCount + 1) "" - mainColumnHasValues - |> Array.iteri (fun mainIndex (mc, isNull) -> - //if isNull for main column, then use empty string as value for properties + for mainIndex in 0..mainColumnHasValues.Length-1 do + let mc, isNull = mainColumnHasValues.[mainIndex] if not isNull then + names <- mc::names + indices <- mainIndex::indices values.[mainIndex] <- pcv.[mainIndex] - ) - let bodyValues = values |> Array.map (box >> Some) |> Array.map (fun c -> ResizeArray[c]) |> ResizeArray excelTable.columns.items.[pIndex].values <- bodyValues - ) + + let! terms = getTermData names + + let indexedTerms = + indices + |> List.mapi (fun ii index -> + index, terms.[ii]) + + do! updateSelectedBuildingBlocks excelTable arcTable propertyColumns indexedTerms do! ExcelHelper.adoptTableFormats(excelTable, context, true) diff --git a/src/Client/SidebarComponents/Navbar.fs b/src/Client/SidebarComponents/Navbar.fs index 26ed39e7..60f70a43 100644 --- a/src/Client/SidebarComponents/Navbar.fs +++ b/src/Client/SidebarComponents/Navbar.fs @@ -289,7 +289,7 @@ let private ShortCutIconList navState setNavState model (dispatch: Messages.Msg | _ -> () QuickAccessButton.create( - "Validate / Update Ontology Terms", + "Rectify Ontology Terms", [ Html.i [prop.className "fa-solid fa-spell-check"] Html.span model.ExcelState.FillHiddenColsStateStore.toReadableString