diff --git a/app/assets/javascripts/bp_annotatorplus.js b/app/assets/javascripts/bp_annotatorplus.js index bc051caa9..e66e1cac4 100644 --- a/app/assets/javascripts/bp_annotatorplus.js +++ b/app/assets/javascripts/bp_annotatorplus.js @@ -1,906 +1,905 @@ -var - bp_last_params = null, - annotationsTable = null, - annotator_ontologies = null; - -// Note: the configuration is in config/bioportal_config.rb. -var BP_CONFIG = jQuery(document).data().bp.config; - -var BP_COLUMNS = { - classes: 0, - ontologies: 1, - types: 2, - sem_types: 3, - matched_classes: 5, - matched_ontologies: 6, - score: 7, - negation: 8, - experiencer: 9, - temporality: 10 -}; - -var CONCEPT_MAP = { - "mapping": "mappedConcept", - "mgrep": "concept", - "closure": "concept" -}; - -function set_last_params(params) { - bp_last_params = params; - bp_last_params.apikey = BP_CONFIG.apikey; // TODO: get the user apikey? - //console.log(bp_last_params); -} - -function insertSampleText(event) { - "use strict"; - event.preventDefault(); - var text = "The patient has no sign of melanoma but his father had skin cancer. The epithelial tissue cells show no abnormal activity."; - jQuery("#annotation_text").focus(); - jQuery("#annotation_text").val(text); -} - -function get_annotations() { - jQuery("#results_error").html(""); - jQuery("#annotator_error").html(""); - - // Validation - if (!jQuery("#annotation_text").val()) { - jQuery("#annotator_error").html("Please enter text to annotate"); - return; - } - - // Really dumb, basic word counter. - if (jQuery("#annotation_text").val().split(' ').length > 500) { - jQuery("#annotator_error").html("Please use less than 500 words. If you need to annotate larger pieces of text you can use the <a href='http://www.bioontology.org/wiki/index.php/Annotator_User_Guide' target='_blank'>Annotator Web Service</a>"); - return; - } - - jQuery("#annotations_container").hide(); - jQuery(".annotator_spinner").show(); - ajax_process_halt(); - - var params = {}, - ont_select = jQuery("#ontology_ontologyId"), - mappings = []; - - params.text = jQuery("#annotation_text").val(); - params.ontologies = (ont_select.val() === null) ? [] : ont_select.val(); - params.longest_only = jQuery("#longest_only").is(':checked'); - params.exclude_numbers = jQuery("#exclude_numbers").is(':checked'); - params.whole_word_only = jQuery("#whole_word_only").is(':checked'); - params.exclude_synonyms = jQuery("#exclude_synonyms").is(':checked'); - params.ncbo_slice = (("ncbo_slice" in BP_CONFIG) ? BP_CONFIG.ncbo_slice : ''); - - params.negation = jQuery("#negation").is(':checked'); - params.experiencer = jQuery("#experiencer").is(':checked'); - params.temporality = jQuery("#temporality").is(':checked'); - - params.score_threshold = jQuery("#score_threshold").val(); - params.confidence_threshold = jQuery("#confidence_threshold").val(); - - params.score = jQuery("#score").val(); - if (params.score) { - annotationsTable.fnSetColumnVis(BP_COLUMNS.score, true); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.score, false); - } - if (params.negation) { - annotationsTable.fnSetColumnVis(BP_COLUMNS.negation, true); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.negation, false); - } - if (params.experiencer) { - annotationsTable.fnSetColumnVis(BP_COLUMNS.experiencer, true); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.experiencer, false); - } - if (params.temporality) { - annotationsTable.fnSetColumnVis(BP_COLUMNS.temporality, true); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.temporality, false); - } - - var maxLevel = parseInt(jQuery("#class_hierarchy_max_level").val()); - if (maxLevel > 0) { - params.expand_class_hierarchy = "true"; - params.class_hierarchy_max_level = maxLevel.toString(); - } - - // UI checkbox to control using the batch call in the controller. - params.raw = true; // do not use batch call to resolve class prefLabel and ontology names. - //if( jQuery("#use_ajax").length > 0 ) { - // params.raw = jQuery("#use_ajax").is(':checked'); - //} - - // Use the annotator default for wholeWordOnly = true. - //if (jQuery("#wholeWordOnly:checked").val() !== undefined) { - // params.wholeWordOnly = jQuery("#wholeWordOnly:checked").val(); - //} - - jQuery("[name='mappings']:checked").each(function() { - mappings.push(jQuery(this).val()); - }); - params.mappings = mappings; - - if (jQuery("#semantic_types").val() !== null) { - params.semantic_types = jQuery("#semantic_types").val(); - annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, true); - jQuery("#results_error").html("Only results from ontologies with semantic types available are displayed."); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, false); - } - - if (jQuery("#semantic_groups").val() !== null) { - params.semantic_groups = jQuery("#semantic_groups").val(); - annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, true); - jQuery("#results_error").html("Only results from ontologies with semantic types available are displayed."); - } else { - annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, false); - } - - params["recognizer"] = jQuery("#recognizer").val(); - - jQuery.ajax({ - type: "POST", - url: "/annotatorplus", // Call back to the UI annotation_controller::create method - data: params, - dataType: "json", - success: function(data) { - set_last_params(params); - display_annotations(data, bp_last_params); - jQuery(".annotator_spinner").hide(200); - jQuery("#annotations_container").show(300); - }, - error: function(data) { - set_last_params(params); - jQuery(".annotator_spinner").hide(200); - jQuery("#annotations_container").hide(); - jQuery("#annotator_error").html(" Problem getting annotations, please try again"); - } - }); -} // get_annotations - -var displayFilteredColumnNames = function() { - "use strict"; - var column_names = []; - var header_text; - jQuery(".bp_popup_list input:checked").closest("th").each(function() { - header_text = this.childNodes[0].textContent.trim(); - column_names.push(header_text); - }); - jQuery("#filter_names").html(column_names.join(", ")); - if (column_names.length > 0) { - jQuery("#filter_list").show(); - } else { - jQuery("#filter_list").hide(); - } -}; - -function createFilterCheckboxes(filter_items, checkbox_class, checkbox_location) { - "use strict"; - var for_sort = [], - sorted = []; - - // Sort ontologies by number of results - jQuery.each(filter_items, function(k, v) { - for_sort.push({ - label: k + " (" + v + ")", - count: v, - value: k, - value_encoded: encodeURIComponent(k) - }); - }); - for_sort.sort(function(a, b) { - return jQuery.trim(a.label) > jQuery.trim(b.label) - }); - - // Create checkboxes for ontology filter - jQuery.each(for_sort, function() { - var checkbox = jQuery("<input/>").attr("class", checkbox_class).attr("type", "checkbox").attr("value", this.value).attr("id", checkbox_class + this.value_encoded); - var label = jQuery("<label/>").attr("for", checkbox_class + this.value_encoded).html(" " + this.label); - sorted.push(jQuery("<span/>").append(checkbox).append(label).html()); - }); - jQuery("#" + checkbox_location).html(sorted.join("<br/>")); -} - -var filter_ontologies = { - init: function() { - "use strict"; - jQuery("#filter_ontologies").bind("click", function(e) { - bp_popup_init(e) - }); - // Need to use bind to avoid "live" propogation issues - jQuery(".filter_ontology_checkboxes").bind("click", function(e) { - filter_ontologies.filterOntology(e) - }); - jQuery("#ontology_filter_list").click(function(e) { - e.stopPropagation() - }); - this.cleanup(); - }, - - cleanup: function() { - "use strict"; - jQuery("html").click(bp_popup_cleanup); - jQuery(document).keyup(function(e) { - if (e.keyCode == 27) { - bp_popup_cleanup(); - } // esc - }); - }, - - filterOntology: function(e) { - "use strict"; - e.stopPropagation(); - var search_regex = []; - jQuery(".filter_ontology_checkboxes:checked").each(function() { - search_regex.push(jQuery(this).val()); - }); - displayFilteredColumnNames(); - if (search_regex.length === 0) { - annotationsTable.fnFilter("", BP_COLUMNS.ontologies); - } else { - annotationsTable.fnFilter(search_regex.join("|"), BP_COLUMNS.ontologies, true, false); - } - } -}; - -var filter_classes = { - init: function() { - "use strict"; - jQuery("#filter_classes").bind("click", function(e) { - bp_popup_init(e) - }); - // Need to use bind to avoid "live" propogation issues - jQuery(".filter_classes_checkboxes").bind("click", function(e) { - filter_classes.filterClasses(e) - }); - jQuery("#classes_filter_list").click(function(e) { - e.stopPropagation() - }); - this.cleanup(); - }, - - cleanup: function() { - "use strict"; - jQuery("html").click(bp_popup_cleanup); - jQuery(document).keyup(function(e) { - if (e.keyCode == 27) { - bp_popup_cleanup(); - } // esc - }); - }, - - filterClasses: function(e) { - "use strict"; - e.stopPropagation(); - var search_regex = []; - jQuery(".filter_classes_checkboxes:checked").each(function() { - // Escape characters used in regex - search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); - }); - displayFilteredColumnNames(); - if (search_regex.length === 0) { - annotationsTable.fnFilter("", BP_COLUMNS.classes); - } else { - annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.classes, true, false); - } - } -}; - -var filter_matched_ontologies = { - init: function() { - "use strict"; - jQuery("#filter_matched_ontologies").bind("click", function(e) { - bp_popup_init(e); - }); - // Need to use bind to avoid "live" propogation issues - jQuery(".filter_matched_ontology_checkboxes").bind("click", function(e) { - filter_matched_ontologies.filter(e); - }); - jQuery("#ontology_matched_filter_list").click(function(e) { - e.stopPropagation(); - }); - this.cleanup(); - }, - - cleanup: function() { - "use strict"; - jQuery("html").click(bp_popup_cleanup); - jQuery(document).keyup(function(e) { - if (e.keyCode == 27) { - bp_popup_cleanup(); - } // esc - }); - }, - - filter: function(e) { - "use strict"; - e.stopPropagation(); - var search_regex = []; - jQuery(".filter_matched_ontology_checkboxes:checked").each(function() { - search_regex.push(jQuery(this).val()); - }); - displayFilteredColumnNames(); - if (search_regex.length === 0) { - annotationsTable.fnFilter("", BP_COLUMNS.matched_ontologies); - } else { - annotationsTable.fnFilter(search_regex.join("|"), BP_COLUMNS.matched_ontologies, true, false); - } - } -}; - -var filter_matched_classes = { - init: function() { - "use strict"; - jQuery("#filter_matched_classes").bind("click", function(e) { - bp_popup_init(e) - }); - // Need to use bind to avoid "live" propogation issues - jQuery(".filter_matched_classes_checkboxes").bind("click", function(e) { - filter_matched_classes.filter(e) - }); - jQuery("#matched_classes_filter_list").click(function(e) { - e.stopPropagation() - }); - this.cleanup(); - }, - - cleanup: function() { - "use strict"; - jQuery("html").click(bp_popup_cleanup); - jQuery(document).keyup(function(e) { - if (e.keyCode == 27) { - bp_popup_cleanup(); - } // esc - }); - }, - - filter: function(e) { - "use strict"; - e.stopPropagation(); - var search_regex = []; - jQuery(".filter_matched_classes_checkboxes:checked").each(function() { - // Escape characters used in regex - search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); - }); - displayFilteredColumnNames(); - if (search_regex.length === 0) { - annotationsTable.fnFilter("", BP_COLUMNS.matched_classes); - } else { - annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.matched_classes, true, false); - } - } -}; - -var filter_match_type = { - init: function() { - "use strict"; - jQuery("#filter_match_type").bind("click", function(e) { - bp_popup_init(e) - }); - // Need to use bind to avoid "live" propogation issues - jQuery(".filter_match_type_checkboxes").bind("click", function(e) { - filter_match_type.filterMatchType(e) - }); - jQuery("#match_type_filter_list").click(function(e) { - e.stopPropagation() - }); - this.cleanup(); - }, - - cleanup: function() { - "use strict"; - jQuery("html").click(bp_popup_cleanup); - jQuery(document).keyup(function(e) { - if (e.keyCode == 27) { - bp_popup_cleanup(); - } // esc - }); - }, - - filterMatchType: function(e) { - "use strict"; - e.stopPropagation(); - var search_regex = []; - jQuery(".filter_match_type_checkboxes:checked").each(function() { - // Escape characters used in regex - search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); - }); - displayFilteredColumnNames(); - if (search_regex.length === 0) { - annotationsTable.fnFilter("", BP_COLUMNS.types); - } else { - annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.types, true, false); - } - } -}; - -var removeFilters = function() { - "use strict"; - jQuery(".filter_ontology_checkboxes").attr("checked", false); - jQuery(".filter_classes_checkboxes").attr("checked", false); - jQuery(".filter_match_type_checkboxes").attr("checked", false); - jQuery(".filter_matched_classes_checkboxes").attr("checked", false); - jQuery(".filter_matched_ontologies_checkboxes").attr("checked", false); - annotationsTable.fnFilter("", BP_COLUMNS.classes); - annotationsTable.fnFilter("", BP_COLUMNS.ontologies); - annotationsTable.fnFilter("", BP_COLUMNS.types); - annotationsTable.fnFilter("", BP_COLUMNS.matched_classes); - annotationsTable.fnFilter("", BP_COLUMNS.matched_ontologies); - jQuery("#filter_list").hide(); -}; - -// Datatables reset sort extension -jQuery.fn.dataTableExt.oApi.fnSortNeutral = function(oSettings) { - "use strict"; - /* Remove any current sorting */ - oSettings.aaSorting = []; - /* Sort display arrays so we get them in numerical order */ - oSettings.aiDisplay.sort(function(x, y) { - return x - y; - }); - oSettings.aiDisplayMaster.sort(function(x, y) { - return x - y; - }); - /* Redraw */ - oSettings.oApi._fnReDraw(oSettings); -}; - -function annotatorFormatLink(param_string, format) { - "use strict"; - // TODO: Check whether 'text' and 'tabDelimited' could work. - // For now, assume that json and xml will work or should work. - var format_map = { - "json": "JSON", - "rdf": "RDF", - "xml": "XML", - "text": "Text", - "tabDelimited": "CSV", - "quaero": "QUAERO", - "brat": "BRAT" - }; - var query = BP_CONFIG.proxy_url + "/annotatorplus?apikey=" + BP_CONFIG.apikey + "&" + param_string; - if (format !== 'json') { - query += "&format=" + format; - } - // var link = "<a href=\"" + encodeURI(query) + "\" class=\"btn btn-default btn-sm\" target=\"_blank\">" + format_map[format] + "</a>"; - var link = "<a href=\"" + encodeURI(query) + "\" class = \"btn btn-outline-primary btn-sm\" target=\"_blank\">" + format_map[format] + "</a>"; - jQuery("#download_links_" + format.toLowerCase()).html(link); -} - -function generateParameters() { - "use strict"; - var params = []; - var new_params = jQuery.extend(true, {}, bp_last_params); // deep copy - delete new_params["apikey"]; - delete new_params["format"]; - delete new_params["raw"]; - //console.log(new_params); - jQuery.each(new_params, function(k, v) { - if (v !== null && v !== undefined) { - if (typeof v == "boolean") { - params.push(k + "=" + v); - } else if (typeof v == "string" && v.length > 0) { - params.push(k + "=" + v); - } else if (typeof v == "array" && v.length > 0) { - params.push(k + "=" + v.join(',')); - } else if (typeof v == "object" && v.length > 0) { - params.push(k + "=" + v.join(',')); - } - } - }); - return params.join("&"); -} - -jQuery(document).ready(function() { - "use strict"; - jQuery("#annotator_button").click(get_annotations); - - jQuery("#semantic_types").select2({ - allowClear: true, - dropdownParent: jQuery(".annotatorplus form") - }); - - jQuery("#semantic_groups").select2({ - allowClear: true, - dropdownParent: jQuery(".annotatorplus form") - }); - - jQuery("#ontology_ontologyId").select2({ - allowClear: true, - dropdownParent: jQuery(".annotatorplus form") - }); - - jQuery("#insert_text_link").click(insertSampleText); - - jQuery("#advancedOptionsLink").click(toggle_advanced_options); - jQuery("#advanced-options-container").hide(); - - // Init annotation table - annotationsTable = jQuery("#annotations").dataTable({ - bPaginate: false, - bAutoWidth: false, - aaSorting: [], - oLanguage: { - sZeroRecords: "No annotations found" - }, - "aoColumns": [{ - // Class column - "sWidth": "15%" - }, { - // Ontology column - "sWidth": "15%" - }, { - // Type column - "sWidth": "5%" - }, { - // column not displayed - "sWidth": "5%", - "bVisible": false - }, { - // Context column - "sWidth": "20%" - }, { - // Matched class column - "sWidth": "15%" - }, { - // matchedOntology column - "sWidth": "15%" - }, { - // Score column - "sWidth": "5%", - "bVisible": false - }, { - // Negation column - "sWidth": "5%", - "bVisible": false - }, { - // Experiencer column - "sWidth": "5%", - "bVisible": false - }, { - // Temporality column - "sWidth": "5%", - "bVisible": false - }] - }); - filter_ontologies.init(); - filter_classes.init(); - filter_match_type.init(); - filter_matched_ontologies.init(); - filter_matched_classes.init(); - - jQuery("#annotator-help").on("click", bpPopWindow); - - jQuery("#annotations_container").hide(); -}); // doc ready - -function get_link(uri, label) { - "use strict"; - return '<a href="' + uri + '">' + label + '</a>'; -} - -function get_class_details(cls) { - var - cls_rel_ui = cls.ui.replace(/^.*\/\/[^\/]+/, ''), - ont_rel_ui = cls_rel_ui.replace(/\?p=classes.*$/, '?p=summary'); - return class_details = { - cls_rel_ui: cls_rel_ui, - ont_rel_ui: ont_rel_ui, - cls_link: get_link(cls_rel_ui, cls.prefLabel), - ont_link: get_link(ont_rel_ui, cls.ontology.name), - semantic_types: cls.semantic_types.join('; ') // test with 'abscess' text and sem type = T046,T020 - } -} - -function get_class_details_from_raw(cls) { - var - ont_acronym = cls.links.ontology.replace(/.*\//, ''), - ont_name = annotator_ontologies[cls.links.ontology].name, - ont_rel_ui = '/ontologies/' + ont_acronym, - ont_link = null; - if (ont_name === undefined) { - ont_link = get_link_for_ont_ajax(ont_acronym); - } else { - ont_link = get_link(ont_rel_ui, ont_name); // no ajax required! - } - var - cls_rel_ui = cls.links.ui.replace(/^.*\/\/[^\/]+/, ''), - cls_label = cls.prefLabel, - cls_link = null; - if (cls_label === undefined) { - cls_link = get_link_for_cls_ajax(cls['@id'], ont_acronym); - } else { - cls_link = get_link(cls_rel_ui, cls_label); // no ajax required! - } - return class_details = { - cls_rel_ui: cls_rel_ui, - ont_rel_ui: ont_rel_ui, - cls_link: cls_link, - ont_link: ont_link, - // - // TODO: Get semantic types from raw data, currently provided by controller. - //semantic_types: cls.semantic_types.join('; ') // test with 'abscess' text and sem type = T046,T020 - semantic_types: '' - } -} - -function get_text_markup(text, from, to) { - var - text_match = text.substring(from - 1, to), - // remove everything prior to the preceding three words (using space delimiters): - text_prefix = text.substring(0, from - 1).replace(/.* ((?:[^ ]* ){2}[^ ]*$)/, "... $1"), - // remove the fourth space and everything following it - text_suffix = text.substring(to).replace(/^((?:[^ ]* ){3}[^ ]*) [\S\s]*/, "$1 ..."), - match_span = '<span style="color: rgb(153,153,153);">', - match_markup_span = '<span style="color: rgb(35, 73, 121); font-weight: bold; padding: 2px 0px;">', - text_markup = match_markup_span + text_match + "</span>"; - //console.log('text markup: ' + text_markup); - return match_span + text_prefix + text_markup + text_suffix + "</span>"; -} - -function get_annotation_rows(annotation, params) { - "use strict"; - // data independent var declarations - var - rows = [], - cells = [], - text_markup = '', - match_type = '', - match_type_translation = { - "mgrep": "direct", - "mapping": "mapping", - "closure": "ancestor" - }; - // data dependent var declarations - var cls = get_class_details(annotation.annotatedClass); - jQuery.each(annotation.annotations, function(i, a) { - text_markup = get_text_markup(params.text, a.from, a.to); - match_type = match_type_translation[a.matchType.toLowerCase()] || 'direct'; - cells = [cls.cls_link, cls.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; - rows.push(cells); - // Add rows for any classes in the hierarchy. - match_type = 'ancestor'; - var h_c = null; - jQuery.each(annotation.hierarchy, function(i, h) { - h_c = get_class_details(h.annotatedClass); - cells = [h_c.cls_link, h_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; - rows.push(cells); - }); // hierarchy loop - // Add rows for any classes in the mappings. Note the ont_link will be different. - match_type = 'mapping'; - var m_c = null; - jQuery.each(annotation.mappings, function(i, m) { - m_c = get_class_details(m.annotatedClass); - cells = [m_c.cls_link, m_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; - rows.push(cells); - }); // mappings loop - }); // annotations loop - return rows; -} - -function get_annotation_score(cls) { - var score = ''; - if (typeof cls.score !== 'undefined') { - score = parseFloat(cls.score).toFixed(3); - } - return score; -} - -/** - * Set context value (negation, temporality or experienced) to empty string to avoid useless dataTable warning - * @param context - * @returns {*} - */ -function get_context_value(context) { - if (typeof context === 'undefined') { - return ''; - } - return context; -} - -function get_annotation_rows_from_raw(annotation, params) { - "use strict"; - // data independent var declarations - var - rows = [], - cells = [], - text_markup = '', - match_type = '', - match_type_translation = { - "mgrep": "direct", - "mapping": "mapping", - "closure": "ancestor" - }; - // data dependent var declarations - var cls = get_class_details_from_raw(annotation.annotatedClass); - if (annotation.annotations.length == 0) { - cells = [cls.cls_link, cls.ont_link, "", cls.semantic_types, "", cls.cls_link, cls.ont_link, get_annotation_score(annotation)]; - rows.push(cells); - } else { - jQuery.each(annotation.annotations, function(i, a) { - text_markup = get_text_markup(params.text, a.from, a.to); - match_type = match_type_translation[a.matchType.toLowerCase()] || 'direct'; - cells = [cls.cls_link, cls.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(annotation), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; - rows.push(cells); - // Add rows for any classes in the hierarchy. - match_type = 'ancestor'; - var h_c = null; - jQuery.each(annotation.hierarchy, function(i, h) { - h_c = get_class_details_from_raw(h.annotatedClass); - cells = [h_c.cls_link, h_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(h), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; - rows.push(cells); - }); // hierarchy loop - // Add rows for any classes in the mappings. Note the ont_link will be different. - match_type = 'mapping'; - var m_c = null; - jQuery.each(annotation.mappings, function(i, m) { - m_c = get_class_details_from_raw(m.annotatedClass); - cells = [m_c.cls_link, m_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(m), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; - rows.push(cells); - }); // mappings loop - }); // annotations loop - } - return rows; -} - -function update_annotations_table(rowsArray) { - "use strict"; - var ontologies = {}, - classes = {}, - match_types = {}, - matched_ontologies = {}, - matched_classes = {}, - context_count = 0; - - jQuery(rowsArray).each(function() { - // [ cls_link, ont_link, match_type, semantic_types, text_markup, cls_link, ont_link ]; - var row = this, - cls_link = row[0], - ont_link = row[1], - match_type = row[2], // direct, ancestors, mapping - //semantic_type = row[3], - //match_text = row[4], - match_cls_link = row[5], - match_ont_link = row[6]; - // Extract labels from links (using non-greedy regex). - var cls_label = cls_link.replace(/^<a.*?>/, '').replace('</a>', '').toLowerCase(), - ont_label = ont_link.replace(/^<a.*?>/, '').replace('</a>', ''), - match_cls_label = match_cls_link.replace(/^<a.*?>/, '').replace('</a>', '').toLowerCase(), - match_ont_label = match_ont_link.replace(/^<a.*?>/, '').replace('</a>', ''); - - // TODO: Gather sem types for display - // var semantic_types = []; - // jQuery.each(annotation.concept.semantic_types, function () { - // semantic_types.push(this.description); - // }); - - // Keep track of contexts. If there are none (IE when using mallet), hide the column - if (row[4] !== "") context_count++; - - // Keep track of how many results are associated with each ontology - ontologies[ont_label] = (ont_label in ontologies) ? ontologies[ont_label] + 1 : 1; - // Keep track of how many results are associated with each class - classes[cls_label] = (cls_label in classes) ? classes[cls_label] + 1 : 1; - // Keep track of match types - match_types[match_type] = (match_type in match_types) ? match_types[match_type] + 1 : 1; - // Keep track of matched classes - matched_classes[match_cls_label] = (match_cls_label in matched_classes) ? matched_classes[match_cls_label] + 1 : 1; - // Keep track of matched ontologies - matched_ontologies[match_ont_label] = (match_ont_label in matched_ontologies) ? matched_ontologies[match_ont_label] + 1 : 1; - }); - - // Add result counts - var count_span = '<span class="result_count">' - jQuery("#result_counts").html("total results " + count_span + rowsArray.length + "</span> "); - var direct_count = ("direct" in match_types) ? match_types["direct"] : 0, - ancestor_count = ("ancestor" in match_types) ? match_types["ancestor"] : 0, - mapping_count = ("mapping" in match_types) ? match_types["mapping"] : 0; - jQuery("#result_counts").append("("); - jQuery("#result_counts").append("direct " + count_span + direct_count + "</span>"); - jQuery("#result_counts").append(" / " + "ancestor " + count_span + ancestor_count + "</span>"); - jQuery("#result_counts").append(" / " + "mapping " + count_span + mapping_count + "</span>"); - jQuery("#result_counts").append(")"); - - // Add checkboxes to filters - createFilterCheckboxes(ontologies, "filter_ontology_checkboxes", "ontology_filter_list"); - createFilterCheckboxes(classes, "filter_classes_checkboxes", "classes_filter_list"); - createFilterCheckboxes(match_types, "filter_match_type_checkboxes", "match_type_filter_list"); - createFilterCheckboxes(matched_ontologies, "filter_matched_ontology_checkboxes", "matched_ontology_filter_list"); - createFilterCheckboxes(matched_classes, "filter_matched_classes_checkboxes", "matched_classes_filter_list"); - - // Reset table - annotationsTable.fnClearTable(); - annotationsTable.fnSortNeutral(); - removeFilters(); - - // Need to re-init because we're not using "live" because of propagation issues - filter_ontologies.init(); - filter_classes.init(); - filter_match_type.init(); - filter_matched_ontologies.init(); - filter_matched_classes.init(); - - // Add data - if (rowsArray.length > 0) { - annotationsTable.fnAddData(rowsArray); - } - - // Hide columns as necessary - if (context_count == 0) { - annotationsTable.fnSetColumnVis(4, false); - } else { - annotationsTable.fnSetColumnVis(4, true); - } - - var match_keys = Object.keys(match_types); - if (match_keys.length == 1 && match_keys[0] === "") - annotationsTable.fnSetColumnVis(2, false); -} - -function display_annotations(data, params) { - "use strict"; - var annotations = data.annotations; - var all_rows = []; - if (params.raw !== undefined && params.raw === true) { - // The annotator_controller does not 'massage' the REST data. - // The class prefLabel and ontology name must be resolved with ajax. - annotator_ontologies = data.ontologies; - for (var i = 0; i < annotations.length; i++) { - all_rows = all_rows.concat(get_annotation_rows_from_raw(annotations[i], params)); - } - } else { - // The annotator_controller does 'massage' the REST data. - // The class prefLabel and ontology name get resoled with a batch all in the controller. - for (var i = 0; i < annotations.length; i++) { - all_rows = all_rows.concat(get_annotation_rows(annotations[i], params)); - } - } - update_annotations_table(all_rows); - // Generate parameters for list at bottom of page - var param_string = generateParameters(); // uses bp_last_param - var query = BP_CONFIG.proxy_url + "/annotatorplus?" + param_string + "&display_links=false&display_context=false"; - var query_encoded = BP_CONFIG.proxy_url + "/annotatorplus?" + encodeURIComponent(param_string); - jQuery("#annotator_parameters").html("<a href=\"" + encodeURI(query) + "\" target=\"_blank\">corresponding REST API call</a>"); - jQuery("#annotator_parameters_encoded").html(query_encoded); - // Add links for downloading results - //annotatorFormatLink("tabDelimited"); - annotatorFormatLink(param_string, "json"); - annotatorFormatLink(param_string, "xml"); - annotatorFormatLink(param_string, "brat"); - annotatorFormatLink(param_string, "quaero"); - annotatorFormatLink(param_string, "rdf"); - - if (params.raw !== undefined && params.raw === true) { - // Initiate ajax calls to resolve class ID to prefLabel and ontology acronym to name. - ajax_process_init(); // see bp_ajax_controller.js - } -} - -function toggle_advanced_options() { - $("#advanced-options-container").toggle(); - var text = $("#advanced-options-container").is(':visible') ? "Hide advanced options <<" : "Show advanced options >>"; - $("#advancedOptionsLink").text(text); -} - -// Creates an HTML form with a button that will POST to the annotator -//function annotatorPostForm(format) { -// "use strict"; -// // TODO: Check whether 'text' and 'tabDelimited' could work. -// // For now, assume that json and xml will work or should work. -// var format_map = { "json": "JSON", "xml": "XML", "text": "Text", "tabDelimited": "CSV" }; -// var params = bp_last_params; -// params["format"] = format; -// var form_fields = []; -// jQuery.each(params, function (k, v) { -// if (v != null) { -// form_fields.push("<input type='hidden' name='" + k + "' value='" + v + "'>"); -// } -// }); -// var action = "action='" + BP_CONFIG.rest_url + "/annotator'"; -// var form = jQuery("<form " + action + " method='post' target='_blank'/>") -// .append(form_fields.join("")) -// .append("<input type='submit' value='" + format_map[format] + "'>"); -// jQuery("#download_links_" + format.toLowerCase()).html(form); -//} +var + bp_last_params = null, + annotationsTable = null, + annotator_ontologies = null; + +// Note: the configuration is in config/bioportal_config.rb. +var BP_CONFIG = jQuery(document).data().bp.config; + +var BP_COLUMNS = { + classes: 0, + ontologies: 1, + types: 2, + sem_types: 3, + matched_classes: 5, + matched_ontologies: 6, + score: 7, + negation: 8, + experiencer: 9, + temporality: 10 +}; + +var CONCEPT_MAP = { + "mapping": "mappedConcept", + "mgrep": "concept", + "closure": "concept" +}; + +function set_last_params(params) { + bp_last_params = params; + bp_last_params.apikey = BP_CONFIG.apikey; // TODO: get the user apikey? + //console.log(bp_last_params); +} + +function insertSampleText(event) { + "use strict"; + event.preventDefault(); + jQuery("#annotation_text").focus(); + jQuery("#annotation_text").val(jQuery("#annotation_sample_text").val()); +} + +function get_annotations() { + jQuery("#results_error").html(""); + jQuery("#annotator_error").html(""); + + // Validation + if (!jQuery("#annotation_text").val()) { + jQuery("#annotator_error").html("Please enter text to annotate"); + return; + } + + // Really dumb, basic word counter. + if (jQuery("#annotation_text").val().split(' ').length > 500) { + jQuery("#annotator_error").html("Please use less than 500 words. If you need to annotate larger pieces of text you can use the <a href='http://www.bioontology.org/wiki/index.php/Annotator_User_Guide' target='_blank'>Annotator Web Service</a>"); + return; + } + + jQuery("#annotations_container").hide(); + jQuery(".annotator_spinner").show(); + ajax_process_halt(); + + var params = {}, + ont_select = jQuery("#ontology_ontologyId"), + mappings = []; + + params.text = jQuery("#annotation_text").val(); + params.ontologies = (ont_select.val() === null) ? [] : ont_select.val(); + params.longest_only = jQuery("#longest_only").is(':checked'); + params.exclude_numbers = jQuery("#exclude_numbers").is(':checked'); + params.whole_word_only = jQuery("#whole_word_only").is(':checked'); + params.exclude_synonyms = jQuery("#exclude_synonyms").is(':checked'); + params.ncbo_slice = (("ncbo_slice" in BP_CONFIG) ? BP_CONFIG.ncbo_slice : ''); + + params.negation = jQuery("#negation").is(':checked'); + params.experiencer = jQuery("#experiencer").is(':checked'); + params.temporality = jQuery("#temporality").is(':checked'); + + params.score_threshold = jQuery("#score_threshold").val(); + params.confidence_threshold = jQuery("#confidence_threshold").val(); + + params.score = jQuery("#score").val(); + if (params.score) { + annotationsTable.fnSetColumnVis(BP_COLUMNS.score, true); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.score, false); + } + if (params.negation) { + annotationsTable.fnSetColumnVis(BP_COLUMNS.negation, true); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.negation, false); + } + if (params.experiencer) { + annotationsTable.fnSetColumnVis(BP_COLUMNS.experiencer, true); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.experiencer, false); + } + if (params.temporality) { + annotationsTable.fnSetColumnVis(BP_COLUMNS.temporality, true); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.temporality, false); + } + + var maxLevel = parseInt(jQuery("#class_hierarchy_max_level").val()); + if (maxLevel > 0) { + params.expand_class_hierarchy = "true"; + params.class_hierarchy_max_level = maxLevel.toString(); + } + + // UI checkbox to control using the batch call in the controller. + params.raw = true; // do not use batch call to resolve class prefLabel and ontology names. + //if( jQuery("#use_ajax").length > 0 ) { + // params.raw = jQuery("#use_ajax").is(':checked'); + //} + + // Use the annotator default for wholeWordOnly = true. + //if (jQuery("#wholeWordOnly:checked").val() !== undefined) { + // params.wholeWordOnly = jQuery("#wholeWordOnly:checked").val(); + //} + + jQuery("[name='mappings']:checked").each(function() { + mappings.push(jQuery(this).val()); + }); + params.mappings = mappings; + + if (jQuery("#semantic_types").val() !== null) { + params.semantic_types = jQuery("#semantic_types").val(); + annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, true); + jQuery("#results_error").html("Only results from ontologies with semantic types available are displayed."); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, false); + } + + if (jQuery("#semantic_groups").val() !== null) { + params.semantic_groups = jQuery("#semantic_groups").val(); + annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, true); + jQuery("#results_error").html("Only results from ontologies with semantic types available are displayed."); + } else { + annotationsTable.fnSetColumnVis(BP_COLUMNS.sem_types, false); + } + + params["recognizer"] = jQuery("#recognizer").val(); + + jQuery.ajax({ + type: "POST", + url: "/annotatorplus", // Call back to the UI annotation_controller::create method + data: params, + dataType: "json", + success: function(data) { + set_last_params(params); + display_annotations(data, bp_last_params); + jQuery(".annotator_spinner").hide(200); + jQuery("#annotations_container").show(300); + }, + error: function(data) { + set_last_params(params); + jQuery(".annotator_spinner").hide(200); + jQuery("#annotations_container").hide(); + jQuery("#annotator_error").html(" Problem getting annotations, please try again"); + } + }); +} // get_annotations + +var displayFilteredColumnNames = function() { + "use strict"; + var column_names = []; + var header_text; + jQuery(".bp_popup_list input:checked").closest("th").each(function() { + header_text = this.childNodes[0].textContent.trim(); + column_names.push(header_text); + }); + jQuery("#filter_names").html(column_names.join(", ")); + if (column_names.length > 0) { + jQuery("#filter_list").show(); + } else { + jQuery("#filter_list").hide(); + } +}; + +function createFilterCheckboxes(filter_items, checkbox_class, checkbox_location) { + "use strict"; + var for_sort = [], + sorted = []; + + // Sort ontologies by number of results + jQuery.each(filter_items, function(k, v) { + for_sort.push({ + label: k + " (" + v + ")", + count: v, + value: k, + value_encoded: encodeURIComponent(k) + }); + }); + for_sort.sort(function(a, b) { + return jQuery.trim(a.label) > jQuery.trim(b.label) + }); + + // Create checkboxes for ontology filter + jQuery.each(for_sort, function() { + var checkbox = jQuery("<input/>").attr("class", checkbox_class).attr("type", "checkbox").attr("value", this.value).attr("id", checkbox_class + this.value_encoded); + var label = jQuery("<label/>").attr("for", checkbox_class + this.value_encoded).html(" " + this.label); + sorted.push(jQuery("<span/>").append(checkbox).append(label).html()); + }); + jQuery("#" + checkbox_location).html(sorted.join("<br/>")); +} + +var filter_ontologies = { + init: function() { + "use strict"; + jQuery("#filter_ontologies").bind("click", function(e) { + bp_popup_init(e) + }); + // Need to use bind to avoid "live" propogation issues + jQuery(".filter_ontology_checkboxes").bind("click", function(e) { + filter_ontologies.filterOntology(e) + }); + jQuery("#ontology_filter_list").click(function(e) { + e.stopPropagation() + }); + this.cleanup(); + }, + + cleanup: function() { + "use strict"; + jQuery("html").click(bp_popup_cleanup); + jQuery(document).keyup(function(e) { + if (e.keyCode == 27) { + bp_popup_cleanup(); + } // esc + }); + }, + + filterOntology: function(e) { + "use strict"; + e.stopPropagation(); + var search_regex = []; + jQuery(".filter_ontology_checkboxes:checked").each(function() { + search_regex.push(jQuery(this).val()); + }); + displayFilteredColumnNames(); + if (search_regex.length === 0) { + annotationsTable.fnFilter("", BP_COLUMNS.ontologies); + } else { + annotationsTable.fnFilter(search_regex.join("|"), BP_COLUMNS.ontologies, true, false); + } + } +}; + +var filter_classes = { + init: function() { + "use strict"; + jQuery("#filter_classes").bind("click", function(e) { + bp_popup_init(e) + }); + // Need to use bind to avoid "live" propogation issues + jQuery(".filter_classes_checkboxes").bind("click", function(e) { + filter_classes.filterClasses(e) + }); + jQuery("#classes_filter_list").click(function(e) { + e.stopPropagation() + }); + this.cleanup(); + }, + + cleanup: function() { + "use strict"; + jQuery("html").click(bp_popup_cleanup); + jQuery(document).keyup(function(e) { + if (e.keyCode == 27) { + bp_popup_cleanup(); + } // esc + }); + }, + + filterClasses: function(e) { + "use strict"; + e.stopPropagation(); + var search_regex = []; + jQuery(".filter_classes_checkboxes:checked").each(function() { + // Escape characters used in regex + search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); + }); + displayFilteredColumnNames(); + if (search_regex.length === 0) { + annotationsTable.fnFilter("", BP_COLUMNS.classes); + } else { + annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.classes, true, false); + } + } +}; + +var filter_matched_ontologies = { + init: function() { + "use strict"; + jQuery("#filter_matched_ontologies").bind("click", function(e) { + bp_popup_init(e); + }); + // Need to use bind to avoid "live" propogation issues + jQuery(".filter_matched_ontology_checkboxes").bind("click", function(e) { + filter_matched_ontologies.filter(e); + }); + jQuery("#ontology_matched_filter_list").click(function(e) { + e.stopPropagation(); + }); + this.cleanup(); + }, + + cleanup: function() { + "use strict"; + jQuery("html").click(bp_popup_cleanup); + jQuery(document).keyup(function(e) { + if (e.keyCode == 27) { + bp_popup_cleanup(); + } // esc + }); + }, + + filter: function(e) { + "use strict"; + e.stopPropagation(); + var search_regex = []; + jQuery(".filter_matched_ontology_checkboxes:checked").each(function() { + search_regex.push(jQuery(this).val()); + }); + displayFilteredColumnNames(); + if (search_regex.length === 0) { + annotationsTable.fnFilter("", BP_COLUMNS.matched_ontologies); + } else { + annotationsTable.fnFilter(search_regex.join("|"), BP_COLUMNS.matched_ontologies, true, false); + } + } +}; + +var filter_matched_classes = { + init: function() { + "use strict"; + jQuery("#filter_matched_classes").bind("click", function(e) { + bp_popup_init(e) + }); + // Need to use bind to avoid "live" propogation issues + jQuery(".filter_matched_classes_checkboxes").bind("click", function(e) { + filter_matched_classes.filter(e) + }); + jQuery("#matched_classes_filter_list").click(function(e) { + e.stopPropagation() + }); + this.cleanup(); + }, + + cleanup: function() { + "use strict"; + jQuery("html").click(bp_popup_cleanup); + jQuery(document).keyup(function(e) { + if (e.keyCode == 27) { + bp_popup_cleanup(); + } // esc + }); + }, + + filter: function(e) { + "use strict"; + e.stopPropagation(); + var search_regex = []; + jQuery(".filter_matched_classes_checkboxes:checked").each(function() { + // Escape characters used in regex + search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); + }); + displayFilteredColumnNames(); + if (search_regex.length === 0) { + annotationsTable.fnFilter("", BP_COLUMNS.matched_classes); + } else { + annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.matched_classes, true, false); + } + } +}; + +var filter_match_type = { + init: function() { + "use strict"; + jQuery("#filter_match_type").bind("click", function(e) { + bp_popup_init(e) + }); + // Need to use bind to avoid "live" propogation issues + jQuery(".filter_match_type_checkboxes").bind("click", function(e) { + filter_match_type.filterMatchType(e) + }); + jQuery("#match_type_filter_list").click(function(e) { + e.stopPropagation() + }); + this.cleanup(); + }, + + cleanup: function() { + "use strict"; + jQuery("html").click(bp_popup_cleanup); + jQuery(document).keyup(function(e) { + if (e.keyCode == 27) { + bp_popup_cleanup(); + } // esc + }); + }, + + filterMatchType: function(e) { + "use strict"; + e.stopPropagation(); + var search_regex = []; + jQuery(".filter_match_type_checkboxes:checked").each(function() { + // Escape characters used in regex + search_regex.push(jQuery(this).val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); + }); + displayFilteredColumnNames(); + if (search_regex.length === 0) { + annotationsTable.fnFilter("", BP_COLUMNS.types); + } else { + annotationsTable.fnFilter("^" + search_regex.join("(?!.)|^") + "(?!.)", BP_COLUMNS.types, true, false); + } + } +}; + +var removeFilters = function() { + "use strict"; + jQuery(".filter_ontology_checkboxes").attr("checked", false); + jQuery(".filter_classes_checkboxes").attr("checked", false); + jQuery(".filter_match_type_checkboxes").attr("checked", false); + jQuery(".filter_matched_classes_checkboxes").attr("checked", false); + jQuery(".filter_matched_ontologies_checkboxes").attr("checked", false); + annotationsTable.fnFilter("", BP_COLUMNS.classes); + annotationsTable.fnFilter("", BP_COLUMNS.ontologies); + annotationsTable.fnFilter("", BP_COLUMNS.types); + annotationsTable.fnFilter("", BP_COLUMNS.matched_classes); + annotationsTable.fnFilter("", BP_COLUMNS.matched_ontologies); + jQuery("#filter_list").hide(); +}; + +// Datatables reset sort extension +jQuery.fn.dataTableExt.oApi.fnSortNeutral = function(oSettings) { + "use strict"; + /* Remove any current sorting */ + oSettings.aaSorting = []; + /* Sort display arrays so we get them in numerical order */ + oSettings.aiDisplay.sort(function(x, y) { + return x - y; + }); + oSettings.aiDisplayMaster.sort(function(x, y) { + return x - y; + }); + /* Redraw */ + oSettings.oApi._fnReDraw(oSettings); +}; + +function annotatorFormatLink(param_string, format) { + "use strict"; + // TODO: Check whether 'text' and 'tabDelimited' could work. + // For now, assume that json and xml will work or should work. + var format_map = { + "json": "JSON", + "rdf": "RDF", + "xml": "XML", + "text": "Text", + "tabDelimited": "CSV", + "quaero": "QUAERO", + "brat": "BRAT" + }; + var query = BP_CONFIG.proxy_url + "/annotatorplus?apikey=" + BP_CONFIG.apikey + "&" + param_string; + if (format !== 'json') { + query += "&format=" + format; + } + // var link = "<a href=\"" + encodeURI(query) + "\" class=\"btn btn-default btn-sm\" target=\"_blank\">" + format_map[format] + "</a>"; + var link = "<a href=\"" + encodeURI(query) + "\" class = \"btn btn-outline-primary btn-sm\" target=\"_blank\">" + format_map[format] + "</a>"; + jQuery("#download_links_" + format.toLowerCase()).html(link); +} + +function generateParameters() { + "use strict"; + var params = []; + var new_params = jQuery.extend(true, {}, bp_last_params); // deep copy + delete new_params["apikey"]; + delete new_params["format"]; + delete new_params["raw"]; + //console.log(new_params); + jQuery.each(new_params, function(k, v) { + if (v !== null && v !== undefined) { + if (typeof v == "boolean") { + params.push(k + "=" + v); + } else if (typeof v == "string" && v.length > 0) { + params.push(k + "=" + v); + } else if (typeof v == "array" && v.length > 0) { + params.push(k + "=" + v.join(',')); + } else if (typeof v == "object" && v.length > 0) { + params.push(k + "=" + v.join(',')); + } + } + }); + return params.join("&"); +} + +jQuery(document).ready(function() { + "use strict"; + jQuery("#annotator_button").click(get_annotations); + + jQuery("#semantic_types").select2({ + allowClear: true, + dropdownParent: jQuery(".annotatorplus form") + }); + + jQuery("#semantic_groups").select2({ + allowClear: true, + dropdownParent: jQuery(".annotatorplus form") + }); + + jQuery("#ontology_ontologyId").select2({ + allowClear: true, + dropdownParent: jQuery(".annotatorplus form") + }); + + jQuery("#insert_text_link").click(insertSampleText); + + jQuery("#advancedOptionsLink").click(toggle_advanced_options); + jQuery("#advanced-options-container").hide(); + + // Init annotation table + annotationsTable = jQuery("#annotations").dataTable({ + bPaginate: false, + bAutoWidth: false, + aaSorting: [], + oLanguage: { + sZeroRecords: "No annotations found" + }, + "aoColumns": [{ + // Class column + "sWidth": "15%" + }, { + // Ontology column + "sWidth": "15%" + }, { + // Type column + "sWidth": "5%" + }, { + // column not displayed + "sWidth": "5%", + "bVisible": false + }, { + // Context column + "sWidth": "20%" + }, { + // Matched class column + "sWidth": "15%" + }, { + // matchedOntology column + "sWidth": "15%" + }, { + // Score column + "sWidth": "5%", + "bVisible": false + }, { + // Negation column + "sWidth": "5%", + "bVisible": false + }, { + // Experiencer column + "sWidth": "5%", + "bVisible": false + }, { + // Temporality column + "sWidth": "5%", + "bVisible": false + }] + }); + filter_ontologies.init(); + filter_classes.init(); + filter_match_type.init(); + filter_matched_ontologies.init(); + filter_matched_classes.init(); + + jQuery("#annotator-help").on("click", bpPopWindow); + + jQuery("#annotations_container").hide(); +}); // doc ready + +function get_link(uri, label) { + "use strict"; + return '<a href="' + uri + '">' + label + '</a>'; +} + +function get_class_details(cls) { + var + cls_rel_ui = cls.ui.replace(/^.*\/\/[^\/]+/, ''), + ont_rel_ui = cls_rel_ui.replace(/\?p=classes.*$/, '?p=summary'); + return class_details = { + cls_rel_ui: cls_rel_ui, + ont_rel_ui: ont_rel_ui, + cls_link: get_link(cls_rel_ui, cls.prefLabel), + ont_link: get_link(ont_rel_ui, cls.ontology.name), + semantic_types: cls.semantic_types.join('; ') // test with 'abscess' text and sem type = T046,T020 + } +} + +function get_class_details_from_raw(cls) { + var + ont_acronym = cls.links.ontology.replace(/.*\//, ''), + ont_name = annotator_ontologies[cls.links.ontology].name, + ont_rel_ui = '/ontologies/' + ont_acronym, + ont_link = null; + if (ont_name === undefined) { + ont_link = get_link_for_ont_ajax(ont_acronym); + } else { + ont_link = get_link(ont_rel_ui, ont_name); // no ajax required! + } + var + cls_rel_ui = cls.links.ui.replace(/^.*\/\/[^\/]+/, ''), + cls_label = cls.prefLabel, + cls_link = null; + if (cls_label === undefined) { + cls_link = get_link_for_cls_ajax(cls['@id'], ont_acronym); + } else { + cls_link = get_link(cls_rel_ui, cls_label); // no ajax required! + } + return class_details = { + cls_rel_ui: cls_rel_ui, + ont_rel_ui: ont_rel_ui, + cls_link: cls_link, + ont_link: ont_link, + // + // TODO: Get semantic types from raw data, currently provided by controller. + //semantic_types: cls.semantic_types.join('; ') // test with 'abscess' text and sem type = T046,T020 + semantic_types: '' + } +} + +function get_text_markup(text, from, to) { + var + text_match = text.substring(from - 1, to), + // remove everything prior to the preceding three words (using space delimiters): + text_prefix = text.substring(0, from - 1).replace(/.* ((?:[^ ]* ){2}[^ ]*$)/, "... $1"), + // remove the fourth space and everything following it + text_suffix = text.substring(to).replace(/^((?:[^ ]* ){3}[^ ]*) [\S\s]*/, "$1 ..."), + match_span = '<span style="color: rgb(153,153,153);">', + match_markup_span = '<span style="color: rgb(35, 73, 121); font-weight: bold; padding: 2px 0px;">', + text_markup = match_markup_span + text_match + "</span>"; + //console.log('text markup: ' + text_markup); + return match_span + text_prefix + text_markup + text_suffix + "</span>"; +} + +function get_annotation_rows(annotation, params) { + "use strict"; + // data independent var declarations + var + rows = [], + cells = [], + text_markup = '', + match_type = '', + match_type_translation = { + "mgrep": "direct", + "mapping": "mapping", + "closure": "ancestor" + }; + // data dependent var declarations + var cls = get_class_details(annotation.annotatedClass); + jQuery.each(annotation.annotations, function(i, a) { + text_markup = get_text_markup(params.text, a.from, a.to); + match_type = match_type_translation[a.matchType.toLowerCase()] || 'direct'; + cells = [cls.cls_link, cls.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; + rows.push(cells); + // Add rows for any classes in the hierarchy. + match_type = 'ancestor'; + var h_c = null; + jQuery.each(annotation.hierarchy, function(i, h) { + h_c = get_class_details(h.annotatedClass); + cells = [h_c.cls_link, h_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; + rows.push(cells); + }); // hierarchy loop + // Add rows for any classes in the mappings. Note the ont_link will be different. + match_type = 'mapping'; + var m_c = null; + jQuery.each(annotation.mappings, function(i, m) { + m_c = get_class_details(m.annotatedClass); + cells = [m_c.cls_link, m_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link]; + rows.push(cells); + }); // mappings loop + }); // annotations loop + return rows; +} + +function get_annotation_score(cls) { + var score = ''; + if (typeof cls.score !== 'undefined') { + score = parseFloat(cls.score).toFixed(3); + } + return score; +} + +/** + * Set context value (negation, temporality or experienced) to empty string to avoid useless dataTable warning + * @param context + * @returns {*} + */ +function get_context_value(context) { + if (typeof context === 'undefined') { + return ''; + } + return context; +} + +function get_annotation_rows_from_raw(annotation, params) { + "use strict"; + // data independent var declarations + var + rows = [], + cells = [], + text_markup = '', + match_type = '', + match_type_translation = { + "mgrep": "direct", + "mapping": "mapping", + "closure": "ancestor" + }; + // data dependent var declarations + var cls = get_class_details_from_raw(annotation.annotatedClass); + if (annotation.annotations.length == 0) { + cells = [cls.cls_link, cls.ont_link, "", cls.semantic_types, "", cls.cls_link, cls.ont_link, get_annotation_score(annotation)]; + rows.push(cells); + } else { + jQuery.each(annotation.annotations, function(i, a) { + text_markup = get_text_markup(params.text, a.from, a.to); + match_type = match_type_translation[a.matchType.toLowerCase()] || 'direct'; + cells = [cls.cls_link, cls.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(annotation), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; + rows.push(cells); + // Add rows for any classes in the hierarchy. + match_type = 'ancestor'; + var h_c = null; + jQuery.each(annotation.hierarchy, function(i, h) { + h_c = get_class_details_from_raw(h.annotatedClass); + cells = [h_c.cls_link, h_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(h), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; + rows.push(cells); + }); // hierarchy loop + // Add rows for any classes in the mappings. Note the ont_link will be different. + match_type = 'mapping'; + var m_c = null; + jQuery.each(annotation.mappings, function(i, m) { + m_c = get_class_details_from_raw(m.annotatedClass); + cells = [m_c.cls_link, m_c.ont_link, match_type, cls.semantic_types, text_markup, cls.cls_link, cls.ont_link, get_annotation_score(m), get_context_value(a.negationContext), get_context_value(a.experiencerContext), get_context_value(a.temporalityContext)]; + rows.push(cells); + }); // mappings loop + }); // annotations loop + } + return rows; +} + +function update_annotations_table(rowsArray) { + "use strict"; + var ontologies = {}, + classes = {}, + match_types = {}, + matched_ontologies = {}, + matched_classes = {}, + context_count = 0; + + jQuery(rowsArray).each(function() { + // [ cls_link, ont_link, match_type, semantic_types, text_markup, cls_link, ont_link ]; + var row = this, + cls_link = row[0], + ont_link = row[1], + match_type = row[2], // direct, ancestors, mapping + //semantic_type = row[3], + //match_text = row[4], + match_cls_link = row[5], + match_ont_link = row[6]; + // Extract labels from links (using non-greedy regex). + var cls_label = cls_link.replace(/^<a.*?>/, '').replace('</a>', '').toLowerCase(), + ont_label = ont_link.replace(/^<a.*?>/, '').replace('</a>', ''), + match_cls_label = match_cls_link.replace(/^<a.*?>/, '').replace('</a>', '').toLowerCase(), + match_ont_label = match_ont_link.replace(/^<a.*?>/, '').replace('</a>', ''); + + // TODO: Gather sem types for display + // var semantic_types = []; + // jQuery.each(annotation.concept.semantic_types, function () { + // semantic_types.push(this.description); + // }); + + // Keep track of contexts. If there are none (IE when using mallet), hide the column + if (row[4] !== "") context_count++; + + // Keep track of how many results are associated with each ontology + ontologies[ont_label] = (ont_label in ontologies) ? ontologies[ont_label] + 1 : 1; + // Keep track of how many results are associated with each class + classes[cls_label] = (cls_label in classes) ? classes[cls_label] + 1 : 1; + // Keep track of match types + match_types[match_type] = (match_type in match_types) ? match_types[match_type] + 1 : 1; + // Keep track of matched classes + matched_classes[match_cls_label] = (match_cls_label in matched_classes) ? matched_classes[match_cls_label] + 1 : 1; + // Keep track of matched ontologies + matched_ontologies[match_ont_label] = (match_ont_label in matched_ontologies) ? matched_ontologies[match_ont_label] + 1 : 1; + }); + + // Add result counts + var count_span = '<span class="result_count">' + jQuery("#result_counts").html("total results " + count_span + rowsArray.length + "</span> "); + var direct_count = ("direct" in match_types) ? match_types["direct"] : 0, + ancestor_count = ("ancestor" in match_types) ? match_types["ancestor"] : 0, + mapping_count = ("mapping" in match_types) ? match_types["mapping"] : 0; + jQuery("#result_counts").append("("); + jQuery("#result_counts").append("direct " + count_span + direct_count + "</span>"); + jQuery("#result_counts").append(" / " + "ancestor " + count_span + ancestor_count + "</span>"); + jQuery("#result_counts").append(" / " + "mapping " + count_span + mapping_count + "</span>"); + jQuery("#result_counts").append(")"); + + // Add checkboxes to filters + createFilterCheckboxes(ontologies, "filter_ontology_checkboxes", "ontology_filter_list"); + createFilterCheckboxes(classes, "filter_classes_checkboxes", "classes_filter_list"); + createFilterCheckboxes(match_types, "filter_match_type_checkboxes", "match_type_filter_list"); + createFilterCheckboxes(matched_ontologies, "filter_matched_ontology_checkboxes", "matched_ontology_filter_list"); + createFilterCheckboxes(matched_classes, "filter_matched_classes_checkboxes", "matched_classes_filter_list"); + + // Reset table + annotationsTable.fnClearTable(); + annotationsTable.fnSortNeutral(); + removeFilters(); + + // Need to re-init because we're not using "live" because of propagation issues + filter_ontologies.init(); + filter_classes.init(); + filter_match_type.init(); + filter_matched_ontologies.init(); + filter_matched_classes.init(); + + // Add data + if (rowsArray.length > 0) { + annotationsTable.fnAddData(rowsArray); + } + + // Hide columns as necessary + if (context_count == 0) { + annotationsTable.fnSetColumnVis(4, false); + } else { + annotationsTable.fnSetColumnVis(4, true); + } + + var match_keys = Object.keys(match_types); + if (match_keys.length == 1 && match_keys[0] === "") + annotationsTable.fnSetColumnVis(2, false); +} + +function display_annotations(data, params) { + "use strict"; + var annotations = data.annotations; + var all_rows = []; + if (params.raw !== undefined && params.raw === true) { + // The annotator_controller does not 'massage' the REST data. + // The class prefLabel and ontology name must be resolved with ajax. + annotator_ontologies = data.ontologies; + for (var i = 0; i < annotations.length; i++) { + all_rows = all_rows.concat(get_annotation_rows_from_raw(annotations[i], params)); + } + } else { + // The annotator_controller does 'massage' the REST data. + // The class prefLabel and ontology name get resoled with a batch all in the controller. + for (var i = 0; i < annotations.length; i++) { + all_rows = all_rows.concat(get_annotation_rows(annotations[i], params)); + } + } + update_annotations_table(all_rows); + // Generate parameters for list at bottom of page + var param_string = generateParameters(); // uses bp_last_param + var query = BP_CONFIG.proxy_url + "/annotatorplus?" + param_string + "&display_links=false&display_context=false"; + var query_encoded = BP_CONFIG.proxy_url + "/annotatorplus?" + encodeURIComponent(param_string); + jQuery("#annotator_parameters").html("<a href=\"" + encodeURI(query) + "\" target=\"_blank\">corresponding REST API call</a>"); + jQuery("#annotator_parameters_encoded").html(query_encoded); + // Add links for downloading results + //annotatorFormatLink("tabDelimited"); + annotatorFormatLink(param_string, "json"); + annotatorFormatLink(param_string, "xml"); + annotatorFormatLink(param_string, "brat"); + annotatorFormatLink(param_string, "quaero"); + annotatorFormatLink(param_string, "rdf"); + + if (params.raw !== undefined && params.raw === true) { + // Initiate ajax calls to resolve class ID to prefLabel and ontology acronym to name. + ajax_process_init(); // see bp_ajax_controller.js + } +} + +function toggle_advanced_options() { + $("#advanced-options-container").toggle(); + var text = $("#advanced-options-container").is(':visible') ? "Hide advanced options <<" : "Show advanced options >>"; + $("#advancedOptionsLink").text(text); +} + +// Creates an HTML form with a button that will POST to the annotator +//function annotatorPostForm(format) { +// "use strict"; +// // TODO: Check whether 'text' and 'tabDelimited' could work. +// // For now, assume that json and xml will work or should work. +// var format_map = { "json": "JSON", "xml": "XML", "text": "Text", "tabDelimited": "CSV" }; +// var params = bp_last_params; +// params["format"] = format; +// var form_fields = []; +// jQuery.each(params, function (k, v) { +// if (v != null) { +// form_fields.push("<input type='hidden' name='" + k + "' value='" + v + "'>"); +// } +// }); +// var action = "action='" + BP_CONFIG.rest_url + "/annotator'"; +// var form = jQuery("<form " + action + " method='post' target='_blank'/>") +// .append(form_fields.join("")) +// .append("<input type='submit' value='" + format_map[format] + "'>"); +// jQuery("#download_links_" + format.toLowerCase()).html(form); +//} diff --git a/app/views/annotator/index.html.haml b/app/views/annotator/index.html.haml index 6c12a1f55..557202739 100644 --- a/app/views/annotator/index.html.haml +++ b/app/views/annotator/index.html.haml @@ -9,8 +9,9 @@ %h2.mt-3 Annotator %p = t('annotator.index.intro', site: $SITE).html_safe - = link_to(help_path(anchor: "Annotator_Tab"), id: "annotator-help", target: "_blank") do - %i.fa.fa-question-circle.fa-lg{"aria-hidden": "true"} + = link_to(Rails.configuration.settings.links[:help_annotator], target: '_blank') do + %i.fas.fa-question-circle.fa-lg + %div.row %div.col diff --git a/app/views/mappings/_concept_mappings_selector.html.haml b/app/views/mappings/_concept_mappings_selector.html.haml index 19b8bafae..94f98e3aa 100644 --- a/app/views/mappings/_concept_mappings_selector.html.haml +++ b/app/views/mappings/_concept_mappings_selector.html.haml @@ -3,7 +3,7 @@ %h2.mb-0 %button.btn.btn-link.btn-block.text-left.collapsed{"data-target" => "#collapseTwo", "data-toggle" => "collapse", :type => "button"} Find mappings of a class/concept - = link_to(Rails.configuration.settings.links[:mappings], id: "mappings-help", "aria-label": "View mappings help") do + = link_to(Rails.configuration.settings.links[:help_mappings], target: '_blank') do %i.fas.fa-question-circle.fa-lg{"aria-hidden": "true"} #collapseTwo.collapse{"data-parent" => "#accordionExample"} .card-body diff --git a/app/views/mappings/_ontology_mappings.html.haml b/app/views/mappings/_ontology_mappings.html.haml index 357784b8e..43f028f06 100644 --- a/app/views/mappings/_ontology_mappings.html.haml +++ b/app/views/mappings/_ontology_mappings.html.haml @@ -3,7 +3,7 @@ %h2.mb-0 %button.btn.btn-link.btn-block.text-left{"data-target" => "#collapseOne", "data-toggle" => "collapse", :type => "button"} = t('mappings.intro').html_safe - = link_to(Rails.configuration.settings.links[:mappings], id: "mappings-help", "aria-label": "View mappings help") do + = link_to(Rails.configuration.settings.links[:help_mappings], target: '_blank') do %i.fas.fa-question-circle.fa-lg{"aria-hidden": "true"} #collapseOne.collapse{"data-parent" => "#accordionExample"} .card-body diff --git a/app/views/ontologies/browse.html.erb b/app/views/ontologies/browse.html.erb index c35fdd01c..13173fa74 100644 --- a/app/views/ontologies/browse.html.erb +++ b/app/views/ontologies/browse.html.erb @@ -11,8 +11,7 @@ <h1 style="font-size: 3em;">Browse</h1> <p> <%= t('ontologies.intro').html_safe %> - <%= link_to(Rails.configuration.settings.links[:help_ontology_browse], id: 'ontology-browse-help', - 'aria-label': 'View ontology browse help') do %> + <%= link_to(Rails.configuration.settings.links[:help_ontology_browse], target: '_blank') do %> <i class='fas fa-question-circle fa-lg' aria-hidden='true'></i> <% end %> </p> diff --git a/app/views/recommender/index.html.haml b/app/views/recommender/index.html.haml index 7abac68a6..8c0b3d470 100644 --- a/app/views/recommender/index.html.haml +++ b/app/views/recommender/index.html.haml @@ -3,9 +3,8 @@ %h2.mt-5 Ontology Recommender %p = t('recommender.intro').html_safe - = link_to(Rails.configuration.settings.links[:help_recommender], id: "recommender-help", - "aria-label": "View ontology recommender help") do - %i.fas.fa-question-circle.fa-lg{"aria-hidden": "true"} + = link_to(Rails.configuration.settings.links[:help_annotator], target: '_blank') do + %i.fas.fa-question-circle.fa-lg %form = hidden_field_tag :recommender_sample_text, t('recommender.sample_text') @@ -16,16 +15,16 @@ %input#radioItText.form-check-input{name: "input_type", type: "radio", value: "1", checked: "checked"} %label.form-check-label{for: "radioItText"} Text %div.form-check.form-check-inline - %input#radioItKeywords.form-check-input{name: "input_type", type: "radio", value: "2"} + %input#radioItKeywords.form-check-input{name: "input_type", type: "radio", value: "2"} %label.form-check-label{for: "radioItKeywords"} Keywords (separated by commas) -# Specify output format %h5.pt-3 Output %div.form-check.form-check-inline - %input#radioOtSingle.form-check-input{name: "output_type", type: "radio", value: "1", checked: "checked"} + %input#radioOtSingle.form-check-input{name: "output_type", type: "radio", value: "1", checked: "checked"} %label.form-check-label{for: "radioOtSingle"} Ontologies %div.form-check.form-check-inline - %input#radioOtSets.form-check-input{name: "output_type", type: "radio", value: "2"} + %input#radioOtSets.form-check-input{name: "output_type", type: "radio", value: "2"} %label.form-check-label{for: "radioOtSets"} Ontology sets -# Input text or keywords diff --git a/app/views/search/index.html.haml b/app/views/search/index.html.haml index 5235f869e..076c8da46 100644 --- a/app/views/search/index.html.haml +++ b/app/views/search/index.html.haml @@ -9,7 +9,7 @@ = text_field_tag("search_keywords", nil, class: "form-control", aria: {describedby: "classSearchHelpBlock"}) %small#classSearchHelpBlock.form-text.text-muted = t(".search_keywords_placeholder") - = link_to("help", Rails.configuration.settings.links[:help_search], id: "search-help", + = link_to("help", Rails.configuration.settings.links[:help_search], target: '_blank', aria: {label: "View search documentation"}, class: "float-right") %div.form-group = link_to("Show advanced options", "javascript:void(0)", id: "advanced_options", data: {text_swap: "Hide advanced options"}, class: "form-text") diff --git a/config/locales/en.yml b/config/locales/en.yml index b37ebbb55..f9cb4e16b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -91,30 +91,37 @@ en: home: index: - find_ontology_placeholder: Start typing ontology name, then choose from list - query_placeholder: Enter a class, e.g. Melanoma - tagline: the world's most comprehensive repository of biomedical ontologies + find_ontology_placeholder: Start typing resource name, then choose from list + query_placeholder: Enter a class, e.g. Shape + tagline: the LifeWatch ERIC repository of semantic resources for ecology and related domains title: Welcome to the %{organization} welcome: Welcome to %{site}, help: - welcome: Welcome to the National Center for Biomedical Ontology’s %{site}. %{site} is a web-based application for accessing and sharing biomedical ontologies. + welcome: Welcome to the LifeWatch ERIC’s %{site}. %{site} is a repository of semantic resources for ecology and related domains. getting_started: > - %{site} allows users to browse, upload, download, search, comment on, and create mappings for ontologies. + %{site} supports the community in the creation, management, mapping and alignment of its semantic resources and subsequently also of its data. browse: > - Users can browse and explore individual ontologies by navigating either a tree structure or an animated graphical view. Users can also view mappings and - ontology metadata, and download ontologies. Additionally, users who are signed in may submit a new ontology to the library. - rest_examples_html: View documentation and examples of the <a href="http://data.bioontology.org/documentation" target="_blank">%{site} REST API</a>. + Users can browse and explore individual semantic resources by navigating either a tree structure or + an animated graphical view. Users can also view mappings and semantic resource metadata, and download + semantic resources. Additionally, users who are signed in may also submit a new semantic resource to the + library. All submissions to the library are reviewed. + documentation_html: > + General documentation is available on <a href="https://github.com/lifewatch-eric/documentation/wiki" target="_blank">GitHub</a> + rest_examples_html: View documentation and examples of the <a href="https://data.ecoportal.lifewatch.eu/documentation" target="_blank">%{site} REST API</a>. announce_list_html: > To receive notices of new releases or site outages, please subscribe to the <a href="https://mailman.stanford.edu/mailman/listinfo/bioontology-support" target="_blank">bioontology-support list</a>. - + contact_html: > + If you have any comment or need for support, please email <b>service.centre@lifewatch.eu</b>. + If you are interested and would like to join our information mailing list, please subscribe to our user mailing + list. annotator: index: - intro: Get annotations for biomedical text with classes from the ontologies + intro: Get annotations for ecological text with classes from the semantic resources annotatorplus_html: <em>Check out <a href="%{annotatorplus_href}">AnnotatorPlus</a> beta; a new version of the Annotator with added support for negation, and more!</em> fast_context: tooltip: "Enable FastContext to detect : if a concept has been negated (affirmed, negated), who experienced the found concept (patient, other), when the annotated concept occurred (recent, historical, hypothetical), and/or if the annotated concept is uncertain (certain, uncertain)." - sample_text: Melanoma is a malignant tumor of melanocytes which are found predominantly in skin but also in the bowel and the eye. + sample_text: Invasive alien species are plants, animals, pathogens and other organisms that are non-native to an ecosystem, and which may cause economic or environmental harm or adversely affect human health. nbco_annotatosplus: index: @@ -125,15 +132,16 @@ en: </br><br/> If using the API, please provide a valid NCBO BioPortal apikey and hit the service at <a href="http://services.bioportal.lirmm.fr/ncbo_annotatorplus">http://services.bioportal.lirmm.fr/ncbo_annotatorplus</a></br> Text submitted to the NCBO Annotator+ must be in English. - sample_text: Melanoma is a malignant tumor of melanocytes which are found predominantly in skin but also in the bowel and the eye. + sample_text: Invasive alien species are plants, animals, pathogens and other organisms that are non-native to an ecosystem, and which may cause economic or environmental harm or adversely affect human health. recommender: - intro: Get recommendations for the most relevant ontologies based on an excerpt from a biomedical text or a list of keywords - + intro: Get recommendations for the most relevant semantic resources based on an excerpt from a ecological text or a list of keywords + sample_text: Ecological attributes (=ecological characteristics) are features of a taxon such as its range size, life history, geographical distribution, climatic tolerances, habitat types, environmental indicator values, edaphic preferences, and so on, that describe the overall ecology of the taxon. + sample_keywords: Ecological attributes (=ecological characteristics) are features of a taxon such as its range size, life history, geographical distribution, climatic tolerances, habitat types, environmental indicator values, edaphic preferences, and so on, that describe the overall ecology of the taxon. search: index: - intro: Search for a class in multiple ontologies - search_keywords_placeholder: Enter a class, e.g. Melanoma + intro: Search for a class in multiple semantic resources + search_keywords_placeholder: Enter a class, e.g. Shape categories_placeholder: Start typing to select categories or leave blank to use all property_definition: Named association between two entities. Examples are "definition" (a relation between a class and some text) and "part-of" (a relation between two classes). obsolete_definition: > @@ -145,7 +153,7 @@ en: intro: Browse a selection of projects that use %{site} resources ontologies: - intro: Browse the library of ontologies + intro: Browse the library of semantic resources concepts: request_term: @@ -167,5 +175,5 @@ en: <p>Justification (optional)<br>Provide any additional information about the requested term here.</p> </blockquote> mappings: - intro: Browse mappings between classes in different ontologies + intro: Browse mappings between classes in different semantic resources diff --git a/config/settings.yml b/config/settings.yml index 318afd9b8..d914e5f5d 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,5 +1,11 @@ shared: links: + help_annotator: 'https://github.com/lifewatch-eric/documentation/wiki/EcoTools#annotator' + help_mappings: 'https://github.com/lifewatch-eric/documentation/wiki/EcoTools#mappings' + help_ontology_browse: 'https://github.com/lifewatch-eric/documentation/wiki/EcoTools#browse' + help_recommender: 'https://github.com/lifewatch-eric/documentation/wiki/EcoTools#recommender' + help_search: 'https://github.com/lifewatch-eric/documentation/wiki/EcoTools#search' + metrics: 'https://www.bioontology.org/wiki/Ontology_Metrics' about: '/about' cite: '/citeus' facebook: 'https://www.facebook.com/LifeWatch-ERIC-245225669516847'