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

Feature, Excel. Restore full finctionality of the button: "Rectify Ontology Terms" #551

Merged
merged 5 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
126 changes: 116 additions & 10 deletions src/Client/OfficeInterop/OfficeInterop.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,59 @@ 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 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
)
}

/// <summary>
/// Get the main column of the arc table of the selected building block of the active annotation table
/// </summary>
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
}

/// <summary>
/// Returns a ResizeArray of indices and header names for the selected building block
/// The indices are rebased to the excel annotation table.
Expand Down Expand Up @@ -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<int*string []>) (indexedTerms: list<int*Term option>) =
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
)
}
/// <summary>
/// Validates the arc table of the currently selected work sheet
/// When the validations returns an error, an error is returned to the user
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/Client/SidebarComponents/Navbar.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down