From 4bf33cb478861250a3f1794140821460115e3173 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Wed, 10 Feb 2021 16:57:18 +0100 Subject: [PATCH] Add ontology accession number as tag in ref columns (Issue #100). --- .../CustomComponents/AutocompleteSearch.fs | 17 +-- src/Client/Messages.fs | 21 +++- src/Client/Model.fs | 25 ++-- src/Client/OfficeInterop/HelperFunctions.fs | 62 ++++++++-- src/Client/OfficeInterop/OfficeInterop.fs | 51 ++++++--- src/Client/OfficeInterop/Regex.fs | 31 +++-- src/Client/OfficeInterop/Types.fs | 13 ++- src/Client/Update.fs | 35 +++--- src/Client/Views/AddBuildingBlockView.fs | 22 ++-- src/Client/Views/ValidationView.fs | 5 +- src/Server/Docs/DocsAnnotationAPIvs1.fs | 2 +- src/Server/Docs/DocsFunctions.fs | 12 ++ src/Server/OntologyDB.fs | 108 ++++++++++++++++++ src/Server/Server.fs | 22 +++- src/Shared/Shared.fs | 21 +++- 15 files changed, 343 insertions(+), 104 deletions(-) diff --git a/src/Client/CustomComponents/AutocompleteSearch.fs b/src/Client/CustomComponents/AutocompleteSearch.fs index adc3b581..ffe2d05d 100644 --- a/src/Client/CustomComponents/AutocompleteSearch.fs +++ b/src/Client/CustomComponents/AutocompleteSearch.fs @@ -91,10 +91,10 @@ with AdvancedSearchLinkText = "Can't find the unit you are looking for?" OnInputChangeMsg = (fun str -> SearchUnitTermTextChange (str, Unit1) |> AddBuildingBlock) - OnSuggestionSelect = (fun sugg -> (sugg.Name, Unit1) |> UnitTermSuggestionUsed |> AddBuildingBlock) + OnSuggestionSelect = (fun sugg -> (sugg, Unit1) |> UnitTermSuggestionUsed |> AddBuildingBlock) HasAdvancedSearch = true - OnAdvancedSearch = (fun sugg -> (sugg.Name, Unit1) |> UnitTermSuggestionUsed |> AddBuildingBlock) + OnAdvancedSearch = (fun sugg -> (sugg, Unit1) |> UnitTermSuggestionUsed |> AddBuildingBlock) } static member ofAddBuildingBlockUnit2State (state:AddBuildingBlockState) : AutocompleteParameters = { @@ -109,10 +109,10 @@ with AdvancedSearchLinkText = "Can't find the unit you are looking for?" OnInputChangeMsg = (fun str -> SearchUnitTermTextChange (str,Unit2) |> AddBuildingBlock) - OnSuggestionSelect = (fun sugg -> (sugg.Name, Unit2) |> UnitTermSuggestionUsed |> AddBuildingBlock) + OnSuggestionSelect = (fun sugg -> (sugg, Unit2) |> UnitTermSuggestionUsed |> AddBuildingBlock) HasAdvancedSearch = true - OnAdvancedSearch = (fun sugg -> (sugg.Name, Unit2) |> UnitTermSuggestionUsed |> AddBuildingBlock) + OnAdvancedSearch = (fun sugg -> (sugg, Unit2) |> UnitTermSuggestionUsed |> AddBuildingBlock) } static member ofAddBuildingBlockState (state:AddBuildingBlockState) : AutocompleteParameters = { @@ -126,12 +126,11 @@ with DropDownIsLoading = state.HasBuildingBlockTermSuggestionsLoading OnInputChangeMsg = (BuildingBlockNameChange >> AddBuildingBlock) - OnSuggestionSelect = (fun sugg -> sugg.Name |> BuildingBlockNameSuggestionUsed |> AddBuildingBlock) + OnSuggestionSelect = (fun sugg -> sugg |> BuildingBlockNameSuggestionUsed |> AddBuildingBlock) HasAdvancedSearch = true AdvancedSearchLinkText = "Cant find the Term you are looking for?" - OnAdvancedSearch = (fun sugg -> sugg.Name |> BuildingBlockNameSuggestionUsed |> AddBuildingBlock - ) + OnAdvancedSearch = (fun sugg -> sugg |> BuildingBlockNameSuggestionUsed |> AddBuildingBlock) } @@ -284,7 +283,9 @@ let autocompleteTermSearchComponentOfParentOntology match inputSize with | Some size -> Button.Size size | _ -> () - ] [str (sprintf "%A" model.TermSearchState.ParentOntology.Value)] + ] [str ( + sprintf "%A" (if model.TermSearchState.ParentOntology.IsSome then model.TermSearchState.ParentOntology.Value.Name else "") + )] ] Control.div [Control.IsExpanded] [ diff --git a/src/Client/Messages.fs b/src/Client/Messages.fs index 6022f339..ccafee20 100644 --- a/src/Client/Messages.fs +++ b/src/Client/Messages.fs @@ -20,8 +20,8 @@ type ExcelInteropMsg = | SyncContext of activeAnnotationTable:TryFindAnnoTableResult*string | InSync of string | FillSelection of activeAnnotationTable:TryFindAnnoTableResult * string * (DbDomain.Term option) - | AddAnnotationBlock of activeAnnotationTable:TryFindAnnoTableResult * colname:string * format:string option - | AddUnitToAnnotationBlock of tryFindActiveAnnotationTable:TryFindAnnoTableResult * format:string option + | AddAnnotationBlock of activeAnnotationTable:TryFindAnnoTableResult * colname:string * colTermOpt:DbDomain.Term option * unitNameOpt:string option * unitTermOpt:DbDomain.Term option + | AddUnitToAnnotationBlock of tryFindActiveAnnotationTable:TryFindAnnoTableResult * format:string option * unitTermOpt:DbDomain.Term option | FormatColumn of activeAnnotationTable:TryFindAnnoTableResult * colname:string * formatString:string * prevmsg:string /// This message does not need the active annotation table as `PipeCreateAnnotationTableInfo` checks if any annotationtables exist in the active worksheet, and if so, errors. | CreateAnnotationTable of allTableNames:string [] * isDark:bool @@ -76,7 +76,7 @@ type DevMsg = type ApiRequestMsg = | TestOntologyInsert of (string*string*string*System.DateTime*string) | GetNewTermSuggestions of string - | GetNewTermSuggestionsByParentTerm of string*string + | GetNewTermSuggestionsByParentTerm of string*OntologyInfo | GetNewBuildingBlockNameSuggestions of string | GetNewUnitTermSuggestions of string*relatedUnitSearch:UnitSearchRequest | GetNewAdvancedTermSearchResults of AdvancedTermSearchOptions @@ -122,11 +122,11 @@ type AddBuildingBlockMsg = | BuildingBlockNameChange of string | ToggleSelectionDropdown - | BuildingBlockNameSuggestionUsed of string + | BuildingBlockNameSuggestionUsed of DbDomain.Term | NewBuildingBlockNameSuggestions of DbDomain.Term [] | SearchUnitTermTextChange of searchString:string * relatedUnitSearch:UnitSearchRequest - | UnitTermSuggestionUsed of unitName:string * relatedUnitSearch:UnitSearchRequest + | UnitTermSuggestionUsed of unitTerm:DbDomain.Term* relatedUnitSearch:UnitSearchRequest | NewUnitTermSuggestions of DbDomain.Term [] * relatedUnitSearch:UnitSearchRequest | ToggleBuildingBlockHasUnit @@ -190,5 +190,14 @@ let pipeNameTuple3 msg param = let constructParam = param |> fun (x,y,z) -> annotationTableOpt,x,y,z msg (constructParam) - |> PipeActiveAnnotationTable + ) + +/// This function is used to easily pipe a message into `PipeActiveAnnotationTable`. This is designed for a message with (x1,x2,x3,x4) other params. +/// Use this as: (x1,x2,x3) |> pipeNameTuple4 msg +let pipeNameTuple4 msg param = + PipeActiveAnnotationTable + (fun annotationTableOpt -> + let constructParam = + param |> fun (x,y,z,u) -> annotationTableOpt,x,y,z,u + msg (constructParam) ) \ No newline at end of file diff --git a/src/Client/Model.fs b/src/Client/Model.fs index ad65eb76..253954fb 100644 --- a/src/Client/Model.fs +++ b/src/Client/Model.fs @@ -64,7 +64,7 @@ type TermSearchState = { TermSearchText : string SelectedTerm : DbDomain.Term option TermSuggestions : DbDomain.Term [] - ParentOntology : string option + ParentOntology : OntologyInfo option SearchByParentOntology : bool HasSuggestionsLoading : bool ShowSuggestions : bool @@ -294,6 +294,7 @@ type AnnotationBuildingBlock = { type AddBuildingBlockState = { CurrentBuildingBlock : AnnotationBuildingBlock + BuildingBlockSelectedTerm : DbDomain.Term option BuildingBlockNameSuggestions : DbDomain.Term [] ShowBuildingBlockSelection : bool BuildingBlockHasUnit : bool @@ -302,22 +303,24 @@ type AddBuildingBlockState = { /// This section is used to add a unit directly to a freshly created building block. UnitTermSearchText : string + UnitSelectedTerm : DbDomain.Term option UnitTermSuggestions : DbDomain.Term [] HasUnitTermSuggestionsLoading : bool ShowUnitTermSuggestions : bool /// This section is used to add a unit directly to an already existing building block - Unit2TermSearchText : string - Unit2TermSuggestions : DbDomain.Term [] - HasUnit2TermSuggestionsLoading : bool - ShowUnit2TermSuggestions : bool + Unit2TermSearchText : string + Unit2SelectedTerm : DbDomain.Term option + Unit2TermSuggestions : DbDomain.Term [] + HasUnit2TermSuggestionsLoading : bool + ShowUnit2TermSuggestions : bool } with static member init () = { ShowBuildingBlockSelection = false CurrentBuildingBlock = AnnotationBuildingBlock.init AnnotationBuildingBlockType.Parameter - + BuildingBlockSelectedTerm = None BuildingBlockNameSuggestions = [||] ShowBuildingBlockTermSuggestions = false HasBuildingBlockTermSuggestionsLoading = false @@ -325,15 +328,17 @@ type AddBuildingBlockState = { /// This section is used to add a unit directly to a freshly created building block. UnitTermSearchText = "" + UnitSelectedTerm = None UnitTermSuggestions = [||] ShowUnitTermSuggestions = false HasUnitTermSuggestionsLoading = false /// This section is used to add a unit directly to an already existing building block - Unit2TermSearchText = "" - Unit2TermSuggestions = [||] - ShowUnit2TermSuggestions = false - HasUnit2TermSuggestionsLoading = false + Unit2TermSearchText = "" + Unit2SelectedTerm = None + Unit2TermSuggestions = [||] + ShowUnit2TermSuggestions = false + HasUnit2TermSuggestionsLoading = false } /// Validation scheme for Table diff --git a/src/Client/OfficeInterop/HelperFunctions.fs b/src/Client/OfficeInterop/HelperFunctions.fs index 22b616f7..469c732b 100644 --- a/src/Client/OfficeInterop/HelperFunctions.fs +++ b/src/Client/OfficeInterop/HelperFunctions.fs @@ -10,6 +10,7 @@ open System.Text.RegularExpressions open OfficeInterop.Regex open OfficeInterop.Types open BuildingBlockTypes +open Shared let createEmptyMatrixForTables (colCount:int) (rowCount:int) value = @@ -23,14 +24,19 @@ let createEmptyMatrixForTables (colCount:int) (rowCount:int) value = /// This will create the column header attributes for a unit block. /// as unit always has to be a term and cannot be for example "Source" or "Sample", both of which have a differen format than for exmaple "Parameter [TermName]", /// we only need one function to generate id and attributes and bring the unit term in the right format. -let unitColAttributes (unitTermName:string) (id:int) = +let unitColAttributes (unitTermName:string) (unitTermOpt:DbDomain.Term option) (id:int) = match id with | 1 -> - sprintf "[%s] (#h; #u)" unitTermName + match unitTermOpt with + | Some t -> sprintf "[%s] (#h; #t%s; #u)" unitTermName t.Accession + | None -> sprintf "[%s] (#h; #u)" unitTermName | _ -> - sprintf "[%s] (#%i; #h; #u)" unitTermName id + match unitTermOpt with + | Some t -> sprintf "[%s] (#%i; #h; #t%s #u)" unitTermName id t.Accession + | None -> sprintf "[%s] (#%i; #h; #u)" unitTermName id -let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseColIndex rowCount (format:string option) = + +let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseColIndex rowCount (format:string option) (unitTermOpt:DbDomain.Term option) = let col = createEmptyMatrixForTables 1 rowCount "" if format.IsSome then let findNewIdForUnit() = @@ -40,7 +46,7 @@ let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseC // Should a column with the same name already exist, then count up the id tag. |> Array.exists (fun existingHeader -> // We don't need to check TSR or TAN, because the main column always starts with "Unit" - existingHeader = sprintf "Unit %s" (unitColAttributes format.Value int) + existingHeader = sprintf "Unit %s" (unitColAttributes format.Value unitTermOpt int) ) if isExisting then loopingCheck (int+1) @@ -55,7 +61,7 @@ let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseC annotationTable.columns.add( index = newBaseColIndex+3., values = U4.Case1 col, - name = sprintf "Unit %s" (unitColAttributes format.Value newUnitId) + name = sprintf "Unit %s" (unitColAttributes format.Value unitTermOpt newUnitId) ) /// create unit TSR @@ -63,7 +69,7 @@ let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseC annotationTable.columns.add( index = newBaseColIndex+4., values = U4.Case1 col, - name = sprintf "Term Source REF %s" (unitColAttributes format.Value newUnitId) + name = sprintf "Term Source REF %s" (unitColAttributes format.Value unitTermOpt newUnitId) ) /// create unit TAN @@ -71,7 +77,7 @@ let createUnitColumns (allColHeaders:string []) (annotationTable:Table) newBaseC annotationTable.columns.add( index = newBaseColIndex+5., values = U4.Case1 col, - name = sprintf "Term Accession Number %s" (unitColAttributes format.Value newUnitId) + name = sprintf "Term Accession Number %s" (unitColAttributes format.Value unitTermOpt newUnitId) ) Some ( @@ -229,7 +235,7 @@ module BuildingBlockTypes = // Build in fail safe. errorMsg2 nextCol currentBlock - // Building blocks are defined by one visuable column and an undefined number of hidden columns. + // Building blocks are defined by one visable column and an undefined number of hidden columns. // Therefore we iterate through the columns array and use every column without an `#h` tag as the start of a new building block. let rec sortColsIntoBuildingBlocks (index:int) (currentBlock:BuildingBlock option) (buildingBlockList:BuildingBlock list) = // Exit case if we iterated through all columns @@ -282,11 +288,47 @@ module BuildingBlockTypes = failwith (sprintf "The tag array of the next column to process in 'sortColsIntoBuildingBlocks' was not recognized as hidden or main column: %A." nextCol.Header) /// Sort all columns into building blocks. - let buildingBlocks = + let buildingBlocksPre = sortColsIntoBuildingBlocks 0 None [] |> List.rev |> Array.ofList + // UPDATE IN > 0.2.0 + /// As we now add the TermAccession as "#txxx" tag in the reference columns we walk over all buildingBlock and update the maincolumn header accordingly. + let buildingBlocks = + buildingBlocksPre + |> Array.map (fun buildingBlock -> + match buildingBlock.TAN, buildingBlock.TSR with + | Some tan, Some tsr -> + match tan.Header.Value.Ontology, tsr.Header.Value.Ontology with + | Some ont1, Some ont2 -> + let isSame = ont1.TermAccession = ont2.TermAccession + if isSame |> not then + failwith (sprintf "During BuildingBlock update with TermAccession found BuildingBlock (%s) with unknow TAN TSR pattern. (3)" buildingBlock.MainColumn.Header.Value.Header) + if ont1.TermAccession <> "" then + let nextMainColumn = { + buildingBlock.MainColumn with + Header = { + buildingBlock.MainColumn.Header.Value with + Ontology = { + buildingBlock.MainColumn.Header.Value.Ontology.Value with + TermAccession = ont1.TermAccession + } |> Some + } |> Some + } + { buildingBlock with MainColumn = nextMainColumn } + else + buildingBlock + | None, None -> + buildingBlock + | _,_ -> + failwith (sprintf "During BuildingBlock update with TermAccession found BuildingBlock (%s) with unknow TAN TSR pattern. (2)" buildingBlock.MainColumn.Header.Value.Header) + | None, None -> + buildingBlock + | _, _ -> + failwith (sprintf "During BuildingBlock update with TermAccession found BuildingBlock (%s) with unknow TAN TSR pattern." buildingBlock.MainColumn.Header.Value.Header) + ) + buildingBlocks open System diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index dda3396b..2159b86c 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -78,7 +78,7 @@ let exampleExcelFunction () = //let! format = createGroupHeaderFormatForRange groupHeader context let! xmlParsed, xml = getCurrentCustomXml customXmlParts context let! buildingBlocks = context.sync().``then``( fun e -> getBuildingBlocks annoHeaderRange annoBodyRange ) - return (sprintf "%A" xmlParsed) + return (sprintf "%A" buildingBlocks) } ) @@ -391,7 +391,7 @@ let getTableRepresentation(annotationTable) = ) /// This function is used to add a new building block to the active annotationTable. -let addAnnotationBlock (annotationTable,colName:string,format:string option) = +let addAnnotationBlock (annotationTable,colName:string,colTermOpt:DbDomain.Term option,format:string option, unitTermOpt:DbDomain.Term option) = /// The following cols are currently always singles (cannot have TSR, TAN, unit cols). For easier refactoring these names are saved in OfficeInterop.Types. let isSingleCol = @@ -410,17 +410,21 @@ let addAnnotationBlock (annotationTable,colName:string,format:string option) = /// This is used to create the bracket information for reference (hidden) columns. Again this has two modi, one with id tag and one without. /// This time no core name is needed as this will always be TSR or TAN. - let hiddenColAttributes (parsedColHeader:ColHeader) (id:int) = + let hiddenColAttributes (parsedColHeader:ColHeader) (columnTermOption: DbDomain.Term option) (id:int) = let coreName = match parsedColHeader.Ontology, parsedColHeader.CoreName with - | Some o , _ -> o + | Some o , _ -> o.Name | None, Some cn -> cn | _ -> parsedColHeader.Header match id with | 1 -> - (sprintf "[%s] (#h)" coreName) + match columnTermOption with + | Some t -> sprintf "[%s] (#h; #t%s)" coreName t.Accession + | None -> sprintf "[%s] (#h)" coreName | _ -> - (sprintf "[%s] (#%i; #h)" coreName id) + match columnTermOption with + | Some t -> sprintf "[%s] (#%i; #h; #t%s)" coreName id t.Accession + | None -> sprintf "[%s] (#%i; #h)" coreName id Excel.run(fun context -> let sheet = context.workbook.worksheets.getActiveWorksheet() @@ -518,8 +522,8 @@ let addAnnotationBlock (annotationTable,colName:string,format:string option) = // Parameter [instrument model] | "Term Source REF [instrument model] (#h) | ... // Factor [instrument model] | "Term Source REF [instrument model] (#h) | ... // in the example above the mainColumn name is different but "TSR" and "TAN" would be the same. - || existingHeader = sprintf "Term Source REF %s" (hiddenColAttributes parsedBaseHeader int) - || existingHeader = sprintf "Term Accession Number %s" (hiddenColAttributes parsedBaseHeader int) + || existingHeader = sprintf "Term Source REF %s" (hiddenColAttributes parsedBaseHeader colTermOpt int) + || existingHeader = sprintf "Term Accession Number %s" (hiddenColAttributes parsedBaseHeader colTermOpt int) ) if isExisting then loopingCheck (int+1) @@ -548,7 +552,7 @@ let addAnnotationBlock (annotationTable,colName:string,format:string option) = annotationTable.columns.add( index = newBaseColIndex+1., values = U4.Case1 col, - name = sprintf "Term Source REF %s" (hiddenColAttributes parsedBaseHeader newId) + name = sprintf "Term Source REF %s" (hiddenColAttributes parsedBaseHeader colTermOpt newId) ) // create TAN @@ -556,7 +560,7 @@ let addAnnotationBlock (annotationTable,colName:string,format:string option) = annotationTable.columns.add( index = newBaseColIndex+2., values = U4.Case1 col, - name = sprintf "Term Accession Number %s" (hiddenColAttributes parsedBaseHeader newId) + name = sprintf "Term Accession Number %s" (hiddenColAttributes parsedBaseHeader colTermOpt newId) ) // Should the column be Data, Source or Sample then we do not add TSR and TAN @@ -573,7 +577,7 @@ let addAnnotationBlock (annotationTable,colName:string,format:string option) = /// if format.isSome then we need to also add unit columns in the following scheme: /// Unit [UnitTermName] (#id; #h; #u) | Term Source REF [UnitTermName] (#id; #h; #u) | Term Accession Number [UnitTermName] (#id; #h; #u) let createUnitColsIfNeeded = - OfficeInterop.HelperFunctions.createUnitColumns allColHeaders annotationTable newBaseColIndex rowCount format + OfficeInterop.HelperFunctions.createUnitColumns allColHeaders annotationTable newBaseColIndex rowCount format unitTermOpt /// If unit block was added then return some msg information let unitColCreationMsg = if createUnitColsIfNeeded.IsSome then fst createUnitColsIfNeeded.Value else "" @@ -668,7 +672,14 @@ let getParentTerm (annotationTable) = None else // is selected range is in table then take header value from selected column - tableRange.values.[0].[newColIndex] + let header = tableRange.values.[0].[newColIndex] + let parsedHeader = parseColHeader (string header.Value) + /// as the reference columns also contain a accession tag we want to return the first reference column header + /// instead of the main column header, if the main column header does include an ontology + if parsedHeader.Ontology.IsSome then + tableRange.values.[0].[newColIndex+1] + else + None // return header of selected col value ) @@ -777,19 +788,20 @@ let createSearchTermsIFromTable (annotationTable') = // create SearchTermI types that will be passed to the server to get filled with a term option. |> Array.map (fun (searchStr,cellArr) -> let rowIndices = cellArr |> Array.map (fun cell -> cell.Index) - Shared.SearchTermI.create tsrTanColIndices searchStr rowIndices + Shared.SearchTermI.create tsrTanColIndices searchStr "" bBlock.MainColumn.Header.Value.Ontology rowIndices ) /// We differentiate between building blocks with and without unit as unit building blocks will not contain terms as values but e.g. numbers. /// In this case we do not want to search the database for the cell values but the parent ontology in the header. /// This will then be used for TSR and TAN. let fillTermConstructsWithUnit (bBlock:BuildingBlock) = - let searchStr = bBlock.MainColumn.Header.Value.Ontology.Value + let searchStr = bBlock.MainColumn.Header.Value.Ontology.Value.Name + let termAccession = bBlock.MainColumn.Header.Value.Ontology.Value.TermAccession let rowIndices = bBlock.MainColumn.Cells |> Array.map (fun x -> x.Index ) - [|Shared.SearchTermI.create tsrTanColIndices searchStr rowIndices|] + [|Shared.SearchTermI.create tsrTanColIndices searchStr termAccession None rowIndices|] if bBlock.Unit.IsSome then fillTermConstructsWithUnit bBlock else @@ -803,10 +815,11 @@ let createSearchTermsIFromTable (annotationTable') = |> Array.map ( fun bBlock -> let unit = bBlock.Unit.Value - let searchString = unit.MainColumn.Header.Value.Ontology.Value + let searchString = unit.MainColumn.Header.Value.Ontology.Value.Name + let termAccession = unit.MainColumn.Header.Value.Ontology.Value.TermAccession let colIndices = [|unit.MainColumn.Index; unit.TSR.Value.Index; unit.TAN.Value.Index|] let rowIndices = unit.MainColumn.Cells |> Array.map (fun x -> x.Index) - Shared.SearchTermI.create colIndices searchString rowIndices + Shared.SearchTermI.create colIndices searchString termAccession None rowIndices ) /// Combine search types @@ -1176,7 +1189,7 @@ let writeTableValidationToXml(tableValidation:ValidationTypes.TableValidation,cu ) /// This function is used to add unit reference columns to an existing building block without unit reference columns -let addUnitToExistingBuildingBlock (annotationTable:string,format:string option) = +let addUnitToExistingBuildingBlock (annotationTable:string,format:string option,unitTermOpt:DbDomain.Term option) = Excel.run(fun context -> let annotationTableName = annotationTable @@ -1250,7 +1263,7 @@ let addUnitToExistingBuildingBlock (annotationTable:string,format:string option) |> Array.map string let unitColumnResult = - createUnitColumns allColHeaders annotationTable (float findLeftClosestBuildingBlock.MainColumn.Index) (int tableRange.rowCount) format + createUnitColumns allColHeaders annotationTable (float findLeftClosestBuildingBlock.MainColumn.Index) (int tableRange.rowCount) format unitTermOpt let maincolName = findLeftClosestBuildingBlock.MainColumn.Header.Value.Header diff --git a/src/Client/OfficeInterop/Regex.fs b/src/Client/OfficeInterop/Regex.fs index 92859165..0150d3f1 100644 --- a/src/Client/OfficeInterop/Regex.fs +++ b/src/Client/OfficeInterop/Regex.fs @@ -16,9 +16,8 @@ let BracketsPattern = "\([^\]]*\)" [] let CoreNamePattern = "^[^[(]*" -// currently unused [] -let UnitAccessionPattern = "#u.+?:\d+" +let TermAccessionPattern = "#t.+?:\d+" // currently unused [] @@ -54,26 +53,34 @@ let parseCoreName (headerStr:string) = | _ -> None -// currently unused -let parseUnitAccession (tag:string) = +let parseTermAccession (tag:string) = match tag with - | Shared.HelperFunctions.Regex UnitAccessionPattern value -> + | Shared.HelperFunctions.Regex TermAccessionPattern value -> value.Trim() |> Some | _ -> None -// currently unused -let parseGroup (tag:string) = - match tag with - | Shared.HelperFunctions.Regex GroupPattern value -> - value.Trim().Replace("#g","") |> Some - | _ -> None +open Shared let parseColHeader (headerStr:string) = let coreName = parseCoreName headerStr - let ontology = parseSquaredBrackets headerStr let tagArr = parseBrackets headerStr + let ontology = + let hasOnt = parseSquaredBrackets headerStr + let termAccession = + match tagArr with + | None -> None + | Some ta -> + let hasAccession = ta |> Array.tryFind (fun x -> x.StartsWith ColumnTags.TermAccessionTag) + if hasAccession.IsSome && (parseTermAccession hasAccession.Value).IsSome + then hasAccession.Value.Replace(Types.ColumnTags.TermAccessionTag,"") |> Some + else None + match hasOnt,termAccession with + | Some ontName, None -> OntologyInfo.create ontName "" |> Some + | Some ontName, Some ta -> OntologyInfo.create ontName ta |> Some + | _,_ -> None + let isUnit = match tagArr with | None -> false diff --git a/src/Client/OfficeInterop/Types.fs b/src/Client/OfficeInterop/Types.fs index b5d3cdcb..e4c81f9c 100644 --- a/src/Client/OfficeInterop/Types.fs +++ b/src/Client/OfficeInterop/Types.fs @@ -62,10 +62,15 @@ module ColumnTags = [] let UnitTag = "#u" - /// This can have additional information afterwards so it needs to be parsed as 'StartsWith' + /// This has additional information afterwards so it needs to be parsed as 'StartsWith' + /// Not used [] let GroupTag = "#g" + /// This has additional information afterwards so it needs to be parsed as 'StartsWith' + [] + let TermAccessionTag = "#t" + open System open Fable.SimpleXml open Fable.SimpleXml.Generator @@ -342,10 +347,12 @@ type TryFindAnnoTableResult = | _ -> Error "Could not process message. Swate was not able to identify the given annotation tables with a known case." +open Shared + type ColHeader = { Header: string CoreName: string option - Ontology: string option + Ontology: OntologyInfo option TagArr: string [] option IsUnitCol: bool } @@ -398,7 +405,7 @@ module BuildingBlockTypes = ColumnAdress = this.MainColumn.Index |> Some Importance = None ValidationFormat = None - Unit = if this.Unit.IsSome then this.Unit.Value.MainColumn.Header.Value.Ontology else None + Unit = if this.Unit.IsSome then this.Unit.Value.MainColumn.Header.Value.Ontology.Value.Name |> Some else None } diff --git a/src/Client/Update.fs b/src/Client/Update.fs index e9e88c6b..f1159184 100644 --- a/src/Client/Update.fs +++ b/src/Client/Update.fs @@ -154,11 +154,11 @@ let handleExcelInteropMsg (excelInteropMsg: ExcelInteropMsg) (currentState:Excel let cmd = matchActiveTableResToMsg activeTableNameRes cmd currentState, cmd - | AddAnnotationBlock (activeTableNameRes,colName,format) -> - let cmd name= + | AddAnnotationBlock (activeTableNameRes,colName,colTermOpt,unitOpt,unitTermOpt) -> + let cmd tableName = Cmd.OfPromise.either OfficeInterop.addAnnotationBlock - (name,colName,format) + (tableName,colName,colTermOpt,unitOpt,unitTermOpt) (fun (newColName,format,msg) -> FormatColumn (activeTableNameRes,newColName,format,msg) |> ExcelInterop ) @@ -166,11 +166,11 @@ let handleExcelInteropMsg (excelInteropMsg: ExcelInteropMsg) (currentState:Excel let cmd = matchActiveTableResToMsg activeTableNameRes cmd currentState, cmd - | AddUnitToAnnotationBlock (activeTableNameRes, format) -> + | AddUnitToAnnotationBlock (activeTableNameRes, format, unitTermOpt) -> let cmd name = Cmd.OfPromise.either OfficeInterop.addUnitToExistingBuildingBlock - (name,format) + (name,format,unitTermOpt) (fun (newColName,format,msg) -> FormatColumn (activeTableNameRes, newColName, format, msg) |> ExcelInterop ) @@ -611,7 +611,7 @@ let handleApiRequestMsg (reqMsg: ApiRequestMsg) (currentState: ApiState) : ApiSt nextState,nextCmd - let handleTermSuggestionByParentTermRequest (apiFunctionname:string) (responseHandler: DbDomain.Term [] -> ApiMsg) queryString parentOntology = + let handleTermSuggestionByParentTermRequest (apiFunctionname:string) (responseHandler: DbDomain.Term [] -> ApiMsg) queryString (parentOntology:OntologyInfo) = let currentCall = { FunctionName = apiFunctionname Status = Pending @@ -1049,12 +1049,14 @@ let handleAddBuildingBlockMsg (addBuildingBlockMsg:AddBuildingBlockMsg) (current | Unit1 -> { currentState with UnitTermSearchText = newTerm + UnitSelectedTerm = None ShowUnitTermSuggestions = triggerNewSearch HasUnitTermSuggestionsLoading = true } | Unit2 -> { currentState with Unit2TermSearchText = newTerm + Unit2SelectedTerm = None ShowUnit2TermSuggestions = triggerNewSearch HasUnit2TermSuggestionsLoading = true } @@ -1080,23 +1082,23 @@ let handleAddBuildingBlockMsg (addBuildingBlockMsg:AddBuildingBlockMsg) (current nextState,Cmd.none - | UnitTermSuggestionUsed (suggestionName, relUnit) -> + | UnitTermSuggestionUsed (suggestion, relUnit) -> let nextState = match relUnit with | Unit1 -> { currentState with - UnitTermSearchText = suggestionName - //UnitTerm = Some suggestion + UnitTermSearchText = suggestion.Name + UnitSelectedTerm = Some suggestion ShowUnitTermSuggestions = false HasUnitTermSuggestionsLoading = false } | Unit2 -> { currentState with - Unit2TermSearchText = suggestionName - //UnitTerm = Some suggestion - ShowUnit2TermSuggestions = false - HasUnit2TermSuggestionsLoading = false + Unit2TermSearchText = suggestion.Name + Unit2SelectedTerm = Some suggestion + ShowUnit2TermSuggestions = false + HasUnit2TermSuggestionsLoading = false } nextState, Cmd.none @@ -1123,6 +1125,7 @@ let handleAddBuildingBlockMsg (addBuildingBlockMsg:AddBuildingBlockMsg) (current let nextState = { currentState with CurrentBuildingBlock = nextBB + BuildingBlockSelectedTerm = None ShowBuildingBlockTermSuggestions = triggerNewSearch HasBuildingBlockTermSuggestionsLoading = true } @@ -1140,16 +1143,18 @@ let handleAddBuildingBlockMsg (addBuildingBlockMsg:AddBuildingBlockMsg) (current nextState,Cmd.none - | BuildingBlockNameSuggestionUsed nameSuggestion -> + | BuildingBlockNameSuggestionUsed suggestion -> let nextBB = { currentState.CurrentBuildingBlock with - Name = nameSuggestion + Name = suggestion.Name } let nextState = { currentState with CurrentBuildingBlock = nextBB + + BuildingBlockSelectedTerm = Some suggestion ShowBuildingBlockTermSuggestions = false HasBuildingBlockTermSuggestionsLoading = false } diff --git a/src/Client/Views/AddBuildingBlockView.fs b/src/Client/Views/AddBuildingBlockView.fs index ff0cc49b..3e56002e 100644 --- a/src/Client/Views/AddBuildingBlockView.fs +++ b/src/Client/Views/AddBuildingBlockView.fs @@ -100,7 +100,7 @@ let addBuildingBlockElements (model:Model) (dispatch:Msg -> unit) = Control.div [] [ Dropdown.dropdown [Dropdown.IsActive model.AddBuildingBlockState.ShowBuildingBlockSelection] [ Dropdown.trigger [] [ - Button.button [Button.OnClick (fun _ -> ToggleSelectionDropdown |> AddBuildingBlock |> dispatch)] [ + Button.a [Button.OnClick (fun _ -> ToggleSelectionDropdown |> AddBuildingBlock |> dispatch)] [ span [Style [MarginRight "5px"]] [model.AddBuildingBlockState.CurrentBuildingBlock.Type |> AnnotationBuildingBlockType.toString |> str] Fa.i [Fa.Solid.AngleDown] [] ] @@ -118,9 +118,8 @@ let addBuildingBlockElements (model:Model) (dispatch:Msg -> unit) = List.append x [ Dropdown.Item.div [ Dropdown.Item.Modifiers [Modifier.TextAlignment (Screen.All,TextAlignment.Right)] - Dropdown.Item.Props [ Href "https://nfdi4plants.github.io/AnnotationPrinciples/"; Target "_Blank" ] ][ - a [][ + a [ Href Shared.URLs.AnnotationPrinciplesUrl; Target "_Blank" ] [ str "more" ] ] @@ -164,11 +163,6 @@ let addBuildingBlockElements (model:Model) (dispatch:Msg -> unit) = Fa.i [ Fa.Size Fa.FaLarge; Fa.Solid.Check ][ ] else Fa.i [ Fa.Size Fa.FaLarge ; Fa.Solid.Ban ][ ] - //Fa.stack [Fa.Stack.Size Fa.FaSmall; Fa.Stack.Props [Style [ Color "#666666"]]][ - // Fa.i [Fa.Regular.Square; Fa.Stack2x][] - // if model.AddBuildingBlockState.BuildingBlockHasUnit then - // Fa.i [Fa.Solid.Check; Fa.Stack1x][] - //] ] ] Control.p [] [ @@ -219,14 +213,16 @@ let addBuildingBlockElements (model:Model) (dispatch:Msg -> unit) = Button.Props [Disabled true] Button.IsFullWidth Button.OnClick ( - let format = + let unitName = match model.AddBuildingBlockState.BuildingBlockHasUnit, model.AddBuildingBlockState.UnitTermSearchText with | _,"" -> None //"0.00" | false, _ -> None//"0.00" | true, str -> Some str //sprintf "0.00 \"%s\"" str - let colName = model.AddBuildingBlockState.CurrentBuildingBlock |> AnnotationBuildingBlock.toAnnotationTableHeader - fun _ -> (colName,format) |> pipeNameTuple2 AddAnnotationBlock |> ExcelInterop |> dispatch + let colName = model.AddBuildingBlockState.CurrentBuildingBlock |> AnnotationBuildingBlock.toAnnotationTableHeader + let colTerm = model.AddBuildingBlockState.BuildingBlockSelectedTerm + let unitTerm = model.AddBuildingBlockState.UnitSelectedTerm + fun _ -> (colName,colTerm,unitName,unitTerm) |> pipeNameTuple4 AddAnnotationBlock |> ExcelInterop |> dispatch ) ] [ str "Insert this annotation building block" @@ -276,11 +272,12 @@ let addUnitToExistingBlockElements (model:Model) (dispatch:Msg -> unit) = Button.Props [Disabled true] Button.IsFullWidth Button.OnClick (fun e -> + let unitTermOpt = model.AddBuildingBlockState.Unit2SelectedTerm match model.AddBuildingBlockState.Unit2TermSearchText with | "" -> GenericLog ("Error", "Cannot execute function with empty unit input") |> Dev |> dispatch | str -> - pipeNameTuple AddUnitToAnnotationBlock (Some str) |> ExcelInterop |> dispatch + (Some str, unitTermOpt) |> pipeNameTuple2 AddUnitToAnnotationBlock |> ExcelInterop |> dispatch ) ] [ str "Add unit to existing building block" @@ -305,4 +302,5 @@ let addBuildingBlockComponent (model:Model) (dispatch:Msg -> unit) = Label.label [Label.Size Size.IsSmall; Label.Props [Style [Color model.SiteStyleState.ColorMode.Accent]]] [str "Add unit reference columns to existing building block."] // Input forms, etc related to add unit to existing building block. addUnitToExistingBlockElements model dispatch + ] \ No newline at end of file diff --git a/src/Client/Views/ValidationView.fs b/src/Client/Views/ValidationView.fs index b881d573..8a61d69e 100644 --- a/src/Client/Views/ValidationView.fs +++ b/src/Client/Views/ValidationView.fs @@ -9,6 +9,8 @@ open Browser open Browser.MediaQueryList open Browser.MediaQueryListExtensions +open Shared + open ExcelColors open Model open Messages @@ -138,8 +140,7 @@ let checkradioList (ind:int) colVal model dispatch = if colVal.Unit.IsSome then ContentType.UnitTerm colVal.Unit.Value |> Some else ContentType.UnitTerm "None" |> Some let ontologyContent = - if hasOntology.IsSome then ContentType.OntologyTerm hasOntology.Value |> Some else ContentType.OntologyTerm "None" |> Some - + if hasOntology.IsSome then ContentType.OntologyTerm hasOntology.Value.Name |> Some else ContentType.OntologyTerm "None" |> Some [ checkradioElement ind None colVal model dispatch diff --git a/src/Server/Docs/DocsAnnotationAPIvs1.fs b/src/Server/Docs/DocsAnnotationAPIvs1.fs index e60becbc..1ec3a2b4 100644 --- a/src/Server/Docs/DocsAnnotationAPIvs1.fs +++ b/src/Server/Docs/DocsAnnotationAPIvs1.fs @@ -121,7 +121,7 @@ let annotatorApiDocsv1 = "This function returns an array of matching Database.Term entries in the form of DbDomain.Term []." (Parameter.create "Term []" (PredefinedParams.TermType |> ParamArray) "Array of database Term entries.") ) - |> annotatorDocsv1.example <@ fun api -> api.getTermSuggestionsByParentTerm (5,"micrOTOF-Q","instrument model") @> + |> annotatorDocsv1.example <@ fun api -> api.getTermSuggestionsByParentTerm (5,"micrOTOF-Q",PredefinedParams.Examples.ontologyInfoExmp) @> //////// annotatorDocsv1.route <@ fun api -> api.getTermsForAdvancedSearch @> diff --git a/src/Server/Docs/DocsFunctions.fs b/src/Server/Docs/DocsFunctions.fs index c6105825..893cef47 100644 --- a/src/Server/Docs/DocsFunctions.fs +++ b/src/Server/Docs/DocsFunctions.fs @@ -108,10 +108,18 @@ module PredefinedParams = dbdomaniOntologyParamArr |> ParamRecordType + let OntologyInfoType = + [| + Parameter.create "Name" ParamString "The ontology term name" + Parameter.create "TermAccession" ParamString "The ontology term accession value" + |] |> ParamRecordType + let SearchTermType = let insertTermParamArr = [| Parameter.create "ColIndices" (ParamArray ParamInteger) "" Parameter.create "SearchString" (ParamString) "" + Parameter.create "TermAccession" (ParamString) "If existing the term accession value" + Parameter.create "IsA" OntologyInfoType "If the term could be IsA related these values are written here." Parameter.create "RowIndices" (ParamArray ParamInteger) "" Parameter.create "TermOpt" (TermType |> ParamOption) "This value is empty when created client side and will be filled by server after term search." |] @@ -120,6 +128,8 @@ module PredefinedParams = module Examples = + let ontologyInfoExmp:OntologyInfo = OntologyInfo.create "Instrument Model" "MS:1000031" + let unitOntologyExmp:DbDomain.Ontology = { ID = 1L Name = "uo" @@ -132,6 +142,8 @@ module PredefinedParams = let searchTermExmp:SearchTermI = { ColIndices = [|2; 3|] SearchString = "Bruker Daltonics HCT Series" + TermAccession = "" + IsA = Some ontologyInfoExmp RowIndices = [|0 .. 10|] TermOpt = None } diff --git a/src/Server/OntologyDB.fs b/src/Server/OntologyDB.fs index e63bc4fb..9b9df961 100644 --- a/src/Server/OntologyDB.fs +++ b/src/Server/OntologyDB.fs @@ -148,6 +148,79 @@ let getTermSuggestionsByParentTerm cString (query:string, parentTerm:string) = (reader.GetBoolean(6)) |] +let getTermByParentTermOntologyInfo cString (query:string, parentTerm:OntologyInfo) = + + let hasAccession = parentTerm.TermAccession <> "" + + use connection = establishConnection cString + connection.Open() + use cmd = + if hasAccession then + new MySqlCommand("getTermByParentTermAndAccession",connection) + else + new MySqlCommand("getTermByParentTerm",connection) + + cmd.CommandType <- CommandType.StoredProcedure + + let queryParam = cmd.Parameters.Add("query",MySqlDbType.VarChar) + let parentOntologyParam = cmd.Parameters.Add("parentOntology",MySqlDbType.VarChar) + + queryParam .Value <- query + parentOntologyParam .Value <- parentTerm.Name + + if hasAccession then + let accessionParam = cmd.Parameters.Add("parentTermAccession", MySqlDbType.VarChar) + accessionParam .Value <- parentTerm.TermAccession + + use reader = cmd.ExecuteReader() + [| + while reader.Read() do + yield + DbDomain.createTerm + (reader.GetInt64(0)) + (reader.GetString(1)) + (reader.GetInt64(2)) + (reader.GetString(3)) + (reader.GetString(4)) + (if (reader.IsDBNull(5)) then + None + else + Some (reader.GetString(5))) + (reader.GetBoolean(6)) + |] + +let getTermSuggestionsByParentTermAndAccession cString (query:string, parentTerm:string, parentTermAccession:string) = + + use connection = establishConnection cString + connection.Open() + use getTermSuggestionsCmd = new MySqlCommand("getTermSuggestionsByParentTermAndAccession",connection) + getTermSuggestionsCmd.CommandType <- CommandType.StoredProcedure + + let queryParam = getTermSuggestionsCmd.Parameters.Add("query",MySqlDbType.VarChar) + let parentOntologyParam = getTermSuggestionsCmd.Parameters.Add("parentOntology",MySqlDbType.VarChar) + let parentTermAccessionParam = getTermSuggestionsCmd.Parameters.Add("parentTermAccession",MySqlDbType.VarChar) + + queryParam .Value <- query + parentOntologyParam .Value <- parentTerm + parentTermAccessionParam.Value <- parentTermAccession + + use reader = getTermSuggestionsCmd.ExecuteReader() + [| + while reader.Read() do + yield + DbDomain.createTerm + (reader.GetInt64(0)) + (reader.GetString(1)) + (reader.GetInt64(2)) + (reader.GetString(3)) + (reader.GetString(4)) + (if (reader.IsDBNull(5)) then + None + else + Some (reader.GetString(5))) + (reader.GetBoolean(6)) + |] + let getUnitTermSuggestions cString (query:string) = use connection = establishConnection cString @@ -208,6 +281,41 @@ let getTermByName cString (queryStr:string) = (reader.GetBoolean(6)) |] +let getTermByNameAndAccession cString (queryStr:string,accessionString:string) = + + use connection = establishConnection cString + connection.Open() + + use cmd = connection.CreateCommand() + cmd + .CommandText <- """ + SELECT * FROM Term + WHERE Term.Name = @name + AND Term.Accession = @accession + """ + + let queryParam = cmd.Parameters.Add("name",MySqlDbType.VarChar) + let accessionParam = cmd.Parameters.Add("accession",MySqlDbType.VarChar) + + queryParam.Value <- queryStr + accessionParam.Value <- accessionString + + use reader = cmd.ExecuteReader() + [| + while reader.Read() do + DbDomain.createTerm + (reader.GetInt64(0)) + (reader.GetString(1)) + (reader.GetInt64(2)) + (reader.GetString(3)) + (reader.GetString(4)) + (if (reader.IsDBNull(5)) then + None + else + Some (reader.GetString(5))) + (reader.GetBoolean(6)) + |] + let getTermByAccession cString (queryStr:string) = use connection = establishConnection cString diff --git a/src/Server/Server.fs b/src/Server/Server.fs index 58b1dba9..97a1314c 100644 --- a/src/Server/Server.fs +++ b/src/Server/Server.fs @@ -81,7 +81,7 @@ let annotatorApi cString = { return searchRes } - getTermSuggestionsByParentTerm = fun (max:int,typedSoFar:string,parentTerm:string) -> + getTermSuggestionsByParentTerm = fun (max:int,typedSoFar:string,parentTerm:OntologyInfo) -> async { let searchRes = @@ -89,7 +89,12 @@ let annotatorApi cString = { | HelperFunctions.Regex HelperFunctions.isAccessionPattern foundAccession -> OntologyDB.getTermByAccession cString foundAccession | _ -> - let like = OntologyDB.getTermSuggestionsByParentTerm cString (typedSoFar,parentTerm) + let like = + if parentTerm.TermAccession = "" + then + OntologyDB.getTermSuggestionsByParentTerm cString (typedSoFar,parentTerm.Name) + else + OntologyDB.getTermSuggestionsByParentTermAndAccession cString (typedSoFar,parentTerm.Name,parentTerm.TermAccession) let searchSet = typedSoFar |> Suggestion.createBigrams like |> Array.sortByDescending (fun sugg -> @@ -134,12 +139,23 @@ let annotatorApi cString = { getTermsByNames = fun (queryArr) -> async { let result = + printfn "START" queryArr |> Array.map (fun searchTerm -> {searchTerm with TermOpt = - // check if search string is empty. This case should delete TAN and TSR in table + // check if search string is empty. This case should delete TAN- and TSR- values in table if searchTerm.SearchString = "" then None + // check if term accession was found. If so search also by this as it is unique + elif searchTerm.TermAccession <> "" then + printfn "hit1: %A" searchTerm.SearchString + let searchRes = OntologyDB.getTermByNameAndAccession cString (searchTerm.SearchString,searchTerm.TermAccession) + if Array.isEmpty searchRes then None else searchRes |> Array.head |> Some + elif searchTerm.IsA.IsSome then + printfn "hit2: %A" searchTerm.SearchString + let searchRes = OntologyDB.getTermByParentTermOntologyInfo cString (searchTerm.SearchString,searchTerm.IsA.Value) + if Array.isEmpty searchRes then None else searchRes |> Array.head |> Some else + printfn "hit3: %A" searchTerm.SearchString let searchRes = OntologyDB.getTermByName cString searchTerm.SearchString if Array.isEmpty searchRes then None else searchRes |> Array.head |> Some } diff --git a/src/Shared/Shared.fs b/src/Shared/Shared.fs index bb3922da..b18cf563 100644 --- a/src/Shared/Shared.fs +++ b/src/Shared/Shared.fs @@ -119,16 +119,31 @@ module DbDomain = RelatedTermID : int64 } -/// used in OfficeInterop to effectively find possible Term names and search for them in db +type OntologyInfo = { + Name : string + TermAccession : string +} with + static member create name termAccession = { + Name = name + TermAccession = termAccession + } + +/// Used in OfficeInterop to effectively find possible Term names and search for them in db type SearchTermI = { ColIndices : int [] + /// This is the Ontology Name SearchString : string + /// This is the Ontology Term Accession 'XX:aaaaaa' + TermAccession : string + IsA : OntologyInfo option RowIndices : int [] TermOpt : DbDomain.Term option } with - static member create colIndices searchString rowIndices = { + static member create colIndices searchString termAccession ontologyInfoOpt rowIndices = { ColIndices = colIndices SearchString = searchString + TermAccession = termAccession + IsA = ontologyInfoOpt RowIndices = rowIndices TermOpt = None } @@ -164,7 +179,7 @@ type IAnnotatorAPIv1 = { // Term related requests getTermSuggestions : (int*string) -> Async /// (nOfReturnedResults*queryString*parentOntology). If parentOntology = "" then isNull -> Error. - getTermSuggestionsByParentTerm : (int*string*string) -> Async + getTermSuggestionsByParentTerm : (int*string*OntologyInfo) -> Async /// (ontOpt,searchName,mustContainName,searchDefinition,mustContainDefinition,keepObsolete) getTermsForAdvancedSearch : (DbDomain.Ontology option*string*string*string*string*bool) -> Async