aboutsummaryrefslogtreecommitdiff
path: root/uploader/templates/phenotypes
diff options
context:
space:
mode:
Diffstat (limited to 'uploader/templates/phenotypes')
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-base.html127
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-raw-files.html277
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html4
-rw-r--r--uploader/templates/phenotypes/macro-display-preview-table.html21
4 files changed, 372 insertions, 57 deletions
diff --git a/uploader/templates/phenotypes/add-phenotypes-base.html b/uploader/templates/phenotypes/add-phenotypes-base.html
index b3a53b0..c4315ee 100644
--- a/uploader/templates/phenotypes/add-phenotypes-base.html
+++ b/uploader/templates/phenotypes/add-phenotypes-base.html
@@ -30,7 +30,9 @@
action="{{url_for('species.populations.phenotypes.add_phenotypes',
species_id=species.SpeciesId,
population_id=population.Id,
- dataset_id=dataset.Id)}}">
+ dataset_id=dataset.Id,
+ use_bundle=use_bundle)}}"
+ data-resumable-target="{{url_for('files.resumable_upload_post')}}">
<legend>Add New Phenotypes</legend>
<div class="form-text help-block">
@@ -57,6 +59,9 @@
<button id="btn-search-pubmed-id" class="btn btn-info">Search</button>
</span>
</div>
+ <span id="search-pubmed-id-error"
+ class="form-text text-muted text-danger hidden">
+ </span><br />
<span class="form-text text-muted">
Enter your publication's PubMed ID above and click "Search" to search
for some (or all) of the publication details requested below.
@@ -157,10 +162,6 @@
{%endblock%}
-{%block sidebarcontents%}
-{{display_pheno_dataset_card(species, population, dataset)}}
-{%endblock%}
-
{%block javascript%}
<script type="text/javascript">
@@ -219,13 +220,12 @@
"journal": details[pubmed_id].fulljournalname,
"volume": details[pubmed_id].volume,
"pages": details[pubmed_id].pages,
- "month": months[_date[1].toLowerCase()],
+ "month": _date.length > 1 ? months[_date[1].toLowerCase()] : "jan",
"year": _date[0],
};
};
var update_publication_details = (details) => {
- console.log("Updating with the following details:", details);
Object.entries(details).forEach((entry) => {;
switch(entry[0]) {
case "authors":
@@ -244,41 +244,7 @@
});
};
- var freds_variable = undefined;
- $("#btn-search-pubmed-id").on("click", (event) => {
- event.preventDefault();
- var search_button = event.target;
- var pubmed_id = $("#txt-pubmed-id").val().trim();
- remove_class($("#txt-pubmed-id").parent(), "has-error");
- if(pubmed_id == "") {
- add_class($("#txt-pubmed-id").parent(), "has-error");
- return false;
- }
-
- var flag_pub_details = false;
- var flag_pub_abstract = false;
- var enable_button = () => {
- search_button.disabled = !(flag_pub_details && flag_pub_abstract);
- };
- search_button.disabled = true;
- // Fetch publication details
- $.ajax("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi",
- {
- "method": "GET",
- "data": {"db": "pubmed", "id": pubmed_id, "format": "json"},
- "success": (data, textStatus, jqXHR) => {
- // process and update publication details
- update_publication_details(extract_details(
- pubmed_id, data.result));
- },
- "error": (jqXHR, textStatus, errorThrown) => {},
- "complete": () => {
- flag_pub_details = true;
- enable_button();
- },
- "dataType": "json"
- });
- // Fetch the abstract
+ var fetch_publication_abstract = (pubmed_id, pub_details) => {
$.ajax("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi",
{
"method": "GET",
@@ -289,25 +255,74 @@
"retmode": "xml"
},
"success": (data, textStatus, jqXHR) => {
- // process and update the abstract...
- freds_variable = data;
- console.log("ABSTRACT DETAILS:", data);
update_publication_details({
- "abstract": Array.from(data
- .getElementsByTagName(
- "Abstract")[0]
- .children)
- .map((elt) => {return elt.textContent.trim();})
- .join("\r\n")
- });
+ ...pub_details,
+ ...{
+ "abstract": Array.from(data
+ .getElementsByTagName(
+ "Abstract")[0]
+ .children)
+ .map((elt) => {return elt.textContent.trim();})
+ .join("\r\n")
+ }});
},
"error": (jqXHR, textStatus, errorThrown) => {},
- "complete": (jqXHR, textStatus) => {
- flag_pub_abstract = true;
- enable_button();
- },
+ "complete": (jqXHR, textStatus) => {},
"dataType": "xml"
});
+ };
+
+ var fetch_publication_details = (pubmed_id, complete_thunks) => {
+ error_display = $("#search-pubmed-id-error");
+ error_display.text("");
+ add_class(error_display, "hidden");
+ $.ajax("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi",
+ {
+ "method": "GET",
+ "data": {"db": "pubmed", "id": pubmed_id, "format": "json"},
+ "success": (data, textStatus, jqXHR) => {
+ // process and update publication details
+ hasError = (
+ Object.hasOwn(data, "error") ||
+ Object.hasOwn(data.result[pubmed_id], "error"));
+ if(hasError) {
+ error_display.text(
+ "There was an error fetching a publication with " +
+ "the given PubMed ID! The error received " +
+ "was: '" + (
+ data.error ||
+ data.result[pubmed_id].error) +
+ "'. Please check ID you provided and try " +
+ "again.");
+ remove_class(error_display, "hidden");
+ } else {
+ fetch_publication_abstract(
+ pubmed_id,
+ extract_details(pubmed_id, data.result));
+ }
+ },
+ "error": (jqXHR, textStatus, errorThrown) => {},
+ "complete": () => {
+ complete_thunks.forEach((thunk) => {thunk()});
+ },
+ "dataType": "json"
+ });
+ };
+
+ $("#btn-search-pubmed-id").on("click", (event) => {
+ event.preventDefault();
+ var search_button = event.target;
+ var pubmed_id = $("#txt-pubmed-id").val().trim();
+ remove_class($("#txt-pubmed-id").parent(), "has-error");
+ if(pubmed_id == "") {
+ add_class($("#txt-pubmed-id").parent(), "has-error");
+ return false;
+ }
+
+ search_button.disabled = true;
+ // Fetch publication details
+ fetch_publication_details(pubmed_id,
+ [() => {search_button.disabled = false;}]);
return false;
});
</script>
diff --git a/uploader/templates/phenotypes/add-phenotypes-raw-files.html b/uploader/templates/phenotypes/add-phenotypes-raw-files.html
index ef0895d..b88d16a 100644
--- a/uploader/templates/phenotypes/add-phenotypes-raw-files.html
+++ b/uploader/templates/phenotypes/add-phenotypes-raw-files.html
@@ -2,6 +2,8 @@
{%from "flash_messages.html" import flash_all_messages%}
{%from "macro-table-pagination.html" import table_pagination%}
{%from "phenotypes/macro-display-pheno-dataset-card.html" import display_pheno_dataset_card%}
+{%from "phenotypes/macro-display-preview-table.html" import display_preview_table%}
+{%from "phenotypes/macro-display-resumable-elements.html" import display_resumable_elements%}
{%block title%}Phenotypes{%endblock%}
@@ -106,13 +108,14 @@
<fieldset id="fldset-data-files">
<legend>Data File(s)</legend>
- <div class="form-group">
+ <div class="form-group non-resumable-elements">
<label for="finput-phenotype-descriptions" class="form-label">
Phenotype Descriptions</label>
<input id="finput-phenotype-descriptions"
name="phenotype-descriptions"
class="form-control"
type="file"
+ data-preview-table="tbl-preview-pheno-desc"
required="required" />
<span class="form-text text-muted">
Provide a file that contains only the phenotype descriptions,
@@ -121,12 +124,23 @@
the documentation for the expected format of the file</a>.</span>
</div>
+ {{display_resumable_elements(
+ "resumable-phenotype-descriptions",
+ "phenotype descriptions",
+ '<p>You can drop a CSV file that contains the phenotype descriptions here,
+ or you can click the "Browse" button to select it from your computer.</p>
+ <p>The CSV file must conform to some standards, as documented in the
+ <a href="#docs-file-phenotype-description"
+ title="Documentation of the phenotype data file format.">
+ "Phenotypes Descriptions" documentation</a> section below.</p>')}}
+
<div class="form-group">
<label for="finput-phenotype-data" class="form-label">Phenotype Data</label>
<input id="finput-phenotype-data"
name="phenotype-data"
class="form-control"
type="file"
+ data-preview-table="tbl-preview-pheno-data"
required="required" />
<span class="form-text text-muted">
Provide a file that contains only the phenotype data. See
@@ -142,6 +156,7 @@
name="phenotype-se"
class="form-control"
type="file"
+ data-preview-table="tbl-preview-pheno-se"
required="required" />
<span class="form-text text-muted">
Provide a file that contains only the standard errors for the phenotypes,
@@ -154,6 +169,7 @@
name="phenotype-n"
class="form-control"
type="file"
+ data-preview-table="tbl-preview-pheno-n"
required="required" />
<span class="form-text text-muted">
Provide a file that contains only the number of samples/individuals used in
@@ -294,20 +310,279 @@
{%endblock%}
+{%block sidebarcontents%}
+{{display_preview_table("tbl-preview-pheno-desc", "descriptions")}}
+{{display_preview_table("tbl-preview-pheno-data", "data")}}
+{%if population.Family in families_with_se_and_n%}
+{{display_preview_table("tbl-preview-pheno-se", "standard errors")}}
+{{display_preview_table("tbl-preview-pheno-n", "number of samples")}}
+{%endif%}
+{{display_pheno_dataset_card(species, population, dataset)}}
+{%endblock%}
+
{%block more_javascript%}
+<script src="{{url_for('base.node_modules',
+ filename='resumablejs/resumable.js')}}"></script>
+<script type="text/javascript" src="/static/js/files.js"></script>
+
<script type="text/javascript">
$("#btn-reset-file-separator").on("click", (event) => {
event.preventDefault();
$("#txt-file-separator").val("\t");
+ $("#txt-file-separator").trigger("change");
});
$("#btn-reset-file-comment-character").on("click", (event) => {
event.preventDefault();
$("#txt-file-comment-character").val("#");
+ $("#txt-file-comment-character").trigger("change");
});
$("#btn-reset-file-na").on("click", (event) => {
event.preventDefault();
$("#txt-file-na").val("- NA N/A");
+ $("#txt-file-na").trigger("change");
+ });
+
+ var update_preview = (table, filedata, formdata, numrows) => {
+ table.find("thead tr").remove()
+ table.find(".data-row").remove();
+ var linenum = 0;
+ var tableheader = table.find("thead");
+ var tablebody = table.find("tbody");
+ var numheadings = 0;
+ var navalues = formdata
+ .na_strings
+ .split(" ")
+ .map((v) => {return v.trim();})
+ .filter((v) => {return Boolean(v);});
+ filedata.forEach((line) => {
+ if(line.startsWith(formdata.comment_char) || linenum >= numrows) {
+ return false;
+ }
+ var row = $("<tr></tr>");
+ line.split(formdata.separator)
+ .map((field) => {
+ var value = field.trim();
+ if(navalues.includes(value)) {
+ return "⋘NUL⋙";
+ }
+ return value;
+ })
+ .filter((field) => {
+ return (field !== "" && field != undefined && field != null);
+ })
+ .forEach((field) => {
+ if(linenum == 0) {
+ numheadings += 1;
+ var tablefield = $("<th></th>");
+ tablefield.text(field);
+ row.append(tablefield);
+ } else {
+ add_class(row, "data-row");
+ var tablefield = $("<td></td>");
+ tablefield.text(field);
+ row.append(tablefield);
+ }
+ });
+
+ if(linenum == 0) {
+ tableheader.append(row);
+ } else {
+ tablebody.append(row);
+ }
+ linenum += 1;
+ });
+
+ if(table.find("tbody tr.data-row").length > 0) {
+ add_class(table.find(".data-row-template"), "hidden");
+ } else {
+ remove_class(table.find(".data-row-template"), "hidden");
+ }
+ };
+
+ var preview_tables_to_elements_map = {
+ "#tbl-preview-pheno-desc": "#finput-phenotype-descriptions",
+ "#tbl-preview-pheno-data": "#finput-phenotype-data",
+ "#tbl-preview-pheno-se": "#finput-phenotype-se",
+ "#tbl-preview-pheno-n": "#finput-phenotype-n"
+ };
+
+ var files_metadata = () => {
+ return {
+ "separator": $("#txt-file-separator").val(),
+ "comment_char": $(
+ "#txt-file-comment-character").val(),
+ "na_strings": $("#txt-file-na").val()
+ }
+ };
+
+ var PREVIEW_ROWS = 5;
+
+ var handler_update_previews = (event) => {
+ Object.entries(preview_tables_to_elements_map).forEach((mapentry) => {
+ var element = $(mapentry[1]);
+ if(element.length === 1) {
+ read_first_n_lines(
+ element[0],
+ 10,
+ [(data) => {
+ update_preview(
+ $(mapentry[0]),
+ data,
+ files_metadata(),
+ PREVIEW_ROWS);}]);
+ }
+ });
+ };
+
+ [
+ "#txt-file-separator",
+ "#txt-file-comment-character",
+ "#txt-file-na"
+ ].forEach((elementid) => {
+ $(elementid).on("change", handler_update_previews);
+ });
+
+ [
+ "#finput-phenotype-descriptions",
+ "#finput-phenotype-data",
+ "#finput-phenotype-se",
+ "#finput-phenotype-n"
+ ].forEach((elementid) => {
+ $(elementid).on("change", (event) => {
+ read_first_n_lines(
+ event.target,
+ 10,
+ [(data) => {
+ update_preview(
+ $("#" + event.target.getAttribute("data-preview-table")),
+ data,
+ files_metadata(),
+ PREVIEW_ROWS);
+ }]);
+ });
+ });
+
+
+ var resumableDisplayFiles = (display_area, files) => {
+ files.forEach((file) => {
+ var display_element = display_area
+ .find(".file-display-template")
+ .clone();
+ remove_class(display_element, "hidden");
+ remove_class(display_element, "file-display-template");
+ add_class(display_element, "file-display");
+ display_element.find(".filename").text(file.name
+ || file.fileName
+ || file.relativePath
+ || file.webkitRelativePath);
+ display_element.find(".filesize").text(
+ (file.size / (1024*1024)).toFixed(2) + "MB");
+ display_element.find(".fileuniqueid").text(file.uniqueIdentifier);
+ display_element.find(".filemimetype").text(file.file.type);
+ display_area.append(display_element);
+ });
+ };
+
+
+ var indicateProgress = (resumable, progress_bar) => {
+ return (event) => {
+ var progress = (resumable.progress() * 100).toFixed(2);
+ var pbar = progress_bar.find(".progress-bar");
+ remove_class(progress_bar, "hidden");
+ pbar.css("width", progress+"%");
+ pbar.attr("aria-valuenow", progress);
+ pbar.text("Uploading: " + progress + "%");
+ };
+ };
+
+ var retryUpload = (retry_button, cancel_button) => {
+ retry_button.on("click", (event) => {
+ resumable.files.forEach((file) => {file.retry();});
+ add_class(retry_button, "hidden");
+ remove_class(cancel_button, "hidden");
+ add_class(browse_button, "hidden");
+ });
+ };
+
+ var cancelUpload = (cancel_button, retry_button) => {
+ cancel_button.on("click", (event) => {
+ resumable.files.forEach((file) => {
+ if(file.isUploading()) {
+ file.abort();
+ }
+ });
+ add_class(cancel_button, "hidden");
+ remove_class(retry_button, "hidden");
+ remove_class(browse_button, "hidden");
+ });
+ };
+
+
+ var startUpload = (browse_button, retry_button, cancel_button) => {
+ return (event) => {
+ remove_class(cancel_button, "hidden");
+ add_class(retry_button, "hidden");
+ add_class(browse_button, "hidden");
+ };
+ };
+
+
+ var uploadSuccess = () => {
+ return (file, message) => {
+ console.log("THE FILE:", file);
+ console.log("THE SUCCESS MESSAGE:", message);
+ // TODOS:
+ // * Save filename/filepath somewhere
+ // * Trigger some function that will run when all files have succeeded
+ };
+ };
+
+
+ var uploadError = () => {
+ return (message, file) => {
+ console.log("THE FILE:", file);
+ console.log("THE ERROR MESSAGE:", message);
+ };
+ };
+
+
+ var r = errorHandler(
+ fileSuccessHandler(
+ uploadStartHandler(
+ filesAddedHandler(
+ markResumableDragAndDropElement(
+ makeResumableElement(
+ $("#frm-add-phenotypes").attr("data-resumable-target"),
+ $("#finput-phenotype-descriptions").parent(),
+ $("#resumable-phenotype-descriptions"),
+ $("#frm-add-phenotypes input[type=submit]"),
+ ["csv", "tsv"]),
+ $("#finput-phenotype-descriptions").parent(),
+ $("#resumable-phenotype-descriptions"),
+ $("#resumable-phenotype-descriptions-browse-button")),
+ (files) => {
+ // TODO: Also trigger preview!
+ resumableDisplayFiles(
+ $("#resumable-phenotype-descriptions-selected-files"), files);
+ }),
+ startUpload($("#resumable-phenotype-descriptions-browse-button"),
+ $("#resumable-phenotype-descriptions-retry-button"),
+ $("#resumable-phenotype-descriptions-cancel-button"))),
+ uploadSuccess()),
+ uploadError());
+
+ progressHandler(
+ r,
+ indicateProgress(r, $("#resumable-phenotype-descriptions-progress-bar")));
+
+
+ $("#frm-add-phenotypes input[type=submit]").on("click", (event) => {
+ event.preventDefault();
+ // TODO: Check all the relevant files exist
+ // TODO: Check all fields
+ // Start the uploads.
+ r.upload();
});
</script>
{%endblock%}
diff --git a/uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html b/uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html
index 8f67baa..898fc0c 100644
--- a/uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html
+++ b/uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html
@@ -201,3 +201,7 @@
<em>phenotypes × individuals</em>.</p>
</div>
{%endblock%}
+
+{%block sidebarcontents%}
+{{display_pheno_dataset_card(species, population, dataset)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/macro-display-preview-table.html b/uploader/templates/phenotypes/macro-display-preview-table.html
new file mode 100644
index 0000000..7509158
--- /dev/null
+++ b/uploader/templates/phenotypes/macro-display-preview-table.html
@@ -0,0 +1,21 @@
+{%macro display_preview_table(tableid, filetype)%}
+<div class="card" style="max-width: 676px;">
+ <div class="card-body">
+ <h5 class="card-title">Phenotypes '{{filetype | title}}' File Preview</h5>
+ <div class="card-text">
+ <table id="{{tableid}}" class="table table-condensed table-responsive" style="overflow: hidden;">
+ <thead>
+ <tr>
+ </tr>
+ <tbody>
+ <tr>
+ <td class="data-row-template text-info">
+ Provide a phenotype '{{filetype | lower}}' file to preview.
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+{%endmacro%}