aboutsummaryrefslogtreecommitdiff
path: root/qc_app
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-12-07 12:40:37 +0300
committerFrederick Muriuki Muriithi2023-12-07 12:40:37 +0300
commit8760d188faebe552028f3142a821d0851df4e1e9 (patch)
treedc56fe37d79685c1107765d80ffe78085c7df7f1 /qc_app
parent493f8fbe747650a4fbac2e0b153ad0074b4f91e4 (diff)
downloadgn-uploader-8760d188faebe552028f3142a821d0851df4e1e9.tar.gz
Samples: Provide preview feature.
Diffstat (limited to 'qc_app')
-rw-r--r--qc_app/static/js/upload_samples.js132
-rw-r--r--qc_app/templates/samples/upload-samples.html56
2 files changed, 178 insertions, 10 deletions
diff --git a/qc_app/static/js/upload_samples.js b/qc_app/static/js/upload_samples.js
new file mode 100644
index 0000000..aed536f
--- /dev/null
+++ b/qc_app/static/js/upload_samples.js
@@ -0,0 +1,132 @@
+/*
+ * Read the file content and set the `data-preview-content` attribute on the
+ * file element
+ */
+function read_first_n_lines(event,
+ fileelement,
+ numlines,
+ firstlineheading = true) {
+ var thefile = fileelement.files[0];
+ var reader = new FileReader();
+ reader.addEventListener("load", (event) => {
+ var filecontent = event.target.result.split(
+ "\n").slice(
+ 0, (numlines + (firstlineheading ? 1 : 0))).map(
+ (line) => {return line.trim("\r");});
+ fileelement.setAttribute(
+ "data-preview-content", JSON.stringify(filecontent));
+ display_preview(event);
+ })
+ reader.readAsText(thefile);
+}
+
+function remove_rows(preview_table) {
+ var table_body = preview_table.getElementsByTagName("tbody")[0];
+ while(table_body.children.length > 0) {
+ table_body.removeChild(table_body.children.item(0));
+ }
+}
+
+/*
+ * Display error row
+ */
+function display_error_row(preview_table, error_message) {
+ remove_rows(preview_table);
+ row = document.createElement("tr");
+ cell = document.createElement("td");
+ cell.setAttribute("colspan", 4);
+ cell.innerHTML = error_message;
+ row.appendChild(cell);
+ preview_table.getElementsByTagName("tbody")[0].appendChild(row);
+}
+
+function strip(str, chars) {
+ var end = str.length;
+ var start = 0
+ for(var j = str.length; j > 0; j--) {
+ if(!chars.includes(str[j - 1])) {
+ break;
+ }
+ end = end - 1;
+ }
+ for(var i = 0; i < end; i++) {
+ if(!chars.includes(str[i])) {
+ break;
+ }
+ start = start + 1;
+ }
+ return str.slice(start, end);
+}
+
+function process_preview_data(preview_data, separator, delimiter) {
+ return preview_data.map((line) => {
+ return line.split(separator).map((field) => {
+ return strip(field, delimiter);
+ });
+ });
+}
+
+function render_preview(preview_table, preview_data) {
+ remove_rows(preview_table);
+ var table_body = preview_table.getElementsByTagName("tbody")[0];
+ preview_data.forEach((line) => {
+ var row = document.createElement("tr");
+ line.forEach((field) => {
+ var cell = document.createElement("td");
+ cell.innerHTML = field;
+ row.appendChild(cell);
+ });
+ table_body.appendChild(row);
+ });
+}
+
+/*
+ * Display a preview of the data, relying on the user's selection.
+ */
+function display_preview(event) {
+ var data_preview_table = document.getElementById("tbl:samples-preview");
+ remove_rows(data_preview_table);
+
+ var separator = document.getElementById("select:separator").value;
+ if(separator === "other") {
+ separator = document.getElementById("txt:separator").value;
+ }
+ if(separator == "") {
+ display_error_row(data_preview_table, "Please provide a separator.");
+ return false;
+ }
+
+ var delimiter = document.getElementById("txt:delimiter").value;
+
+ var firstlineheading = document.getElementById("chk:heading").checked;
+
+ var fileelement = document.getElementById("file:samples");
+ var preview_data = JSON.parse(
+ fileelement.getAttribute("data-preview-content") || "[]");
+ if(preview_data.length == 0) {
+ display_error_row(
+ data_preview_table,
+ "No file data to preview. Check that file is provided.");
+ }
+
+ render_preview(data_preview_table, process_preview_data(
+ preview_data.slice(0 + (firstlineheading ? 1 : 0)),
+ separator,
+ delimiter));
+}
+
+document.getElementById("chk:heading").addEventListener(
+ "change", display_preview);
+document.getElementById("select:separator").addEventListener(
+ "change", display_preview);
+document.getElementById("txt:separator").addEventListener(
+ "keyup", display_preview);
+document.getElementById("txt:delimiter").addEventListener(
+ "keyup", display_preview);
+document.getElementById("file:samples").addEventListener(
+ "change", (event) => {
+ read_first_n_lines(event,
+ document.getElementById("file:samples"),
+ 30,
+ document.getElementById("chk:heading").checked);
+ });
diff --git a/qc_app/templates/samples/upload-samples.html b/qc_app/templates/samples/upload-samples.html
index b19e38c..23dc8a8 100644
--- a/qc_app/templates/samples/upload-samples.html
+++ b/qc_app/templates/samples/upload-samples.html
@@ -46,13 +46,21 @@
<fieldset>
<input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
<label class="form-col-1">species:</label>
- <label class="form-col-2">{{species.SpeciesName}} [{{species.MenuName}}]</label>
+ <span class="form-col-2">{{species.SpeciesName}} [{{species.MenuName}}]</span>
</fieldset>
<fieldset>
<input type="hidden" name="inbredset_id" value="{{population.InbredSetId}}" />
<label class="form-col-1">grouping/population:</label>
- <label class="form-col-2">{{population.Name}} [{{population.FullName}}]</label>
+ <span class="form-col-2">{{population.Name}} [{{population.FullName}}]</span>
+ </fieldset>
+
+ <fieldset>
+ <input id="chk:heading"
+ type="checkbox"
+ name="first_line_heading"
+ class="form-col-1" />
+ <label for="chk:heading" class="form-col-2">first line is a heading?</label>
</fieldset>
<fieldset>
@@ -68,27 +76,34 @@
<option value=";">Semicolon</option>
<option value="other">Other</option>
</select>
- <input type="text" name="other_separator" class="form-col-2" />
- <label class="form-col-2">
+ <input id="txt:separator"
+ type="text"
+ name="other_separator"
+ class="form-col-2" />
+ <span class="form-col-2">
This is the character that separates the fields in your CSV file. If you
select "<strong>Other</strong>", then you must provide the separator in
the text field provided.
- </label>
+ </span>
</fieldset>
<fieldset>
<label for="txt:delimiter" class="form-col-1">field delimiter</label>
- <input type="text" name="field_delimiter" class="form-col-2" />
- <label class="form-col-2">
+ <input id="txt:delimiter"
+ type="text"
+ name="field_delimiter"
+ maxlength="1"
+ class="form-col-2" />
+ <span class="form-col-2">
If there is a character delimiting the string texts within particular
fields in your CSV, provide the character here. This can be left blank if
no such delimiters exist in your file.
- </label>
+ </span>
</fieldset>
<fieldset>
- <label for="file_upload" class="form-col-1">select file</label>
- <input type="file" name="samples_file" id="file_upload"
+ <label for="file:samples" class="form-col-1">select file</label>
+ <input type="file" name="samples_file" id="file:samples"
accept="text/csv, text/tab-separated-values"
class="form-col-2" />
</fieldset>
@@ -100,8 +115,29 @@
</fieldset>
</form>
+<table id="tbl:samples-preview">
+ <caption class="heading">preview content</caption>
+
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Name2</th>
+ <th>Symbol</th>
+ <th>Alias</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr id="default-row">
+ <td colspan="4">
+ Please make some selections to preview the data.</td>
+ </tr>
+ </tbody>
+</table>
+
{%endblock%}
{%block javascript%}
+<script src="/static/js/upload_samples.js" type="text/javascript"></script>
{%endblock%}