diff --git a/Ontology-Search-Template.html b/Ontology-Search-Template.html index 5e2f6fa..b42a7ba 100644 --- a/Ontology-Search-Template.html +++ b/Ontology-Search-Template.html @@ -29,7 +29,7 @@ .hidden { display: none; } - + .error { padding-top: 10px; color: #e74c3c; @@ -233,6 +233,8 @@ + + @@ -302,21 +304,21 @@ * the sidebar UI with the resulting translation. */ function runSearch() { - + var term = $('input[name=search-text]').val(); - + if(term === "") { showError('Please enter a search term.', $('#button-bar')); return } - - this.disabled = true; - + + this.disabled = true; + $('#search-results').html('

Searching...

'); $('#error').remove(); var service = $('#service').val(); - + google.script.run .withSuccessHandler( @@ -325,10 +327,10 @@ var html = '
'; console.log(result); if (result) { - + Object.keys(result).sort().forEach(function(ontology_abbr) { var ontology = result[ontology_abbr]; - + html += '

' + ontology_abbr + ' - ' + ontology["ontology-name"] + '

' + '
'; }); - + } else { html += "

Sorry, no results were found matching your query...

"; } @@ -385,4 +387,4 @@ - \ No newline at end of file + diff --git a/OntologySearch.gs b/OntologySearch.gs index e3a6824..656a93d 100644 --- a/OntologySearch.gs +++ b/OntologySearch.gs @@ -52,6 +52,10 @@ function performSearch(service, term) { return searchLOV(term); case "ols": return searchOLS(term); + case "tib-ols": + return searchTIBOLS(term); + case "custom-ols3": + return searchCustomOLS3(term); default: return searchLOV(term); } @@ -134,7 +138,7 @@ function searchLOV(term) { var restriction = findRestrictionForCurrentColumn("LOV"); var vocabularies = getLinkedOpenVocabularies(); - var url = "http://lov.okfn.org/dataset/lov/api/v2/search?q=" + term; + var url = "https://lov.linkeddata.es/dataset/lov/api/v2/search?q=" + term; var vocabShortname; var vocabURI; if (restriction.source) { @@ -142,7 +146,7 @@ function searchLOV(term) { if (vocabularies[vocabShortname]) { vocabURI = vocabularies[vocabShortname].uri; if (vocabURI) - url = "http://lov.okfn.org/dataset/lov/api/v2/search?q=" + term + "&voc=" + vocabURI; + url = "https://lov.linkeddata.es/dataset/lov/api/v2/search?q=" + term + "&voc=" + vocabURI; } else { url = ""; } @@ -251,7 +255,122 @@ function searchOLS(term) { ontologyDict[ontologyLabel] = {"ontology-name": elem.ontology_name, "terms": []}; record = { label: elem.label, - id: elem.id, + id: elem.short_form, + 'ontology-label': elem.ontology_prefix, + 'ontology-name': elem.ontology_name, + accession: elem.iri,//elem.obo_id, + ontology: elem.ontology_name, + details: '', + url: elem.iri + }; + + storeInCache(record.id, JSON.stringify(record)); + ontologyDict[ontologyLabel].terms.push(record); + } + }); + storeInCache(url, JSON.stringify(ontologyDict)); + // Logger.log(ontologyDict); + return ontologyDict; + } + catch(e) { + Logger.log(e); + throw(e); + } +} + + +/** + * @method + * @name searchTIBOLS + * @param{string} term + */ +function searchTIBOLS(term) { + try { + var restriction = findRestrictionForCurrentColumn("OLS"); + var ontologies = getTIBOLSOntologies(); + var url = TIBOLS_API_BASE_URI + '/search'; + var queryObj = { + q: term, + rows: OLS_PAGINATION_SIZE, + start: 0, + ontology: restriction ? restriction.ontologyId : undefined + }; + var queryString = jsonToQueryString(queryObj); + url += '?' + queryString; + var cacheResult = fetchFromCache(url); + if (cacheResult) { + return JSON.parse(cacheResult); + } + var text = UrlFetchApp.fetch(url).getContentText(), json = JSON.parse(text), ontologyDict = {}; + + var docs = json.response && json.response.docs; + if (!docs || docs.length === 0) { + throw "No Result found."; + } + docs.forEach(function(elem) { + var ontology = ontologies[elem.ontology_name], ontologyLabel = elem.ontology_prefix, record; + if (!ontologyDict[ontologyLabel]) { + ontologyDict[ontologyLabel] = {"ontology-name": elem.ontology_name, "terms": []}; + record = { + label: elem.label, + id: elem.short_form, + 'ontology-label': elem.ontology_prefix, + 'ontology-name': elem.ontology_name, + accession: elem.iri,//elem.obo_id, + ontology: elem.ontology_name, + details: '', + url: elem.iri + }; + + storeInCache(record.id, JSON.stringify(record)); + ontologyDict[ontologyLabel].terms.push(record); + } + }); + storeInCache(url, JSON.stringify(ontologyDict)); + // Logger.log(ontologyDict); + return ontologyDict; + } + catch(e) { + Logger.log(e); + throw(e); + } +} + +/** + * @method + * @name searchCustomOLS3 + * @param{string} term + */ +function searchCustomOLS3(term) { + try { + var restriction = findRestrictionForCurrentColumn("OLS"); + var ontologies = getCustomOLS3Ontologies(); + var url = CUSTOMOLS3_API_BASE_URI + '/search'; + var queryObj = { + q: term, + rows: OLS_PAGINATION_SIZE, + start: 0, + ontology: restriction ? restriction.ontologyId : undefined + }; + var queryString = jsonToQueryString(queryObj); + url += '?' + queryString; + var cacheResult = fetchFromCache(url); + if (cacheResult) { + return JSON.parse(cacheResult); + } + var text = UrlFetchApp.fetch(url).getContentText(), json = JSON.parse(text), ontologyDict = {}; + + var docs = json.response && json.response.docs; + if (!docs || docs.length === 0) { + throw "No Result found."; + } + docs.forEach(function(elem) { + var ontology = ontologies[elem.ontology_name], ontologyLabel = elem.ontology_prefix, record; + if (!ontologyDict[ontologyLabel]) { + ontologyDict[ontologyLabel] = {"ontology-name": elem.ontology_name, "terms": []}; + record = { + label: elem.label, + id: elem.short_form, 'ontology-label': elem.ontology_prefix, 'ontology-name': elem.ontology_name, accession: elem.iri,//elem.obo_id, @@ -293,7 +412,7 @@ function handleTermInsertion(term_id) { // figure out whether the Term Source REF and Term Accession Number columns exist, if they do exist at all. Insertion technique will vary // depending on the file being looked at. var sourceAndAccessionPositions = getSourceAndAccessionPositionsForTerm(selectedRange.getColumn()); - + // add all terms into a separate sheet with all their information. if (sourceAndAccessionPositions.sourceRef != undefined && sourceAndAccessionPositions.accession != undefined) { insertOntologySourceInformationInInvestigationBlock(ontologyObject); @@ -317,7 +436,7 @@ function handleTermInsertion(term_id) { sheet.getRange(row, selectedColumn).setValue(ontologyObject.term); sheet.getRange(row, nextColumn).setValue(ontologyObject.url); } else { - sheet.getRange(row, selectedColumn).setFormula('=HYPERLINK("' + ontologyObject.url + '","' + ontologyObject.term + '")') + sheet.getRange(row, selectedColumn).setFormula('=HYPERLINK("' + ontologyObject.url + '";"' + ontologyObject.term + '")') } } } @@ -328,4 +447,4 @@ function handleTermInsertion(term_id) { throw err; } -} \ No newline at end of file +} diff --git a/OntologyTagging.gs b/OntologyTagging.gs index 0c0cc92..59fcc41 100644 --- a/OntologyTagging.gs +++ b/OntologyTagging.gs @@ -184,7 +184,7 @@ function replaceTermWithSelectedValue(term_id) { sheet.getRange(row, selectedColumn).setValue(ontologyObject.term); sheet.getRange(row, nextColumn).setValue(ontologyObject.accession); } else { - sheet.getRange(row, selectedColumn).setFormula('=HYPERLINK("'+ ontologyObject.accession +'","' + ontologyObject.term + '")') + sheet.getRange(row, selectedColumn).setFormula('=HYPERLINK("'+ ontologyObject.accession +'";"' + ontologyObject.term + '")') } } } diff --git a/Ontomaton-terms.md b/Ontomaton-terms.md index 0b6d160..bdb3ed0 100644 --- a/Ontomaton-terms.md +++ b/Ontomaton-terms.md @@ -13,6 +13,7 @@
  • NBCO Bioportal
  • EMBL-EBI Ontology Lookup Service
  • Linked Open Vocabularies catalogue
  • +
  • TIB Terminology Service
  • @@ -30,11 +31,11 @@ Bioinformatics 2013 29: 525-527. Bioinformatics article on OntoMaton [here](http://dx.doi.org/10.1093/bioinformatics/bts718). -Eamonn Maguire, Alejandra González-Beltrán, Patricia L. Whetzel, Susanna-Assunta Sansone, and Philippe Rocca-Serra -OntoMaton: a Bioportal powered ontology widget for Google Spreadsheets +Eamonn Maguire, Alejandra González-Beltrán, Patricia L. Whetzel, Susanna-Assunta Sansone, and Philippe Rocca-Serra +OntoMaton: a Bioportal powered ontology widget for Google Spreadsheets Bioinformatics 2013 29: 525-527. doi: [10.1093/bioinformatics/bts718](http://dx.doi.org/10.1093/bioinformatics/bts718) Please, note that at the time of the publication, OntoMaton was powered by the NCBO BioPortal web services. Since then, we have made the following extensions: - we upgraded the code to [Google Add-ons for Docs and Sheets](http://googledrive.blogspot.it/2014/03/add-ons.html), which [deprecated the Apps Script applications and the Script Gallery](http://googleappsdeveloper.blogspot.co.uk/2014/06/deprecating-script-gallery-in-old.html), used in the original OntoMaton version - we upgraded to the new BioPortal API for searching ontology terms and annotator services (see BioPortal 4.0 release notes), as the old API was deprecated in 2014 -- we extended the ontology search functionality to support REST services from the Linked Open Vocabularies and the EBI Ontology Lookup Service, thus now allowing ontology term searches over three separate services. - -For more information, see [our blog posts on OntoMaton](https://isatools.wordpress.com/?s=ontomaton). +- we extended the ontology search functionality to support REST services from the Linked Open Vocabularies and the EBI Ontology Lookup Service, and the TIB Terminology Service, thus now allowing ontology term searches over several separate services. +For more information, see [our blog posts on OntoMaton](https://isatools.wordpress.com/?s=ontomaton). ### Installation -With the new add on infrastructure, installation is very easy. +With the new add on infrastructure, installation is very easy. 1. Click on the 'Add-ons' menu item in your Google Spreadsheet: @@ -43,7 +42,7 @@ With the new add on infrastructure, installation is very easy.
    -2. Click on 'Get add-ons...' and then search for 'OntoMaton': +2. Click on 'Get add-ons...' and then search for 'OntoMaton':
    @@ -68,7 +67,7 @@ Here you can click on the image and read more about OntoMaton:
    -4. You'll then have the OntoMaton app installed. +4. You'll then have the OntoMaton app installed.
    @@ -82,7 +81,7 @@ You can access it through the 'Add On' menu option. ### Ontology Search -From OntoMaton, you can search three different services within one tool: the [NCBO Bioportal](http://bioportal.bioontology.org/), [Linked Open Vocabularies](http://lov.okfn.org) and [EBI Ontology Lookup Service](https://www.ebi.ac.uk/ols/), and insert the terms in your Google Spreadsheet directly. Full term provenance is recorded for you and later downstream analysis. +From OntoMaton, you can search three different services within one tool: the [NCBO Bioportal](http://bioportal.bioontology.org/), [Linked Open Vocabularies](https://lov.linkeddata.es) and [EBI Ontology Lookup Service](https://www.ebi.ac.uk/ols/), and insert the terms in your Google Spreadsheet directly. Full term provenance is recorded for you and later downstream analysis.
    @@ -90,7 +89,7 @@ From OntoMaton, you can search three different services within one tool: the [NC ### Ontology Tagging -With OntoMaton, you can select a number of spreadsheet cells and then 'tag' them. This means that OntoMaton will take the terms in the cells and send them to BioPortal's Annotator service. The results will come back as a list of the free text terms, showing for each all matches in BioPortal. +With OntoMaton, you can select a number of spreadsheet cells and then 'tag' them. This means that OntoMaton will take the terms in the cells and send them to BioPortal's Annotator service. The results will come back as a list of the free text terms, showing for each all matches in BioPortal.
    @@ -105,9 +104,9 @@ With OntoMaton, you can select a number of spreadsheet cells and then 'tag' them From the settings screen, you can configure: -* How terms should be inserted in to the spreadsheet when not in 'ISA mode' (where the next columns aren't named 'Term Source REF' or 'Term Source Accession'). The two options are as either as a hyperlink to the term in Bioportal/OLS/LOV or as a term name with the hyperlink in parentheses. -* Restrictions, which specify for zero or more columns (with a name in the first cell), restrictions that should be placed on the search space per each of the ontology lookup services we use (Bioportal/OLS/LOV) E.g. the column 'Label' is restricted to terms from the Chemincal Entities of Biomedical Interest ontology (ChEBI). Please, note that for instance if a column has a restriction over the BioPortal service, the restiction will not have an effect if searching terms with OLS. - +* How terms should be inserted in to the spreadsheet when not in 'ISA mode' (where the next columns aren't named 'Term Source REF' or 'Term Source Accession'). The two options are as either as a hyperlink to the term in Bioportal/OLS/LOV or as a term name with the hyperlink in parentheses. +* Restrictions, which specify for zero or more columns (with a name in the first cell), restrictions that should be placed on the search space per each of the ontology lookup services we use (Bioportal/OLS/LOV) E.g. the column 'Label' is restricted to terms from the Chemincal Entities of Biomedical Interest ontology (ChEBI). Please, note that for instance if a column has a restriction over the BioPortal service, the restiction will not have an effect if searching terms with OLS. +* Setting a Custom API base URL for OLS3-compatible terminology services. ### Restricting OntoMaton's search space @@ -121,17 +120,17 @@ From the settings screen, you can configure: When you add a restriction using the 'Settings' panel for the first time, a 'Restrictions' sheet will be added automatically. This sheet will have the following column headers: -```Column Name | Ontology | Branch | Version | Ontology Name | Service```. Then you may define for a particular column header in your spreadsheet what ontology should be searched (or list of ontologies) over what service (BioPortal, OLS or LOV). A restriction will only apply if using the corresponding service for search. +```Column Name | Ontology | Branch | Version | Ontology Name | Service```. Then you may define for a particular column header in your spreadsheet what ontology should be searched (or list of ontologies) over what service (BioPortal, OLS or LOV). A restriction will only apply if using the corresponding service for search. Additionally, within one ontology restriction, for BioPortal searches, you can restrict to a particular branch of an ontology, providing a way to further restrict the search space. An example of a google spreadsheet with such functionality can be viewed here: https://docs.google.com/spreadsheet/ccc?key=0Al5WvYyk0zzmdDNLeEcxWHZJX042dS0taXJPNXpJMHc - + ### Video Tutorial Access the video tutorial showing how to install and use OntoMaton (version 1) [here](http://www.youtube.com/watch?v=Qs0nxGBfQac&feature=player_embedded). - + ### Templates Templates can be found through accessing them on the google templates site. OntoMaton templates are [here](https://drive.google.com/templates?type=spreadsheets&q=ontomaton). @@ -149,5 +148,3 @@ This work is licensed through a [CPAL license](http://isatab.sf.net/licenses/Ont ### Merchandise Fancy an OntoMaton t-shirt? We haven't got any to give away...yet! But...you can buy one of these rather snazzy t-shirts from [Spreadshirts](http://antarctic-design.spreadshirt.co.uk/men-s-classic-t-shirt-A22910590/customize/color/2) for just £15! - - diff --git a/Settings-Template.html b/Settings-Template.html index 520599d..225a1b6 100644 --- a/Settings-Template.html +++ b/Settings-Template.html @@ -85,6 +85,14 @@ google.script.run.setOntologyInsertionPreference(insertionOneColumn); } + function updateCustomOLS3_API_BASE_URI() { + new_base_url = $('#option_CustomOLS3_API_BASE_URI').string(); + console.log("new_base_url is set to:"); + console.log(new_base_url); + google.script.run.setCustomOls3ApiBaseUri(new_base_url); + } + + function setOntologies(data) { ontologies = data; $('#loading').addClass('hidden'); @@ -157,10 +165,18 @@
    -
    +
    -
    +
    + +

    +

    Custom OLS3 API BASE_URI

    +
    + +
    +


    @@ -220,4 +236,4 @@ - \ No newline at end of file + diff --git a/Settings.gs b/Settings.gs index 1454bc9..0f32b04 100644 --- a/Settings.gs +++ b/Settings.gs @@ -38,7 +38,7 @@ function showSettings() { SpreadsheetApp.getUi().showSidebar(html); } -function loadOntologies() { +function loadOntologies() { return { 'BioPortal': getBioPortalOntologies(), 'LOV': getLinkedOpenVocabularies(), @@ -53,6 +53,8 @@ function createSettingsTab() { settingsSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("Settings"); settingsSheet.getRange("A1").setValue("insertTermInOneColumn"); settingsSheet.getRange("B1").setValue(true); + settingsSheet.getRange("A2").setValue("CustomOLS3_API_BASE_URI"); + settingsSheet.getRange("B2").setValue("https://service.tib.eu/ts4tib/api"); SpreadsheetApp.getActiveSpreadsheet().setActiveSheet(activeSheet); } } @@ -61,8 +63,8 @@ function viewRestrictionHandler() { var restrictionSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Restrictions"); if (restrictionSheet == undefined) { - UiApp.getActiveApplication().getElementById("status").setText("Restriction sheet doesn't exist yet. Add a restriction and it will be created automatically."); - return UiApp.getActiveApplication(); + var htmlOutput = HtmlService.createHtmlOutput("

    Restriction sheet doesn't exist yet. Add a restriction and it will be created automatically.

    "); + SpreadsheetApp.getActiveSpreadsheet().show(htmlOutput); } else { SpreadsheetApp.getActiveSpreadsheet().setActiveSheet(restrictionSheet); } @@ -79,11 +81,11 @@ function loadPreferences() { } function addRestrictionHandler(params) { - + var ontology = params.ontology.trim(); var columnName = params.columnName.trim(); var service = params.service.trim(); - + var restrictionSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Restrictions"); if (restrictionSheet == undefined) { @@ -99,7 +101,7 @@ function addRestrictionHandler(params) { SpreadsheetApp.getActiveSpreadsheet().setActiveSheet(activeSheet); } - + if(columnName !== "") { var nextBlankRow = findNextBlankRow(restrictionSheet); restrictionSheet.getRange(nextBlankRow, 1).setValue(columnName); @@ -108,7 +110,7 @@ function addRestrictionHandler(params) { restrictionSheet.getRange(nextBlankRow, 6).setValue(service); return "Restriction for " + columnName + " Added" } - + return "Column name cannot be empty." } @@ -117,6 +119,11 @@ function setOntologyInsertionPreference(insertSingleColumn) { settingsSheet.getRange("B1").setValue(insertSingleColumn); } +function setCustomOls3ApiBaseUri(new_url) { + var settingsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Settings"); + settingsSheet.getRange("B2").setValue(new_url); +} + function getBioPortalOntologies() { @@ -147,7 +154,7 @@ function getBioPortalOntologies() { function getLinkedOpenVocabularies(){ - var vocabsURL = "http://lov.okfn.org/dataset/lov/api/v2/vocabulary/list"; + var vocabsURL = "https://lov.linkeddata.es/dataset/lov/api/v2/vocabulary/list"; var cache = CacheService.getPrivateCache(); if (cache.get("lov_fragments") == null) { @@ -205,3 +212,75 @@ function getOLSOntologies() { return ontologyDict; } + +/** + * @method + * @name getTIBOLSOntologies + * @description gets all the ontologies from TIB-OLS + * @return{Object} + */ +function getTIBOLSOntologies() { + var ontologiesUri = TIBOLS_API_BASE_URI + "/ontologies?size=" + OLS_PAGINATION_SIZE; + var cache = CacheService.getPrivateCache(), res, text, json, ontologies = []; + + if (cache.get("tib-ols") == null) { + do { + res = UrlFetchApp.fetch(ontologiesUri); + text = res.getContentText('utf-8'); + json = JSON.parse(text); + ontologies = ontologies.concat(json._embedded.ontologies); + ontologiesUri = json._links && json._links.next && json._links.next.href; + } + while (ontologiesUri); + // store into cache the result as plain text + splitResultAndCache(cache, "tib-ols", JSON.stringify(ontologies)); + } else { + ontologies = JSON.parse(getCacheResultAndMerge(cache, "tib-ols")); + } + + var ontologyDict = {}; + ontologies.forEach(function(ontology) { + var config = ontology.config || {}; + ontologyDict[ontology.ontologyId] = { + name: config.title, + uri: config.id + }; + }); + return ontologyDict; +} + +/** + * @method + * @name getCustomOLS3Ontologies + * @description gets all the ontologies from a custom OLS3 endpoint + * @return{Object} + */ +function getCustomOLS3Ontologies() { + var ontologiesUri = CUSTOMOLS3_API_BASE_URI + "/ontologies?size=" + OLS_PAGINATION_SIZE; + var cache = CacheService.getPrivateCache(), res, text, json, ontologies = []; + + if (cache.get("custom-ols3") == null) { + do { + res = UrlFetchApp.fetch(ontologiesUri); + text = res.getContentText('utf-8'); + json = JSON.parse(text); + ontologies = ontologies.concat(json._embedded.ontologies); + ontologiesUri = json._links && json._links.next && json._links.next.href; + } + while (ontologiesUri); + // store into cache the result as plain text + splitResultAndCache(cache, "custom-ols3", JSON.stringify(ontologies)); + } else { + ontologies = JSON.parse(getCacheResultAndMerge(cache, "custom-ols3")); + } + + var ontologyDict = {}; + ontologies.forEach(function(ontology) { + var config = ontology.config || {}; + ontologyDict[ontology.ontologyId] = { + name: config.title, + uri: config.id + }; + }); + return ontologyDict; +} diff --git a/Utils.gs b/Utils.gs index f7cfb19..0789a72 100644 --- a/Utils.gs +++ b/Utils.gs @@ -32,8 +32,10 @@ // constants -var OLS_API_BASE_URI = "http://www.ebi.ac.uk/ols/api"; +var TIBOLS_API_BASE_URI = "https://service.tib.eu/ts4tib/api"; +var OLS_API_BASE_URI = "http://www.ebi.ac.uk/ols4/api"; var OLS_PAGINATION_SIZE = 500; // MAX pagination size +var CUSTOMOLS3_API_BASE_URI = "https://service.tib.eu/ts4tib/api"; // replace with Settings sheet lookup /** * @description serializes an object to a queryString @@ -361,4 +363,4 @@ function replaceControlCharacters(text) { } catch (e){ return text; } -} \ No newline at end of file +}