about summary refs log tree commit diff
path: root/gn2/wqflask/static/new/javascript/auth
diff options
context:
space:
mode:
authorArun Isaac2023-12-29 18:55:37 +0000
committerArun Isaac2023-12-29 19:01:46 +0000
commit204a308be0f741726b9a620d88fbc22b22124c81 (patch)
treeb3cf66906674020b530c844c2bb4982c8a0e2d39 /gn2/wqflask/static/new/javascript/auth
parent83062c75442160427b50420161bfcae2c5c34c84 (diff)
downloadgenenetwork2-204a308be0f741726b9a620d88fbc22b22124c81.tar.gz
Namespace all modules under gn2.
We move all modules under a gn2 directory. This is important for
"correct" packaging and deployment as a Guix service.
Diffstat (limited to 'gn2/wqflask/static/new/javascript/auth')
-rw-r--r--gn2/wqflask/static/new/javascript/auth/search.js172
-rw-r--r--gn2/wqflask/static/new/javascript/auth/search_genotypes.js95
-rw-r--r--gn2/wqflask/static/new/javascript/auth/search_mrna.js97
-rw-r--r--gn2/wqflask/static/new/javascript/auth/search_phenotypes.js188
4 files changed, 552 insertions, 0 deletions
diff --git a/gn2/wqflask/static/new/javascript/auth/search.js b/gn2/wqflask/static/new/javascript/auth/search.js
new file mode 100644
index 00000000..d094cebf
--- /dev/null
+++ b/gn2/wqflask/static/new/javascript/auth/search.js
@@ -0,0 +1,172 @@
+class InvalidCSSIDSelector extends Error {
+    constructor(message) {
+	super(message);
+	this.name = "InvalidCSSIDSelector";
+    }
+}
+
+class InvalidDataAttributeName extends Error {
+    constructor(message) {
+	super(message);
+	this.name = "InvalidDataAttributeName";
+    }
+}
+
+/**
+ * CSSIDSelector: A CSS ID Selector
+ * @param {String} A CSS selector of the form '#...'
+ */
+class CSSIDSelector {
+    constructor(selector) {
+	if(!selector.startsWith("#")) {
+	    throw new InvalidCSSIDSelector(
+		"Expected the CSS selector to begin with a `#` character.");
+	}
+	let id_str = selector.slice(1, selector.length);
+	if(document.getElementById(id_str) == null) {
+	    throw new InvalidCSSIDSelector(
+		"Element with ID '" + id_str + "' does not exist.");
+	}
+	this.selector = selector;
+    }
+}
+
+/**
+ * TableDataSource: A type to represent a table's data source
+ * @param {String} A CSS selector for an ID
+ * @param {String} A `data-*` attribute name
+ */
+class TableDataSource {
+    constructor(table_id, data_attribute_name, checkbox_creation_function) {
+	this.table_id = new CSSIDSelector(table_id);
+	let data = document.querySelector(
+	    table_id).getAttribute(data_attribute_name);
+	if(data == null) {
+	    throw new InvalidDataAttributeName(
+		"data-* attribute '" + data_attribute_name + "' does not exist " +
+		    "for table with ID '" + table_id.slice(1, table_id.length) +
+		    "'.");
+	} else {
+	    this.data_attribute_name = data_attribute_name;
+	}
+	this.checkbox_creation_function = checkbox_creation_function;
+    }
+}
+
+/**
+ * Render the table
+ * @param {String} The selector for the table's ID
+ * @param {String} The name of the data-* attribute holding the table's data
+ * @param {Function} The function to call to generate the appropriate checkbox
+ */
+function render_table(table_data_source) {
+    table_id = table_data_source.table_id.selector;
+    data_attr_name = table_data_source.data_attribute_name;
+    $(table_id + " tbody tr").remove();
+    table_data = JSON.parse($(table_id).attr(data_attr_name)).sort((d1, d2) => {
+	return (d1.dataset_name > d2.dataset_name ? 1 : (
+	    d1.dataset_name < d2.dataset_name ? -1 : 0))
+    });
+    if(table_data.length < 1) {
+	row = $("<tr>")
+	cell = $('<td colspan="100%" align="center">');
+	cell.append(
+	    $('<span class="glyphicon glyphicon-info-sign text-info">'));
+	cell.append("&nbsp;");
+	cell.append("No genotype datasets remaining.");
+	row.append(cell);
+	$(table_id + " tbody").append(row);
+    }
+    table_data.forEach(function(dataset) {
+	row = $("<tr>")
+	row.append(table_data_source.checkbox_creation_function(dataset));
+	row.append(table_cell(dataset.InbredSetName));
+	row.append(table_cell(dataset.dataset_name));
+	row.append(table_cell(dataset.dataset_fullname));
+	row.append(table_cell(dataset.dataset_shortname));
+	$(table_id + " tbody").append(row);
+    });
+}
+
+function in_array(items, filter_fn) {
+    return items.filter(filter_fn).length > 0;
+}
+
+function remove_from_table_data(dataset, table_data_source, filter_fn) {
+    let table_id = table_data_source.table_id.selector;
+    let data_attr_name = table_data_source.data_attribute_name;
+    without_dataset = JSON.parse($(table_id).attr(data_attr_name)).filter(
+	filter_fn);
+    $(table_id).attr(data_attr_name, JSON.stringify(without_dataset));
+}
+
+function add_to_table_data(dataset, table_data_source, filter_fn) {
+    let table_id = table_data_source.table_id.selector;
+    let data_attr_name = table_data_source.data_attribute_name;
+    table_data = JSON.parse($(table_id).attr(data_attr_name));
+    if(!in_array(table_data, filter_fn)) {
+	table_data.push(dataset);
+    }
+    $(table_id).attr(data_attr_name, JSON.stringify(Array.from(table_data)));
+}
+
+/**
+ * Switch the dataset/trait from search table to selection table and vice versa
+ * @param {Object} A dataset/trait object
+ * @param {TableDataSource} The source table for the dataset/trait
+ * @param {TableDataSource} The destination table for the dataset/trait
+ */
+function select_deselect(item, source, destination, filter_fn, render_fn=render_table) {
+    dest_selector = destination.table_id.selector
+    dest_data = JSON.parse(
+	$(dest_selector).attr(destination.data_attribute_name));
+    add_to_table_data(item, destination, filter_fn); // Add to destination table
+    remove_from_table_data(item, source, (arg) => {return !filter_fn(arg)}); // Remove from source table
+    /***** BEGIN: Re-render tables *****/
+    render_fn(destination);
+    render_fn(source);
+    /***** END: Re-render tables *****/
+}
+
+function debounce(func, delay=500) {
+    var timeout;
+    return function search(event) {
+	clearTimeout(timeout);
+	timeout = setTimeout(func, delay);
+    };
+}
+
+/**
+ * Build a checkbox
+ * @param {Dataset Object} A JSON.stringify-able object
+ * @param {String} The name to assign the checkbox
+ */
+function build_checkbox(data_object, checkbox_name, checkbox_aux_classes="", checked=false) {
+    cell = $("<td>");
+    check = $(
+	'<input type="checkbox" class="checkbox" ' +
+	    'name="' + checkbox_name + '">');
+    check.val(JSON.stringify(data_object));
+    check.prop("checked", checked);
+    auxilliary_classes = checkbox_aux_classes.trim();
+    if(Boolean(auxilliary_classes)) {
+	check.attr("class",
+		   check.attr("class") + " " + auxilliary_classes.trim());
+    }
+    cell.append(check);
+    return cell;
+}
+
+function link_checkbox(dataset) {
+    return build_checkbox(dataset, "selected", "checkbox-selected", true);
+}
+
+function search_checkbox(dataset) {
+    return build_checkbox(dataset, "search_datasets", "checkbox-search");
+}
+
+function table_cell(value) {
+    cell = $("<td>");
+    cell.html(value);
+    return cell;
+}
diff --git a/gn2/wqflask/static/new/javascript/auth/search_genotypes.js b/gn2/wqflask/static/new/javascript/auth/search_genotypes.js
new file mode 100644
index 00000000..d1b8ed9e
--- /dev/null
+++ b/gn2/wqflask/static/new/javascript/auth/search_genotypes.js
@@ -0,0 +1,95 @@
+function toggle_link_button() {
+    num_groups = $("#frm-link-genotypes select option").length - 1;
+    num_selected = JSON.parse(
+	$("#tbl-link-genotypes").attr("data-selected-datasets")).length;
+    if(num_groups > 0 && num_selected > 0) {
+	$("#frm-link-genotypes input[type='submit']").prop("disabled", false);
+    } else {
+	$("#frm-link-genotypes input[type='submit']").prop("disabled", true);
+    }
+}
+
+function search_genotypes() {
+    query = document.getElementById("txt-query").value;
+    selected = JSON.parse(document.getElementById(
+	"tbl-link-genotypes").getAttribute("data-selected-datasets"));
+    species_name = document.getElementById("txt-species-name").value
+    search_endpoint = "/auth/data/genotype/search"
+    search_table = new TableDataSource(
+	"#tbl-genotypes", "data-datasets", search_checkbox);
+    $.ajax(
+	search_endpoint,
+	{
+	    "method": "POST",
+	    "contentType": "application/json; charset=utf-8",
+	    "dataType": "json",
+	    "data": JSON.stringify({
+		"query": query,
+		"selected": selected,
+		"dataset_type": "genotype",
+		"species_name": species_name}),
+	    "error": function(jqXHR, textStatus, errorThrown) {
+		data = jqXHR.responseJSON
+		elt = document.getElementById("search-error").setAttribute(
+		    "style", "display: block;");
+		document.getElementById("search-error-text").innerHTML = (
+		    data.error + " (" + data.status_code + "): " +
+			data.error_description);
+		document.getElementById("tbl-genotypes").setAttribute(
+		    "data-datasets", JSON.stringify([]));
+		render_table(search_table);
+	    },
+	    "success": function(data, textStatus, jqXHR) {
+		document.getElementById("search-error").setAttribute(
+		    "style", "display: none;");
+		document.getElementById("tbl-genotypes").setAttribute(
+		    "data-datasets", JSON.stringify(data));
+		render_table(search_table);
+	    }
+	});
+}
+
+/**
+ * Return function to check whether `dataset` is in array of `datasets`.
+ * @param {GenotypeDataset} A genotype dataset.
+ * @param {Array} An array of genotype datasets.
+ */
+function make_filter(trait) {
+    return (dst) => {
+	return (dst.SpeciesId == dataset.SpeciesId &&
+		dst.InbredSetId == dataset.InbredSetId &&
+		dst.GenoFreezeId == dataset.GenoFreezeId);
+    };
+}
+
+$(document).ready(function() {
+    let search_table = new TableDataSource(
+	"#tbl-genotypes", "data-datasets", search_checkbox);
+    let link_table = new TableDataSource(
+	"#tbl-link-genotypes", "data-selected-datasets", link_checkbox);
+
+    $("#frm-search-traits").submit(function(event) {
+	event.preventDefault();
+	return false;
+    });
+
+    $("#txt-query").keyup(debounce(search_genotypes));
+
+    $("#tbl-genotypes").on("change", ".checkbox-search", function(event) {
+        if(this.checked) {
+	    dataset = JSON.parse(this.value);
+	    select_deselect(
+		dataset, search_table, link_table, make_filter(dataset));
+	    toggle_link_button();
+        }
+    });
+
+    $("#tbl-link-genotypes").on("change", ".checkbox-selected", function(event) {
+	if(!this.checked) {
+	    dataset = JSON.parse(this.value);
+	    select_deselect(
+		dataset, link_table, search_table, make_filter(dataset));
+	    toggle_link_button();
+	}
+    });
+});
diff --git a/gn2/wqflask/static/new/javascript/auth/search_mrna.js b/gn2/wqflask/static/new/javascript/auth/search_mrna.js
new file mode 100644
index 00000000..3eca4ed2
--- /dev/null
+++ b/gn2/wqflask/static/new/javascript/auth/search_mrna.js
@@ -0,0 +1,97 @@
+function toggle_link_button() {
+    num_groups = $("#frm-link select option").length - 1;
+    num_selected = JSON.parse(
+	$("#tbl-link").attr("data-datasets")).length;
+    if(num_groups > 0 && num_selected > 0) {
+	$("#frm-link input[type='submit']").prop("disabled", false);
+    } else {
+	$("#frm-link input[type='submit']").prop("disabled", true);
+    }
+}
+
+function search_mrna() {
+    query = document.getElementById("txt-query").value;
+    selected = JSON.parse(document.getElementById(
+	"tbl-link").getAttribute("data-datasets"));
+    species_name = document.getElementById("txt-species-name").value
+    search_endpoint = "/auth/data/mrna/search"
+    search_table = new TableDataSource(
+	"#tbl-search", "data-datasets", search_checkbox);
+    $.ajax(
+	search_endpoint,
+	{
+	    "method": "POST",
+	    "contentType": "application/json; charset=utf-8",
+	    "dataType": "json",
+	    "data": JSON.stringify({
+		"query": query,
+		"selected": selected,
+		"dataset_type": "mrna",
+		"species_name": species_name}),
+	    "error": function(jqXHR, textStatus, errorThrown) {
+		error_data = jqXHR.responseJSON
+		console.debug("ERROR_DATA:", error_data);
+		elt = document.getElementById("search-error").setAttribute(
+		    "style", "display: block;");
+		document.getElementById("search-error-text").innerHTML = (
+		    error_data.error + " (" + error_data.status_code + "): " +
+			error_data.error_description);
+		document.getElementById("tbl-search").setAttribute(
+		    "data-datasets", JSON.stringify([]));
+		render_table(search_table);
+	    },
+	    "success": function(data, textStatus, jqXHR) {
+		document.getElementById("search-error").setAttribute(
+		    "style", "display: none;");
+		document.getElementById("tbl-search").setAttribute(
+		    "data-datasets", JSON.stringify(data));
+		render_table(search_table);
+	    }
+	});
+}
+
+/**
+ * Make function to check whether `dataset` is in array of `datasets`.
+ * @param {mRNADataset} A mrna dataset.
+ * @param {Array} An array of mrna datasets.
+ */
+function make_filter(dataset) {
+    return (dst) => {
+	return (dst.SpeciesId == dataset.SpeciesId &&
+		dst.InbredSetId == dataset.InbredSetId &&
+		dst.ProbeFreezeId == dataset.ProbeFreezeId &&
+		dst.ProbeSetFreezeId == dataset.ProbeSetFreezeId);
+    };
+}
+
+$(document).ready(function() {
+    let search_table = new TableDataSource(
+	"#tbl-search", "data-datasets", search_checkbox);
+    let link_table = new TableDataSource(
+	"#tbl-link", "data-datasets", link_checkbox);
+
+    $("#frm-search").submit(function(event) {
+	event.preventDefault();
+	return false;
+    });
+
+    $("#txt-query").keyup(debounce(search_mrna));
+
+    $("#tbl-search").on("change", ".checkbox-search", function(event) {
+        if(this.checked) {
+	    dataset = JSON.parse(this.value);
+	    select_deselect(
+		dataset, search_table, link_table, make_filter(dataset));
+	    toggle_link_button();
+        }
+    });
+
+    $("#tbl-link").on("change", ".checkbox-selected", function(event) {
+	if(!this.checked) {
+	    dataset = JSON.parse(this.value);
+	    select_deselect(
+		dataset, link_table, search_table, make_filter(dataset));
+	    toggle_link_button();
+	}
+    });
+});
diff --git a/gn2/wqflask/static/new/javascript/auth/search_phenotypes.js b/gn2/wqflask/static/new/javascript/auth/search_phenotypes.js
new file mode 100644
index 00000000..99ecb16e
--- /dev/null
+++ b/gn2/wqflask/static/new/javascript/auth/search_phenotypes.js
@@ -0,0 +1,188 @@
+/**
+ * Global variables: Bad idea - figure out how to pass them down a call stack.
+ */
+search_table = new TableDataSource(
+    "#tbl-phenotypes", "data-traits", (trait) => {
+	return build_checkbox(trait, "search_traits", "checkbox-search");
+    });
+link_table = new TableDataSource(
+    "#tbl-link-phenotypes", "data-traits", (trait) => {
+	return build_checkbox(
+	    trait, "selected", "checkbox-selected", checked=true);
+    });
+
+/**
+ * Toggle the state for the "Link Traits" button
+ */
+function toggle_link_button() {
+    num_groups = $("#frm-link-phenotypes select option").length - 1;
+    num_selected = JSON.parse(
+	$("#tbl-link-phenotypes").attr("data-traits")).length;
+    if(num_groups > 0 && num_selected > 0) {
+	$("#frm-link-phenotypes input[type='submit']").prop("disabled", false);
+    } else {
+	$("#frm-link-phenotypes input[type='submit']").prop("disabled", true);
+    }
+}
+
+/**
+ * Default error function: print out debug messages
+ */
+function default_error_fn(jqXHR, textStatus, errorThrown) {
+    console.debug("XHR:", jqXHR);
+    console.debug("STATUS:", textStatus);
+    console.debug("ERROR:", errorThrown);
+}
+
+/**
+ * Render the table(s) for the phenotype traits
+ * @param {TableDataSource} The table to render
+ */
+function render_pheno_table(table_data_source) {
+    table_id = table_data_source.table_id.selector;
+    data_attr_name = table_data_source.data_attribute_name;
+    $(table_id + " tbody tr").remove();
+    table_data = JSON.parse($(table_id).attr(data_attr_name)).sort((t1, t2) => {
+	return (t1.name > t2.name ? 1 : (t1.name < t2.name ? -1 : 0))
+    });
+    if(table_data.length < 1) {
+	row = $("<tr>")
+	cell = $('<td colspan="100%" align="center">');
+	cell.append(
+	    $('<span class="glyphicon glyphicon-info-sign text-info">'));
+	cell.append("&nbsp;");
+	cell.append("No phenotype traits to select from.");
+	row.append(cell);
+	$(table_id + " tbody").append(row);
+    }
+    table_data.forEach(function(trait) {
+	row = $("<tr>")
+	row.append(table_data_source.checkbox_creation_function(trait));
+	row.append(table_cell(trait.name));
+	row.append(table_cell(trait.group));
+	row.append(table_cell(trait.dataset));
+	row.append(table_cell(trait.dataset_fullname));
+	row.append(table_cell(trait.description));
+	row.append(table_cell(trait.authors.join(", ")));
+	row.append(table_cell(
+	    '<a href="' + trait.pubmed_link +
+		'" title="Pubmed link for trait ' + trait.name + '.">' +
+		trait.year + "</a>"));
+	row.append(table_cell("Chr:" + trait.geno_chr + "@" + trait.geno_mb));
+	row.append(table_cell(trait.lrs));
+	row.append(table_cell(trait.additive));
+	$(table_id + " tbody").append(row);
+    });
+}
+
+function display_search_results(data, textStatus, jqXHR) {
+    if(data.status == "queued" || data.status == "started") {
+	setTimeout(() => {
+	    fetch_search_results(data.job_id, display_search_results);
+	}, 250);
+	return;
+    }
+    if(data.status == "completed") {
+	$("#tbl-phenotypes").attr(
+	    "data-traits", JSON.stringify(data.search_results));
+	// Remove this reference to global variable
+	render_pheno_table(search_table);
+    }
+    $("#txt-search").prop("disabled", false);
+}
+
+/**
+ * Fetch the search results
+ * @param {UUID}: The job id to fetch data for
+ */
+function fetch_search_results(job_id, success, error=default_error_fn) {
+    host = $("#frm-search-traits").attr("data-gn-server-url");
+    endpoint = host + "auth/data/search/phenotype/" + job_id
+    $("#txt-search").prop("disabled", true);
+    $.ajax(
+	endpoint,
+	{
+	    "method": "GET",
+	    "contentType": "application/json; charset=utf-8",
+	    "dataType": "json",
+	    "error": error,
+	    "success": success
+	}
+    );
+}
+
+function search_phenotypes() {
+    query = document.getElementById("txt-query").value;
+    selected = JSON.parse(document.getElementById(
+	"tbl-link-phenotypes").getAttribute("data-traits"));
+    species_name = document.getElementById("txt-species-name").value
+    per_page = document.getElementById("txt-per-page").value
+    search_table = new TableDataSource(
+	"#tbl-phenotypes", "data-traits", search_checkbox);
+    endpoint = "/auth/data/phenotype/search"
+    $.ajax(
+	endpoint,
+	{
+	    "method": "POST",
+	    "contentType": "application/json; charset=utf-8",
+	    "dataType": "json",
+	    "data": JSON.stringify({
+		"query": query,
+		"species_name": species_name,
+		"dataset_type": "phenotype",
+		"per_page": per_page,
+		"selected_traits": selected
+	    }),
+	    "error": default_error_fn,
+	    "success": (data, textStatus, jqXHR) => {
+		fetch_search_results(data.job_id, display_search_results);
+	    }
+	});
+}
+
+/**
+ * Return a function to check whether `trait` is in array of `traits`.
+ * @param {PhenotypeTrait} A phenotype trait.
+ * @param {Array} An array of phenotype traits.
+ */
+function make_filter(trait) {
+    return (trt) => {
+	return (trt.species == trait.species &&
+		trt.group == trait.group &&
+		trt.dataset == trait.dataset &&
+		trt.name == trait.name);
+    };
+}
+
+$(document).ready(function() {
+    $("#frm-search-traits").submit(event => {
+	event.preventDefault();
+	return false;
+    });
+
+    $("#txt-query").keyup(debounce(search_phenotypes));
+
+    $("#tbl-link-phenotypes").on("change", ".checkbox-selected", function(event) {
+	if(!this.checked) {
+	    trait = JSON.parse(this.value);
+	    select_deselect(trait, link_table, search_table,
+			    make_filter(trait), render_pheno_table);
+	    toggle_link_button();
+	}
+    });
+
+    $("#tbl-phenotypes").on("change", ".checkbox-search", function(event) {
+	if(this.checked) {
+	    trait = JSON.parse(this.value)
+	    select_deselect(trait, search_table, link_table,
+			    make_filter(trait), render_pheno_table);
+	    toggle_link_button();
+	}
+    });
+
+    setTimeout(() => {
+	fetch_search_results(
+	    $("#tbl-phenotypes").attr("data-initial-job-id"),
+	    display_search_results);
+    }, 500);
+});