Skip to content

Commit

Permalink
fix #16, add instances table component (instances by ontolgy and clas…
Browse files Browse the repository at this point in the history
…s views) and instances controller
  • Loading branch information
syphax-bouazzouni committed Jan 18, 2022
1 parent aaccdad commit 5182c57
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 75 deletions.
2 changes: 2 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
//= require bp_recommender
//= require bp_property_tree
//= require home
//= require fair_score
//= require ontologies_instances
//= require ontologies
//= require projects
//= require submissions
Expand Down
18 changes: 17 additions & 1 deletion app/assets/javascripts/bp_class_tree.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function initClassTree() {
};

function nodeClicked(node_id) {

// Get current html and store data in cache (to account for changes since the cache was retrieved)
setCacheCurrent();

Expand Down Expand Up @@ -132,7 +133,8 @@ function nodeClicked(node_id) {
insertNotesTable(tabData["notes_table_data"]);

wrapupTabChange(selectedTab);
} else {
}
else {
jQuery.blockUI({ message: '<h1>' + '<%= image_tag src="jquery.simple.tree/spinner.gif" %>' + ' Loading Class...</h1>', showOverlay: false });
jQuery.get('/ajax_concepts/'+jQuery(document).data().bp.ont_viewer.ontology_id+'/?conceptid='+node_id+'&callback=load',
function(data){
Expand All @@ -154,6 +156,20 @@ function nodeClicked(node_id) {
}
);
}

//TODO: think of how to use the cache for instances
updateConceptInstances(getOntology() , node_id)

}

// Update instances table when a new concept in the tree view is clicked
function updateConceptInstances(ontology_acronym,node_id){
$("#class-instances-data-table").ready(function() {
let inst = InstancesTable.mount("#class-instances-data-table" , ontology_acronym , decodeURIComponent(node_id) )
inst.dataTable.on("xhr" , () => {
$("#instances_count").text(inst.dataTable.ajax.json().totalCount)
})
})
}

// Keep trying to put the tree view content in place (looks for #sd_content)
Expand Down
24 changes: 17 additions & 7 deletions app/assets/javascripts/bp_ontology_viewer.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
var paramValue = value.split("=")[1];
params[paramName] = paramValue;
});
} else {
}
else {
queryStringParams = window.location.search.substring(1).split("&");

jQuery(queryStringParams).each(function(index, value){
Expand All @@ -53,7 +54,8 @@

// We need to get everything using AJAX
content_section = null;
} else {
}
else {
var pane = jQuery(document).data().bp.ont_viewer.content_section;
showOntologyContent(pane);
document.title = jQuery.bioportal.ont_pages[pane].page_name + " | " + jQuery(document).data().bp.ont_viewer.org_site;
Expand Down Expand Up @@ -93,7 +95,8 @@ function displayTree(data) {
jQuery.unblockUI();
return;

} else {
}
else {

var new_concept_param = (typeof new_concept_id === 'undefined') ? "" : "&conceptid=" + new_concept_id;

Expand Down Expand Up @@ -200,10 +203,10 @@ function showOntologyContent(content_section) {

// Prevent the default behavior of clicking the ontology page links
// Instead, fire some history events
var nav_ont = function(link) {
var page = jQuery(link).attr("data-bp-ont-page");
History.pushState({p:page}, jQuery.bioportal.ont_pages[page].page_name + " | " + jQuery(document).data().bp.ont_viewer.org_site, "?p=" + page);
}
const nav_ont = function (link) {
let page = jQuery(link).attr("data-bp-ont-page");
History.pushState({p: page}, jQuery.bioportal.ont_pages[page].page_name + " | " + jQuery(document).data().bp.ont_viewer.org_site, "?p=" + page);
};

jQuery(document).ready(function() {
var metadata_only = jQuery(document).data().bp.ont_viewer.metadata_only;
Expand Down Expand Up @@ -281,6 +284,10 @@ jQuery(document).ready(function() {
jQuery.bioportal.ont_pages["notes"].retrieve();
}

if (content_section !== "instances" && metadataOnly !== "true") {
jQuery.bioportal.ont_pages["instances"].retrieve();
}

if (content_section !== "widgets" && metadataOnly !== "true") {
jQuery.bioportal.ont_pages["widgets"].retrieve();
}
Expand Down Expand Up @@ -398,6 +405,9 @@ jQuery.bioportal.OntologyPage = function(id, location_path, error_string, page_n
jQuery("#ont_notes_content .link_button").button();
});

jQuery.bioportal.ont_pages["instances"] = new jQuery.bioportal.OntologyPage("instances", "/ontologies/" + jQuery(document).data().bp.ont_viewer.ontology_id + "?p=instances&ajax=true", "Problem retrieving widgets", jQuery(document).data().bp.ont_viewer.ontology_name + " - Instances", "Instances");


jQuery.bioportal.ont_pages["widgets"] = new jQuery.bioportal.OntologyPage("widgets", "/ontologies/" + jQuery(document).data().bp.ont_viewer.ontology_id + "?p=widgets&ajax=true", "Problem retrieving widgets", jQuery(document).data().bp.ont_viewer.ontology_name + " - Widgets", "Widgets");
})(window);

Expand Down
197 changes: 197 additions & 0 deletions app/assets/javascripts/ontologies_instances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/**
* Get a label from an uri
* @param uri
* @returns {string}
*/
function getLabel(uri) {
let label = uri
if (isALink(uri)) { // is a link
let index = uri.toString().indexOf('#')
if (index > -1) {
label = uri.toString().substr(index + 1)
} else {
index = uri.toString().lastIndexOf('/')
if (index > -1)
label = uri.toString().substr(index + 1)
}
}
return label
}

function isALink(uri){
return uri.startsWith("http") || uri.startsWith("https")
}

function getInstanceDetailsFromURI(ontology , uri){
return new Promise((resolve , reject) => {
$.getJSON("/ajax/"+ontology+"/instances/"+ encodeURIComponent(uri))
.done((data) => resolve(data))
.fail((error)=> reject(error))
})

}

class InstancesTable{
constructor(tableElem , ontologyAcronym , classUri = ""){
this.tableElem = tableElem
this.ontologyAcronym = ontologyAcronym
this.classUri = classUri
this.dataTable = null
}

init(){
if ( $.fn.dataTable.isDataTable( this.tableElem ) ) {
$(this.tableElem).DataTable().destroy();

}
this.dataTable = $(this.tableElem).DataTable( {
"paging": true,
"pagingType": "full",
"info": false,
"searching": false,
"ordering": false,
"serverSide":true,
"processing": true,
"ajax": {
"url": this.#getAjaxUrl(),
"contentType": "application/json",
"dataSrc": (json) => {
json.recordsTotal = json["totalCount"];
json.recordsFiltered = json.recordsTotal
return json["collection"].map(x => [
x["@id"],
x["types"],
x["properties"]
])
},
"data": (d) => {
return {page: (d.start/d.length)+ 1 , pagesize: d.length}
}
},
"columnDefs": this.#render(),
"language": {
'loadingRecords': '&nbsp;',
'processing': `
<div class="spinner-border m-2" role="status">
<span class="sr-only">Loading...</span>
</div>
`
},
"createdRow": (row, data, dataIndex ) => {
$(row).click(() => this.#openPopUpDetail(data));
}

})
}

static mount(tableId , ontologyAcronym , conceptId){
if(tableId.toString().length>0){
const tableElm = document.querySelector(tableId)

if(tableElm){
const instanceTable = new InstancesTable( tableElm , ontologyAcronym , conceptId)
instanceTable.init()
return instanceTable
}
}
}

#render(){
const toLink = function (uri) {
return `<a id="${uri}" href="javascript:void(0)" title="${uri}">${getLabel(uri)}</a>`
}
const arr =["ID"];
if(!this.#isClassURISet()){
arr.push("Types")
}
return arr.map((x,i) => {
return {
"targets" : i ,
"title":x,
"render": function (data, type ,row ,meta){
if(typeof data === "string")
data = [data]
return data.map((x) => toLink(x)).join(',')
}
}
})
}

#getAjaxUrl(page= null , size = null) {
let url = "/ajax/"+ this.ontologyAcronym
let params = ["page="+page,"pagesize="+size].filter(x => x !== null).join("&")
if(this.#isClassURISet() > 0){
url += "/classes/" + encodeURIComponent(this.classUri)
}
url += "/instances" + (page!==null || size!=null ? "?"+params : "");
return url
}

#isClassURISet(){
return this.classUri.length > 0
}

#openPopUpDetail(data){
$.facebox(() => {
let uri = data[0]
let types = data[1]
let properties = data[2]

$.facebox( new InstanceDetails(this.ontologyAcronym, uri , types , properties).render().html())
})
}
}


class InstanceDetails{

constructor(ontology, uri , types , properties) {
this.uri = uri
this.types = types
this.properties = properties
this.ontology = ontology
}

render(){
console.log("render details")
console.log(this)
const toLink = function (uri, href) {
if(isALink(uri))
return `<a id="${uri}" href="${href}" title="${uri}" target="_blank">${getLabel(uri)}</a>`
else
return uri
}
const getPropertyHref = function (uri){
return `?p=properties`
}
const getInstanceHref = function (uri) {
return `?p=instances&conceptid=${encodeURIComponent(uri)}`
}
const getClassHref = function (uri) {
return `?p=classes&conceptid=${encodeURIComponent(uri)}`
}

let container = $(`<div>
<h4>Details of ${toLink(this.uri , "javascript:void(0)")} of type : ${this.types.map(x => toLink(x , getClassHref(x)))}</h4>
</div>`)
let table = $(`<table class='zebra' style='width: 100% ; min-width: 60vw'>
<thead>
<tr>
<th>Property name</th>
<th>Property value</th>
</tr>
</thead>
</table>`)

let tbody = $(`<tbody></tbody>`)
delete this.properties["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]
Object.entries(this.properties).forEach((x) => {
let row = `<tr><td>${toLink(x[0], getPropertyHref(x[0]))}</td><td>${x[1].map(x => toLink(x,getInstanceHref(x))).join(',')}</td></tr>`
tbody.append(row)
})
table.append(tbody)
container.append(table)
return container
}

}
54 changes: 54 additions & 0 deletions app/controllers/instances_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
class InstancesController < ApplicationController

def index_by_ontology
logger.debug params[:ontology].inspect
custom_render LinkedData::Client::HTTP.get("/ontologies/#{params[:ontology]}/instances", get_query_parameters , raw:true)
end

def index_by_class
custom_render LinkedData::Client::HTTP
.get("/ontologies/#{params[:ontology]}/classes/#{CGI.escape(params[:class])}/instances",
get_query_parameters, raw: true )

end

def show
inst = LinkedData::Client::HTTP
.get("/ontologies/#{params[:ontology]}/instances/#{CGI.escape(params[:instance])}",
get_query_parameters, raw: true)

render json: JSON.parse(inst)
end

private
# json render + adding next and prev pages links
def custom_render(instances)
instances = JSON.parse(instances)
if (instances.respond_to? :links) && (!instances.respond_to? :errors)
instances.links = {
nextPage: get_page_link(instances.nextPage),
prevPage: get_page_link(instances.prevPage)
}
end

render json: instances
end

def get_page_link(page_number)
return nil if page_number.nil?

if request.query_parameters.has_key?(:page)
request.original_url.gsub(/page=\d+/, "page=#{page_number}")
elsif request.query_parameters.empty?
request.original_url + "?" + "page=#{page_number}"
else
request.original_url + "&" + "page=#{page_number}"
end
end

def get_query_parameters
params = request.query_parameters.slice(:include, :display, :page, :pagesize) || {}
params[:include] = 'all' unless params.has_key? :include
params
end
end
Loading

0 comments on commit 5182c57

Please sign in to comment.