aboutsummaryrefslogtreecommitdiff
path: root/uploader/templates
diff options
context:
space:
mode:
Diffstat (limited to 'uploader/templates')
-rw-r--r--uploader/templates/base.html139
-rw-r--r--uploader/templates/cli-output.html8
-rw-r--r--uploader/templates/continue_from_create_dataset.html52
-rw-r--r--uploader/templates/continue_from_create_study.html52
-rw-r--r--uploader/templates/dbupdate_error.html12
-rw-r--r--uploader/templates/dbupdate_hidden_fields.html29
-rw-r--r--uploader/templates/errors_display.html47
-rw-r--r--uploader/templates/expression-data/base.html13
-rw-r--r--uploader/templates/expression-data/data-review.html85
-rw-r--r--uploader/templates/expression-data/index.html33
-rw-r--r--uploader/templates/expression-data/job-progress.html47
-rw-r--r--uploader/templates/expression-data/no-such-job.html15
-rw-r--r--uploader/templates/expression-data/parse-failure.html26
-rw-r--r--uploader/templates/expression-data/parse-results.html39
-rw-r--r--uploader/templates/expression-data/select-file.html115
-rw-r--r--uploader/templates/expression-data/select-population.html29
-rw-r--r--uploader/templates/final_confirmation.html47
-rw-r--r--uploader/templates/flash_messages.html25
-rw-r--r--uploader/templates/genotypes/base.html23
-rw-r--r--uploader/templates/genotypes/create-dataset.html82
-rw-r--r--uploader/templates/genotypes/index.html32
-rw-r--r--uploader/templates/genotypes/list-genotypes.html149
-rw-r--r--uploader/templates/genotypes/list-markers.html105
-rw-r--r--uploader/templates/genotypes/select-population.html25
-rw-r--r--uploader/templates/genotypes/view-dataset.html61
-rw-r--r--uploader/templates/http-error.html18
-rw-r--r--uploader/templates/index.html107
-rw-r--r--uploader/templates/insert_error.html32
-rw-r--r--uploader/templates/insert_progress.html46
-rw-r--r--uploader/templates/insert_success.html19
-rw-r--r--uploader/templates/login.html12
-rw-r--r--uploader/templates/macro-table-pagination.html26
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-base.html331
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-raw-files.html732
-rw-r--r--uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html207
-rw-r--r--uploader/templates/phenotypes/base.html19
-rw-r--r--uploader/templates/phenotypes/create-dataset.html108
-rw-r--r--uploader/templates/phenotypes/edit-phenotype.html332
-rw-r--r--uploader/templates/phenotypes/index.html21
-rw-r--r--uploader/templates/phenotypes/job-status.html155
-rw-r--r--uploader/templates/phenotypes/list-datasets.html68
-rw-r--r--uploader/templates/phenotypes/macro-display-pheno-dataset-card.html31
-rw-r--r--uploader/templates/phenotypes/macro-display-preview-table.html21
-rw-r--r--uploader/templates/phenotypes/macro-display-resumable-elements.html60
-rw-r--r--uploader/templates/phenotypes/review-job-data.html101
-rw-r--r--uploader/templates/phenotypes/select-population.html31
-rw-r--r--uploader/templates/phenotypes/view-dataset.html123
-rw-r--r--uploader/templates/phenotypes/view-phenotype.html135
-rw-r--r--uploader/templates/platforms/base.html13
-rw-r--r--uploader/templates/platforms/create-platform.html124
-rw-r--r--uploader/templates/platforms/index.html25
-rw-r--r--uploader/templates/platforms/list-platforms.html93
-rw-r--r--uploader/templates/populations/base.html18
-rw-r--r--uploader/templates/populations/create-population.html258
-rw-r--r--uploader/templates/populations/index.html28
-rw-r--r--uploader/templates/populations/list-populations.html93
-rw-r--r--uploader/templates/populations/macro-display-population-card.html41
-rw-r--r--uploader/templates/populations/macro-select-population.html52
-rw-r--r--uploader/templates/populations/rqtl2/create-tissue-success.html106
-rw-r--r--uploader/templates/populations/rqtl2/index.html54
-rw-r--r--uploader/templates/populations/rqtl2/no-such-job.html13
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-job-error.html39
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-job-results.html24
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-job-status.html20
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-qc-job-error.html120
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-qc-job-results.html66
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-qc-job-status.html41
-rw-r--r--uploader/templates/populations/rqtl2/rqtl2-qc-job-success.html37
-rw-r--r--uploader/templates/populations/rqtl2/select-geno-dataset.html69
-rw-r--r--uploader/templates/populations/rqtl2/select-population.html57
-rw-r--r--uploader/templates/populations/rqtl2/select-probeset-dataset.html191
-rw-r--r--uploader/templates/populations/rqtl2/select-probeset-study-id.html143
-rw-r--r--uploader/templates/populations/rqtl2/select-tissue.html115
-rw-r--r--uploader/templates/populations/rqtl2/summary-info.html65
-rw-r--r--uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-01.html276
-rw-r--r--uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-02.html33
-rw-r--r--uploader/templates/populations/view-population.html102
-rw-r--r--uploader/templates/samples/base.html12
-rw-r--r--uploader/templates/samples/index.html23
-rw-r--r--uploader/templates/samples/list-samples.html132
-rw-r--r--uploader/templates/samples/select-population.html26
-rw-r--r--uploader/templates/samples/upload-failure.html37
-rw-r--r--uploader/templates/samples/upload-progress.html31
-rw-r--r--uploader/templates/samples/upload-samples.html160
-rw-r--r--uploader/templates/samples/upload-success.html36
-rw-r--r--uploader/templates/select_dataset.html161
-rw-r--r--uploader/templates/select_platform.html82
-rw-r--r--uploader/templates/select_study.html108
-rw-r--r--uploader/templates/species/base.html17
-rw-r--r--uploader/templates/species/create-species.html148
-rw-r--r--uploader/templates/species/edit-species.html177
-rw-r--r--uploader/templates/species/list-species.html75
-rw-r--r--uploader/templates/species/macro-display-species-card.html22
-rw-r--r--uploader/templates/species/macro-select-species.html59
-rw-r--r--uploader/templates/species/view-species.html90
-rw-r--r--uploader/templates/stdout_output.html8
-rw-r--r--uploader/templates/unhandled_exception.html24
-rw-r--r--uploader/templates/upload_progress_indicator.html35
-rw-r--r--uploader/templates/worker_failure.html24
99 files changed, 7828 insertions, 0 deletions
diff --git a/uploader/templates/base.html b/uploader/templates/base.html
new file mode 100644
index 0000000..5eca445
--- /dev/null
+++ b/uploader/templates/base.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html lang="en">
+
+ <head>
+
+ <meta charset="UTF-8" />
+ <meta application-name="GeneNetwork Quality-Control Application" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ {%block extrameta%}{%endblock%}
+
+ <title>Data Upload and Quality Control: {%block title%}{%endblock%}</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="{{url_for('base.bootstrap',
+ filename='css/bootstrap.min.css')}}" />
+ <link rel="stylesheet" type="text/css"
+ href="{{url_for('base.bootstrap',
+ filename='css/bootstrap-theme.min.css')}}" />
+ <link rel="stylesheet" type="text/css" href="/static/css/styles.css" />
+ <link rel="stylesheet"
+ href="{{url_for('base.datatables', filename='css/jquery.dataTables.css')}}" />
+
+ {%block css%}{%endblock%}
+
+ </head>
+
+ <body>
+ <header id="header">
+ <span id="header-text">GeneNetwork</span>
+ <nav id="header-nav">
+ <ul class="nav justify-content-end">
+ <li>
+ {%if user_logged_in()%}
+ <a href="{{url_for('oauth2.logout')}}"
+ title="Log out of the system">
+ <span class="glyphicon glyphicon-user"></span>
+ Sign Out</a>
+ {%else%}
+ <a href="{{authserver_authorise_uri()}}"
+ title="Log in to the system">Sign In</a>
+ {%endif%}
+ </li>
+ </ul>
+ </nav>
+ </header>
+
+ <aside id="nav-sidebar">
+ <ul class="nav flex-column">
+ <li {%if activemenu=="home"%}class="activemenu"{%endif%}>
+ <a href="/" >Home</a></li>
+ <li {%if activemenu=="species"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.list_species')}}"
+ title="View and manage species information.">Species</a></li>
+ <li {%if activemenu=="platforms"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.platforms.index')}}"
+ title="View and manage species platforms.">Sequencing Platforms</a></li>
+ <li {%if activemenu=="populations"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.populations.index')}}"
+ title="View and manage species populations.">Populations</a></li>
+ <li {%if activemenu=="samples"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.populations.samples.index')}}"
+ title="Upload population samples.">Samples</a></li>
+ <li {%if activemenu=="genotypes"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.populations.genotypes.index')}}"
+ title="Upload Genotype data.">Genotype Data</a></li>
+ <!--
+ TODO: Maybe include menus here for managing studies and dataset or
+ maybe have the studies/datasets managed under their respective
+ sections, e.g. "Publish*" studies/datasets under the "Phenotypes"
+ section, "ProbeSet*" studies/datasets under the "Expression Data"
+ sections, etc.
+ -->
+ <li {%if activemenu=="phenotypes"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.index')}}"
+ title="Upload phenotype data.">Phenotype Data</a></li>
+ <!--
+ <li {%if activemenu=="expression-data"%}class="activemenu"{%endif%}>
+ <a href="{{url_for('species.populations.expression-data.index')}}"
+ title="Upload expression data."
+ class="not-implemented">Expression Data</a></li>
+ <li {%if activemenu=="individuals"%}class="activemenu"{%endif%}>
+ <a href="#"
+ class="not-implemented"
+ title="Upload individual data.">Individual Data</a></li>
+ <li {%if activemenu=="rna-seq"%}class="activemenu"{%endif%}>
+ <a href="#"
+ class="not-implemented"
+ title="Upload RNA-Seq data.">RNA-Seq Data</a></li>
+ <li {%if activemenu=="async-jobs"%}class="activemenu"{%endif%}>
+ <a href="#"
+ class="not-implemented"
+ title="View and manage the backgroud jobs you have running">
+ Background Jobs</a></li>
+ -->
+ </ul>
+ </aside>
+
+ <main id="main" class="main">
+
+ <div id="pagetitle" class="pagetitle">
+ <span class="title">Data Upload and Quality Control: {%block pagetitle%}{%endblock%}</span>
+ <!--
+ <nav>
+ <ol class="breadcrumb">
+ <li {%if activelink is not defined or activelink=="home"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('base.index')}}">Home</a>
+ </li>
+ {%block lvl1_breadcrumbs%}{%endblock%}
+ </ol>
+ </nav>
+ -->
+ </div>
+
+ <div id="all-content">
+ <div id="main-content">
+ {%block contents%}{%endblock%}
+ </div>
+ <div id="sidebar-content">
+ {%block sidebarcontents%}{%endblock%}
+ </div>
+ </div>
+ </main>
+
+
+ <script src="{{url_for('base.jquery',
+ filename='jquery.min.js')}}"></script>
+ <script src="{{url_for('base.bootstrap',
+ filename='js/bootstrap.min.js')}}"></script>
+ <script type="text/javascript" src="/static/js/misc.js"></script>
+ <script type="text/javascript"
+ src="{{url_for('base.datatables',
+ filename='js/jquery.dataTables.js')}}"></script>
+ {%block javascript%}{%endblock%}
+ </body>
+</html>
diff --git a/uploader/templates/cli-output.html b/uploader/templates/cli-output.html
new file mode 100644
index 0000000..64b1a9a
--- /dev/null
+++ b/uploader/templates/cli-output.html
@@ -0,0 +1,8 @@
+{%macro cli_output(job, stream)%}
+
+<h4 class="subheading">{{stream | upper}} Output</h4>
+<div class="cli-output" style="max-height: 10em; overflow: auto;">
+ <pre>{{job.get(stream, "")}}</pre>
+</div>
+
+{%endmacro%}
diff --git a/uploader/templates/continue_from_create_dataset.html b/uploader/templates/continue_from_create_dataset.html
new file mode 100644
index 0000000..03bb49c
--- /dev/null
+++ b/uploader/templates/continue_from_create_dataset.html
@@ -0,0 +1,52 @@
+{%extends "base.html"%}
+{%from "dbupdate_hidden_fields.html" import hidden_fields%}
+
+{%block title%}Create Study{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" href="/static/css/two-column-with-separator.css" />
+{%endblock%}
+
+{%block contents%}
+<h2 class="heading">{{filename}}: create study</h2>
+
+{%with messages = get_flashed_messages(with_categories=true)%}
+{%if messages:%}
+<ul>
+ {%for category, message in messages:%}
+ <li class="{{category}}">{{message}}</li>
+ {%endfor%}
+</ul>
+{%endif%}
+{%endwith%}
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.final_confirmation')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col1">
+ <legend>continue with new dataset</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, datasetid=datasetid, totallines=totallines)}}
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+
+<div class="row">
+ <p class="two-col-sep-separator">OR</p>
+</div>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.select_dataset')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col2">
+ <legend>Select from existing dataset</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, datasetid=datasetid, totallines=totallines)}}
+
+ <button type="submit" class="btn btn-primary">go back</button>
+ </form>
+</div>
+{%endblock%}
diff --git a/uploader/templates/continue_from_create_study.html b/uploader/templates/continue_from_create_study.html
new file mode 100644
index 0000000..34e6e5e
--- /dev/null
+++ b/uploader/templates/continue_from_create_study.html
@@ -0,0 +1,52 @@
+{%extends "base.html"%}
+{%from "dbupdate_hidden_fields.html" import hidden_fields%}
+
+{%block title%}Create Study{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" href="/static/css/two-column-with-separator.css" />
+{%endblock%}
+
+{%block contents%}
+<h2 class="heading">{{filename}}: create study</h2>
+
+{%with messages = get_flashed_messages(with_categories=true)%}
+{%if messages:%}
+<ul>
+ {%for category, message in messages:%}
+ <li class="{{category}}">{{message}}</li>
+ {%endfor%}
+</ul>
+{%endif%}
+{%endwith%}
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.select_dataset')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col1">
+ <legend>continue with new study</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, totallines=totallines)}}
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+
+<div class="row">
+ <p class="two-col-sep-separator">OR</p>
+</div>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.select_study')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col2">
+ <legend>Select from existing study</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, totallines=totallines)}}
+
+ <button type="submit" class="btn btn-primary">go back</button>
+ </form>
+</div>
+{%endblock%}
diff --git a/uploader/templates/dbupdate_error.html b/uploader/templates/dbupdate_error.html
new file mode 100644
index 0000000..e1359d2
--- /dev/null
+++ b/uploader/templates/dbupdate_error.html
@@ -0,0 +1,12 @@
+{%extends "base.html"%}
+
+{%block title%}DB Update Error{%endblock%}
+
+{%block contents%}
+<h1 class="heading">database update error</h2>
+
+<p class="alert-danger">
+ <strong>Database Update Error</strong>: {{error_message}}
+</p>
+
+{%endblock%}
diff --git a/uploader/templates/dbupdate_hidden_fields.html b/uploader/templates/dbupdate_hidden_fields.html
new file mode 100644
index 0000000..ccbc299
--- /dev/null
+++ b/uploader/templates/dbupdate_hidden_fields.html
@@ -0,0 +1,29 @@
+{%macro hidden_fields(filename, filetype):%}
+
+<!-- {{kwargs}}: mostly for accessing the kwargs in macro -->
+
+<input type="hidden" name="filename" value="{{filename}}" />
+<input type="hidden" name="filetype" value="{{filetype}}" />
+{%if kwargs.get("totallines")%}
+<input type="hidden" name="totallines" value="{{kwargs['totallines']}}" />
+{%endif%}
+{%if kwargs.get("species"):%}
+<input type="hidden" name="species" value="{{kwargs['species']}}" />
+{%endif%}
+{%if kwargs.get("genechipid"):%}
+<input type="hidden" name="genechipid" value="{{kwargs['genechipid']}}" />
+{%endif%}
+{%if kwargs.get("inbredsetid"):%}
+<input type="hidden" name="inbredsetid" value="{{kwargs['inbredsetid']}}" />
+{%endif%}
+{%if kwargs.get("tissueid"):%}
+<input type="hidden" name="tissueid" value="{{kwargs['tissueid']}}" />
+{%endif%}
+{%if kwargs.get("studyid"):%}
+<input type="hidden" name="studyid" value="{{kwargs['studyid']}}" />
+{%endif%}
+{%if kwargs.get("datasetid"):%}
+<input type="hidden" name="datasetid" value="{{kwargs['datasetid']}}" />
+{%endif%}
+
+{%endmacro%}
diff --git a/uploader/templates/errors_display.html b/uploader/templates/errors_display.html
new file mode 100644
index 0000000..715cfcf
--- /dev/null
+++ b/uploader/templates/errors_display.html
@@ -0,0 +1,47 @@
+{%macro errors_display(errors, no_error_msg, error_message, complete)%}
+
+{%if errors | length == 0 %}
+<span {%if complete%}class="alert-success"{%endif%}>{{no_error_msg}}</span>
+{%else %}
+<p class="alert-danger">{{error_message}}</p>
+
+<table class="table reports-table">
+ <thead>
+ <tr>
+ <th>line number</th>
+ <th>column(s)</th>
+ <th>error</th>
+ <th>error message</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for error in errors%}
+ <tr>
+ <td>{{error["line"]}}</td>
+ <td>
+ {%if isinvalidvalue(error):%}
+ {{error.column}}
+ {%elif isduplicateheading(error): %}
+ {{error.columns}}
+ {%else: %}
+ -
+ {%endif %}
+ </td>
+ <td>
+ {%if isinvalidvalue(error):%}
+ Invalid Value
+ {%elif isduplicateheading(error): %}
+ Duplicate Header
+ {%else%}
+ Inconsistent Columns
+ {%endif %}
+ </td>
+ <td>{{error["message"]}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+</table>
+{%endif%}
+
+{%endmacro%}
diff --git a/uploader/templates/expression-data/base.html b/uploader/templates/expression-data/base.html
new file mode 100644
index 0000000..d63fd7e
--- /dev/null
+++ b/uploader/templates/expression-data/base.html
@@ -0,0 +1,13 @@
+{%extends "populations/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="expression-data"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.expression-data.index')}}">
+ Expression Data</a>
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/expression-data/data-review.html b/uploader/templates/expression-data/data-review.html
new file mode 100644
index 0000000..c985b03
--- /dev/null
+++ b/uploader/templates/expression-data/data-review.html
@@ -0,0 +1,85 @@
+{%extends "base.html"%}
+
+{%block title%}Data Review{%endblock%}
+
+{%block contents%}
+<h1 class="heading">data review</h1>
+
+<div class="row">
+ <h2 id="data-concerns">Data Concerns</h2>
+ <p>The following are some of the requirements that the data in your file
+ <strong>MUST</strong> fulfil before it is considered valid for this system:
+ </p>
+
+ <ol>
+ <li>File headings
+ <ul>
+ <li>The first row in the file should contains the headings. The number of
+ headings in this first row determines the number of columns expected for
+ all other lines in the file.</li>
+ <li>Each heading value in the first row MUST appear in the first row
+ <strong>ONE AND ONLY ONE</strong> time</li>
+ <li>The sample/cases (previously 'strains') headers in your first row will be
+ against those in the <a href="https://genenetwork.org"
+ title="Link to the GeneNetwork service">
+ GeneNetwork</a> database.<br />
+ <small class="text-muted">
+ If you encounter an error saying your sample(s)/case(s) do not exist
+ in the GeneNetwork database, then you will have to use the
+ <a href="{{url_for('species.populations.samples.index')}}"
+ title="Upload samples/cases feature">Upload Samples/Cases</a>
+ option on this system to upload them.
+ </small>
+ </ul>
+ </li>
+
+ <li>Data
+ <ol>
+ <li><strong>NONE</strong> of the data cells/fields is allowed to be empty.
+ All fields/cells <strong>MUST</strong> contain a value.</li>
+ <li>The first column of the data rows will be considered a textual field,
+ holding the "identifier" for that row<li>
+ <li>Except for the first column/field for each data row,
+ <strong>NONE</strong> of the data columns/cells/fields should contain
+ spurious characters like `eeeee`, `5.555iloveguix`, etc...<br />
+ All of them should be decimal values</li>
+ <li>decimal numbers must conform to the following criteria:
+ <ul>
+ <li>when checking an average file decimal numbers must have exactly three
+ decimal places to the right of the decimal point.</li>
+ <li>when checking a standard error file decimal numbers must have six or
+ greater decimal places to the right of the decimal point.</li>
+ <li>there must be a number to the left side of the decimal place
+ (e.g. 0.55555 is allowed but .55555 is not).</li>
+ </ul>
+ </li>
+ </ol>
+ </li>
+ </ol>
+</div>
+
+
+<div class="row">
+ <h2 id="file-types">Supported File Types</h2>
+ We support the following file types:
+
+ <ul>
+ <li>Tab-Separated value files (.tsv)
+ <ul>
+ <li>The <strong>TAB</strong> character is used to separate the fields of each
+ column</li>
+ <li>The values of each field <strong>ARE NOT</strong> quoted.</li>
+ <li>Here is an
+ <a href="https://gitlab.com/fredmanglis/gnqc_py/-/blob/main/tests/test_data/no_data_errors.tsv"
+ target="_blank">example file</a> with a single data row.</li>
+ </ul>
+ </li>
+ <li>.txt files: Content has the same format as .tsv file above</li>
+ <li>.zip files: each zip file should contain
+ <strong>ONE AND ONLY ONE</strong> file of the .tsv or .txt type above.
+ <br />Any zip file with more than one file is invalid, and so is an empty
+ zip file.</li>
+ </ul>
+
+</div>
+{%endblock%}
diff --git a/uploader/templates/expression-data/index.html b/uploader/templates/expression-data/index.html
new file mode 100644
index 0000000..9ba3582
--- /dev/null
+++ b/uploader/templates/expression-data/index.html
@@ -0,0 +1,33 @@
+{%extends "expression-data/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Expression Data{%endblock%}
+
+{%block pagetitle%}Expression Data{%endblock%}
+
+{%block breadcrumb%}
+<li class="breadcrumb-item">
+ <a href="{{url_for('base.index')}}">Home</a>
+</li>
+<li class="breadcrumb-item active">
+ <a href="{{url_for('species.populations.expression-data.index')}}"
+ title="Upload expression data.">
+ Expression Data</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+<div class="row">
+ <h2 class="heading">Expression Data</h2>
+ {{flash_all_messages()}}
+
+ <p>This section allows you to enter the expression data for your experiment.
+ You will need to select the species that your data concerns below.</p>
+</div>
+
+<div class="row">
+ {{select_species_form(url_for("species.populations.expression-data.index"),
+ species)}}
+</div>
+{%endblock%}
diff --git a/uploader/templates/expression-data/job-progress.html b/uploader/templates/expression-data/job-progress.html
new file mode 100644
index 0000000..ef264e1
--- /dev/null
+++ b/uploader/templates/expression-data/job-progress.html
@@ -0,0 +1,47 @@
+{%extends "base.html"%}
+{%from "errors_display.html" import errors_display%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block extrameta%}
+<meta http-equiv="refresh" content="5">
+{%endblock%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<h1 class="heading">{{job_name}}</h2>
+
+<div class="row">
+ <form action="{{url_for('species.populations.expression-data.abort',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}" method="POST">
+ <legend class="heading">Status</legend>
+ <div class="form-group">
+ <label for="job_status" class="form-label">status:</label>
+ <span class="form-text">{{job_status}}: {{message}}</span><br />
+ </div>
+
+ <div class="form-group">
+ <label for="job_{{job_id}}" class="form-label">parsing: </label>
+ <progress id="job_{{job_id}}"
+ value="{{progress/100}}"
+ class="form-control">
+ {{progress}}</progress>
+ <span class="form-text text-muted">{{"%.2f" | format(progress)}}%</span>
+ </div>
+
+ <input type="hidden" name="job_id" value="{{job_id}}" />
+
+ <button type="submit" class="btn btn-danger">Abort</button>
+ </form>
+</div>
+
+<div class="row">
+ {{errors_display(errors, "No errors found so far", "We have found the following errors so far", False)}}
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/expression-data/no-such-job.html b/uploader/templates/expression-data/no-such-job.html
new file mode 100644
index 0000000..d22c429
--- /dev/null
+++ b/uploader/templates/expression-data/no-such-job.html
@@ -0,0 +1,15 @@
+{%extends "base.html"%}
+
+{%block extrameta%}
+<meta http-equiv="refresh"
+ content="5;url={{url_for('species.populations.expression-data.index.upload_file')}}">
+{%endblock%}
+
+{%block title%}No Such Job{%endblock%}
+
+{%block contents%}
+<h1 class="heading">No Such Job: {{job_id}}</h2>
+
+<p>No job, with the id '<em>{{job_id}}</em>' was found!</p>
+
+{%endblock%}
diff --git a/uploader/templates/expression-data/parse-failure.html b/uploader/templates/expression-data/parse-failure.html
new file mode 100644
index 0000000..31f6be8
--- /dev/null
+++ b/uploader/templates/expression-data/parse-failure.html
@@ -0,0 +1,26 @@
+{%extends "base.html"%}
+
+{%block title%}Worker Failure{%endblock%}
+
+{%block contents%}
+<h1 class="heading">Worker Failure</h1>
+
+<p>
+ There was an error while parsing your file.
+</p>
+
+<p>
+ Please notify the developers of this issue when you encounter it,
+ providing the information below.
+</p>
+
+<h4>Debugging Information</h4>
+
+<ul>
+ <li><strong>job id</strong>: {{job["job_id"]}}</li>
+ <li><strong>filename</strong>: {{job["filename"]}}</li>
+ <li><strong>line number</strong>: {{job["line_number"]}}</li>
+ <li><strong>Progress</strong>: {{job["percent"]}} %</li>
+</ul>
+
+{%endblock%}
diff --git a/uploader/templates/expression-data/parse-results.html b/uploader/templates/expression-data/parse-results.html
new file mode 100644
index 0000000..03a23e2
--- /dev/null
+++ b/uploader/templates/expression-data/parse-results.html
@@ -0,0 +1,39 @@
+{%extends "base.html"%}
+{%from "errors_display.html" import errors_display%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Parse Results{%endblock%}
+
+{%block contents%}
+
+<div class="row">
+ <h2 class="heading">{{job_name}}: parse results</h2>
+
+ {%if user_aborted%}
+ <span class="alert-warning">Job aborted by the user</span>
+ {%endif%}
+
+ {{errors_display(errors, "No errors found in the file", "We found the following errors", True)}}
+
+ {%if errors | length == 0 and not user_aborted %}
+ <form method="post" action="{{url_for('dbinsert.select_platform')}}">
+ <input type="hidden" name="job_id" value="{{job_id}}" />
+ <input type="submit" value="update database" class="btn btn-primary" />
+ </form>
+ {%endif%}
+
+ {%if errors | length > 0 or user_aborted %}
+ <br />
+ <a href="{{url_for('species.populations.expression-data.upload_file',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Back to index page."
+ class="btn btn-primary">Go back</a>
+
+ {%endif%}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/expression-data/select-file.html b/uploader/templates/expression-data/select-file.html
new file mode 100644
index 0000000..4ca461e
--- /dev/null
+++ b/uploader/templates/expression-data/select-file.html
@@ -0,0 +1,115 @@
+{%extends "expression-data/base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+{%from "upload_progress_indicator.html" import upload_progress_indicator%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Expression Data &mdash; Upload Data{%endblock%}
+
+{%block pagetitle%}Expression Data &mdash; Upload Data{%endblock%}
+
+{%block contents%}
+{{upload_progress_indicator()}}
+
+<div class="row">
+ <h2 class="heading">Upload Expression Data</h2>
+
+ <p>This feature enables you to upload expression data. It expects the data to
+ be in <strong>tab-separated values (TSV)</strong> files. The data should be
+ a simple matrix of <em>phenotype × sample</em>, i.e. The first column is a
+ list of the <em>phenotypes</em> and the first row is a list of
+ <em>samples/cases</em>.</p>
+
+ <p>If you haven't done so please go to this page to learn the requirements for
+ file formats and helpful suggestions to enter your data in a fast and easy
+ way.</p>
+
+ <ol>
+ <li><strong>PLEASE REVIEW YOUR DATA.</strong>Make sure your data complies
+ with our system requirements. (
+ <a href="{{url_for('species.populations.expression-data.data_review')}}#data-concerns"
+ title="Details for the data expectations.">Help</a>
+ )</li>
+ <li><strong>UPLOAD YOUR DATA FOR DATA VERIFICATION.</strong> We accept
+ <strong>.csv</strong>, <strong>.txt</strong> and <strong>.zip</strong>
+ files (<a href="{{url_for('species.populations.expression-data.data_review')}}#file-types"
+ title="Details for the data expectations.">Help</a>)</li>
+ </ol>
+</div>
+
+<div class="row">
+ <form action="{{url_for(
+ 'species.populations.expression-data.upload_file',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ method="POST"
+ enctype="multipart/form-data"
+ id="frm-upload-expression-data">
+ {{flash_messages("error-expr-data")}}
+
+ <div class="form-group">
+ <legend class="heading">File Type</legend>
+
+ <div class="radio">
+ <label for="filetype_average" class="form-check-label">
+ <input type="radio" name="filetype" value="average" id="filetype_average"
+ required="required" class="form-check-input" />
+ Average</label>
+ <p class="form-text text-muted">
+ <small>The averages data …</small></p>
+ </div>
+
+ <div class="radio">
+ <label for="filetype_standard_error" class="form-check-label">
+ <input type="radio" name="filetype" value="standard-error"
+ id="filetype_standard_error" required="required"
+ class="form-check-input" />
+ Standard Error
+ </label>
+ <p class="form-text text-muted">
+ <small>The standard errors computed from the averages …</small></p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <span id="no-file-error" class="alert-danger" style="display: none;">
+ No file selected
+ </span>
+ <label for="file_upload" class="form-label">Select File</label>
+ <input type="file" name="qc_text_file" id="file_upload"
+ accept="text/plain, text/tab-separated-values, application/zip"
+ class="form-control"/>
+ <p class="form-text text-muted">
+ <small>Select the file to upload.</small></p>
+ </div>
+
+ <button type="submit"
+ class="btn btn-primary"
+ data-toggle="modal"
+ data-target="#upload-progress-indicator">upload file</button>
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/upload_progress.js"></script>
+<script type="text/javascript">
+ function setup_formdata(form) {
+ var formdata = new FormData();
+ formdata.append(
+ "qc_text_file",
+ form.querySelector("input[type='file']").files[0]);
+ formdata.append(
+ "filetype",
+ selected_filetype(
+ Array.from(form.querySelectorAll("input[type='radio']"))));
+ return formdata;
+ }
+
+ setup_upload_handlers(
+ "frm-upload-expression-data", make_data_uploader(setup_formdata));
+</script>
+{%endblock%}
diff --git a/uploader/templates/expression-data/select-population.html b/uploader/templates/expression-data/select-population.html
new file mode 100644
index 0000000..8555e27
--- /dev/null
+++ b/uploader/templates/expression-data/select-population.html
@@ -0,0 +1,29 @@
+{%extends "expression-data/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+
+{%block title%}Expression Data{%endblock%}
+
+{%block pagetitle%}Expression Data{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>You have selected the species. Now you need to select the population that
+ the expression data belongs to.</p>
+</div>
+
+<div class="row">
+ {{select_population_form(url_for(
+ "species.populations.expression-data.select_population",
+ species_id=species.SpeciesId),
+ populations)}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/final_confirmation.html b/uploader/templates/final_confirmation.html
new file mode 100644
index 0000000..0727fc8
--- /dev/null
+++ b/uploader/templates/final_confirmation.html
@@ -0,0 +1,47 @@
+{%extends "base.html"%}
+{%from "dbupdate_hidden_fields.html" import hidden_fields%}
+
+{%block title%}Confirmation{%endblock%}
+
+{%macro display_item(item_name, item_data):%}
+<li>
+ <strong>{{item_name}}</strong>
+ {%if item_data%}
+ <ul>
+ {%for term,value in item_data.items():%}
+ <li><strong>{{term}}:</strong> {{value}}</li>
+ {%endfor%}
+ </ul>
+ {%endif%}
+</li>
+{%endmacro%}
+
+{%block contents%}
+<h2 class="heading">Final Confirmation</h2>
+
+<div class="two-col-sep-col1">
+ <p><strong>Selected Data</strong></p>
+ <ul>
+ <li><strong>File</strong>
+ <ul>
+ <li><strong>Filename</strong>: {{filename}}</li>
+ <li><strong>File Type</strong>: {{filetype}}</li>
+ </ul>
+ </li>
+ {{display_item("Species", the_species)}}
+ {{display_item("Platform", platform)}}
+ {{display_item("Study", study)}}
+ {{display_item("Dataset", dataset)}}
+ </ul>
+</div>
+
+<form method="POST" action="{{url_for('dbinsert.insert_data')}}">
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid,datasetid=datasetid, totallines=totallines)}}
+ <fieldset>
+ <input type="submit" class="btn btn-primary" value="confirm" />
+ </fieldset>
+</form>
+</div>
+{%endblock%}
diff --git a/uploader/templates/flash_messages.html b/uploader/templates/flash_messages.html
new file mode 100644
index 0000000..b7af178
--- /dev/null
+++ b/uploader/templates/flash_messages.html
@@ -0,0 +1,25 @@
+{%macro flash_all_messages()%}
+{%with messages = get_flashed_messages(with_categories=true)%}
+{%if messages:%}
+<ul>
+ {%for category, message in messages:%}
+ <li class="{{category}}">{{message}}</li>
+ {%endfor%}
+</ul>
+{%endif%}
+{%endwith%}
+{%endmacro%}
+
+{%macro flash_messages(filter_class)%}
+{%with messages = get_flashed_messages(with_categories=true)%}
+{%if messages:%}
+<ul>
+ {%for category, message in messages:%}
+ {%if filter_class in category%}
+ <li class="{{category}}">{{message}}</li>
+ {%endif%}
+ {%endfor%}
+</ul>
+{%endif%}
+{%endwith%}
+{%endmacro%}
diff --git a/uploader/templates/genotypes/base.html b/uploader/templates/genotypes/base.html
new file mode 100644
index 0000000..7d61312
--- /dev/null
+++ b/uploader/templates/genotypes/base.html
@@ -0,0 +1,23 @@
+{%extends "populations/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="genotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ {%if population is mapping%}
+ <a href="{{url_for('species.populations.genotypes.list_genotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">
+ {%if dataset is defined and dataset is mapping%}
+ {{dataset.Name}}
+ {%else%}
+ Genotypes
+ {%endif%}</a>
+ {%else%}
+ <a href="{{url_for('species.populations.genotypes.index')}}">Genotypes</a>
+ {%endif%}
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/genotypes/create-dataset.html b/uploader/templates/genotypes/create-dataset.html
new file mode 100644
index 0000000..10331c1
--- /dev/null
+++ b/uploader/templates/genotypes/create-dataset.html
@@ -0,0 +1,82 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Genotypes — Create Dataset{%endblock%}
+
+{%block pagetitle%}Genotypes — Create Dataset{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="create-dataset"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">Create Dataset</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <form id="frm-geno-create-dataset"
+ method="POST"
+ action="{{url_for('species.populations.genotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">
+ <legend>Create a new Genotype Dataset</legend>
+
+ <div class="form-group">
+ <label for="txt-geno-dataset-name" class="form-label">Name</label>
+ <input type="text"
+ id="txt-geno-dataset-name"
+ name="geno-dataset-name"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>This is a short representative, but constrained name for the genotype
+ dataset.<br />
+ The field will only accept letters ('A-Za-z'), numbers (0-9), hyphens
+ and underscores. Any other character will cause the name to be
+ rejected.</p></small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-geno-dataset-fullname" class="form-label">Full Name</label>
+ <input type="text"
+ id="txt-geno-dataset-fullname"
+ name="geno-dataset-fullname"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>This is a longer, more descriptive name for your dataset.</p></small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-geno-dataset-shortname"
+ class="form-label">Short Name</label>
+ <input type="text"
+ id="txt-geno-dataset-shortname"
+ name="geno-dataset-shortname"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>A short name for your dataset. If you leave this field blank, the
+ short name will be set to the same value as the
+ "<strong>Name</strong>" field above.</p></small>
+ </div>
+
+ <div class="form-group">
+ <input type="submit"
+ class="btn btn-primary"
+ value="create dataset" />
+ </div>
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/genotypes/index.html b/uploader/templates/genotypes/index.html
new file mode 100644
index 0000000..b50ebc5
--- /dev/null
+++ b/uploader/templates/genotypes/index.html
@@ -0,0 +1,32 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ This section allows you to upload genotype information for your experiments,
+ in the case that you have not previously done so.
+ </p>
+ <p>
+ We'll need to link the genotypes to the species and population, so do please
+ go ahead and select those in the next two steps.
+ </p>
+</div>
+
+<div class="row">
+ {{select_species_form(url_for("species.populations.genotypes.index"),
+ species)}}
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/species.js"></script>
+{%endblock%}
diff --git a/uploader/templates/genotypes/list-genotypes.html b/uploader/templates/genotypes/list-genotypes.html
new file mode 100644
index 0000000..0f074fd
--- /dev/null
+++ b/uploader/templates/genotypes/list-genotypes.html
@@ -0,0 +1,149 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-genotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.list_genotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List genotypes</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <h2>Genetic Markers</h2>
+ <p>There are a total of {{total_markers}} currently registered genetic markers
+ for the "{{species.FullName}}" species. You can click
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="View genetic markers for species '{{species.FullName}}">
+ this link to view the genetic markers
+ </a>.
+ </p>
+</div>
+
+<div class="row">
+ <h2>Genotype Encoding</h2>
+ <p>
+ The genotype encoding used for the "{{population.FullName}}" population from
+ the "{{species.FullName}}" species is as shown in the table below.
+ </p>
+ <table class="table">
+
+ <thead>
+ <tr>
+ <th>Allele Type</th>
+ <th>Allele Symbol</th>
+ <th>Allele Value</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for row in genocode%}
+ <tr>
+ <td>{{row.AlleleType}}</td>
+ <td>{{row.AlleleSymbol}}</td>
+ <td>{{row.DatabaseValue if row.DatabaseValue is not none else "NULL"}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="7" class="text-info">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ There is no explicit genotype encoding defined for this population.
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+
+ {%if genocode | length < 1%}
+ <a href="#add-genotype-encoding"
+ title="Add a genotype encoding system for this population"
+ class="btn btn-primary not-implemented">
+ add genotype encoding
+ </a>
+ {%endif%}
+</div>
+
+<div class="row text-danger">
+ <h3>Some Important Concepts to Consider/Remember</h3>
+ <ul>
+ <li>Reference vs. Non-reference alleles</li>
+ <li>In <em>GenoCode</em> table, items are ordered by <strong>InbredSet</strong></li>
+ </ul>
+ <h3>Possible references</h3>
+ <ul>
+ <li>https://mr-dictionary.mrcieu.ac.uk/term/genotype/</li>
+ <li>https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7363099/</li>
+ </ul>
+</div>
+
+<div class="row">
+ <h2>Genotype Datasets</h2>
+
+ <p>The genotype data is organised under various genotype datasets. You can
+ click on the link for the relevant dataset to view a little more information
+ about it.</p>
+
+ {%if dataset is not none%}
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Full Name</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr>
+ <td>{{dataset.Name}}</td>
+ <td><a href="{{url_for('species.populations.genotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}"
+ title="View details regarding and manage dataset '{{dataset.FullName}}'">
+ {{dataset.FullName}}</a></td>
+ </tr>
+ </tbody>
+ </table>
+ {%else%}
+ <p class="text-warning">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ There is no genotype dataset defined for this population.
+ </p>
+ <p>
+ <a href="{{url_for('species.populations.genotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Create a new genotype dataset for the '{{population.FullName}}' population for the '{{species.FullName}}' species."
+ class="btn btn-primary">
+ create new genotype dataset</a></p>
+ {%endif%}
+</div>
+<div class="row text-warning">
+ <p>
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>NOTE</strong>: Currently the GN2 (and related) system(s) expect a
+ single genotype dataset. If there is more than one, the system apparently
+ fails in unpredictable ways.
+ </p>
+ <p>Fix this to allow multiple datasets, each with a different assembly from
+ all the rest.</p>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/genotypes/list-markers.html b/uploader/templates/genotypes/list-markers.html
new file mode 100644
index 0000000..a705ae3
--- /dev/null
+++ b/uploader/templates/genotypes/list-markers.html
@@ -0,0 +1,105 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Genotypes: List Markers{%endblock%}
+
+{%block pagetitle%}Genotypes: List Markers{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-markers"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List markers</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+{%if markers | length > 0%}
+<div class="row">
+ <p>
+ There are a total of {{total_markers}} genotype markers for this species.
+ </p>
+ <div class="row">
+ <div class="col-md-2" style="text-align: start;">
+ {%if start_from > 0%}
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ start_from=start_from-count,
+ count=count)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
+ <div class="col-md-8" style="text-align: center;">
+ Displaying markers {{start_from+1}} to {{start_from+count if start_from+count < total_markers else total_markers}} of
+ {{total_markers}}
+ </div>
+ <div class="col-md-2" style="text-align: end;">
+ {%if start_from + count < total_markers%}
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ start_from=start_from+count,
+ count=count)}}">
+ Next
+ <span class="glyphicon glyphicon-forward"></span>
+ </a>
+ {%endif%}
+ </div>
+ </div>
+ <table class="table">
+ <thead>
+ <tr>
+ <th title="">#</th>
+ <th title="">Marker Name</th>
+ <th title="Chromosome">Chr</th>
+ <th title="Physical location of the marker in megabasepairs">
+ Location (Mb)</th>
+ <th title="">Source</th>
+ <th title="">Source2</th>
+ </thead>
+
+ <tbody>
+ {%for marker in markers%}
+ <tr>
+ <td>{{marker.sequence_number}}</td>
+ <td>{{marker.Marker_Name}}</td>
+ <td>{{marker.Chr}}</td>
+ <td>{{marker.Mb}}</td>
+ <td>{{marker.Source}}</td>
+ <td>{{marker.Source2}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+</div>
+{%else%}
+<div class="row">
+ <p class="text-warning">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ This species does not currently have any genetic markers uploaded, therefore,
+ there is nothing to display here.
+ </p>
+ <p>
+ <a href="#add-genetic-markers-for-species-{{species.SpeciesId}}"
+ title="Add genetic markers for this species"
+ class="btn btn-primary">
+ add genetic markers
+ </a>
+ </p>
+</div>
+{%endif%}
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/genotypes/select-population.html b/uploader/templates/genotypes/select-population.html
new file mode 100644
index 0000000..acdd063
--- /dev/null
+++ b/uploader/templates/genotypes/select-population.html
@@ -0,0 +1,25 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ {{select_population_form(url_for("species.populations.genotypes.select_population", species_id=species.SpeciesId), species, populations)}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/populations.js"></script>
+{%endblock%}
diff --git a/uploader/templates/genotypes/view-dataset.html b/uploader/templates/genotypes/view-dataset.html
new file mode 100644
index 0000000..e7ceb36
--- /dev/null
+++ b/uploader/templates/genotypes/view-dataset.html
@@ -0,0 +1,61 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Genotypes: View Dataset{%endblock%}
+
+{%block pagetitle%}Genotypes: View Dataset{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="view-dataset"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">view dataset</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <h2>Genotype Dataset Details</h2>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Full Name</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr>
+ <td>{{dataset.Name}}</td>
+ <td>{{dataset.FullName}}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<div class="row text-warning">
+ <h2>Assembly Details</h2>
+
+ <p>Maybe include the assembly details here if found to be necessary.</p>
+</div>
+
+<div class="row">
+ <h2>Genotype Data</h2>
+
+ <p class="text-danger">
+ Provide link to enable uploading of genotype data here.</p>
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/http-error.html b/uploader/templates/http-error.html
new file mode 100644
index 0000000..374fb86
--- /dev/null
+++ b/uploader/templates/http-error.html
@@ -0,0 +1,18 @@
+{%extends "base.html"%}
+
+{%block title%}HTTP Error: {{exc.code}}{%endblock%}
+
+{%block contents%}
+<h1>{{exc.code}}: {{exc.description}}</h1>
+
+<div class="row">
+ <p>
+ You attempted to access {{request_url}} which failed with the following
+ error:
+ </p>
+</div>
+
+<div class="row">
+ <pre>{{"\n".join(trace)}}</pre>
+</div>
+{%endblock%}
diff --git a/uploader/templates/index.html b/uploader/templates/index.html
new file mode 100644
index 0000000..aa1414e
--- /dev/null
+++ b/uploader/templates/index.html
@@ -0,0 +1,107 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Home{%endblock%}
+
+{%block pagetitle%}Home{%endblock%}
+
+{%block contents%}
+
+<div class="row">
+ {{flash_all_messages()}}
+ <div class="explainer">
+ <p>Welcome to the <strong>GeneNetwork Data Upload and Quality Control
+ System</strong>.</p>
+ <p>This tool helps you prepare and upload research data to GeneNetwork for
+ analysis.</p>
+
+ <h2 class="heading">Getting Started</h2>
+ <p>The sections below explain the features of the system. Review this guide
+ to learn how to use the system.</p>
+
+ {%block extrapageinfo%}{%endblock%}
+
+ <h3 class="subheading">Species</h3>
+
+ <p>GeneNetwork supports genetic studies across multiple species (e.g. mice
+ [Mus musculus], human [homo sapiens], rats [Rattus norvegicus], etc.) .
+ Here you can:</p>
+ <ul>
+ <li>View all species that are currently supported</li>
+ <li>Add new species not yet in the system</li>
+ </ul>
+
+ <h3 class="subheading">Populations</h3>
+
+ <p>A "population" refers to a specific subgroup within a species that you’re
+ studying (e.g., BXD mice). Here you can:</p>
+ <ul>
+ <li>View the populations that exist for a selected species</li>
+ <li>Add new populations of study for a selected species</li>
+ </ul>
+
+ <h3 class="subheading">Samples</h3>
+
+ <p>Manage individual specimens or cases used in your experiments. These
+ include:</p>
+
+ <ul>
+ <li>Experimental subjects</li>
+ <li>Data sources (e.g., tissue samples, clinical cases)</li>
+ <li>Strain means (instead of entering multiple BXD1 individuals, for
+ example, the mean would be entered for a single BXD1 strain)</li>
+ </ul>
+
+
+ <h3 class="subheading">Genotype Data</h3>
+
+ <p>Upload and review genetic markers and allele encodings for your
+ population. Key details:</p>
+
+ <ul>
+ <li>Markers are species-level (e.g., mouse SNP databases).</li>
+ <li>Allele data is population-specific (tied to your experimental
+ samples).</li>
+ </ul>
+
+ <p><strong>Requirement</strong>: Samples must already have been registered
+ in the system before uploading genotype data.</p>
+
+ <h3 class="subheading">Phenotype Data</h3>
+
+ <p>Phenotypes are the visible traits or features of a living thing. For
+ example, phenotypes include:</p>
+
+ <ul>
+ <li>Weight</li>
+ <li>Height</li>
+ <li>Color (such as the color of fur or eyes)</li>
+ </ul>
+
+ <p>This part of the system will allow you to upload and manage the values
+ for different phenotypes from various samples in your studies.</p>
+
+ <!--
+
+ <h3 class="subheading">Expression Data</h3>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+
+ <h3 class="subheading">Individual Data</h3>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+
+ <h3 class="subheading">RNA-Seq Data</h3>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+ </div>
+ -->
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/insert_error.html b/uploader/templates/insert_error.html
new file mode 100644
index 0000000..5301288
--- /dev/null
+++ b/uploader/templates/insert_error.html
@@ -0,0 +1,32 @@
+{%extends "base.html"%}
+
+{%block title%}Data Insertion Failure{%endblock%}
+
+{%block contents%}
+<h1 class="heading">Insertion Failure</h1>
+
+<div class="row">
+ <p>
+ There was an error inserting data into the database
+ </p>
+
+ <p>
+ Please notify the developers of this issue when you encounter it,
+ providing the information below.
+ </p>
+
+ <h4>Debugging Information</h4>
+
+ <ul>
+ <li><strong>job id</strong>: {{job["jobid"]}}</li>
+ </ul>
+</div>
+
+<div class="row">
+ <h4>STDERR Output</h4>
+ <pre class="cli-output">
+ {{job["stderr"]}}
+ </pre>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/insert_progress.html b/uploader/templates/insert_progress.html
new file mode 100644
index 0000000..52177d6
--- /dev/null
+++ b/uploader/templates/insert_progress.html
@@ -0,0 +1,46 @@
+{%extends "base.html"%}
+{%from "stdout_output.html" import stdout_output%}
+
+{%block extrameta%}
+<meta http-equiv="refresh" content="5">
+{%endblock%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<h1 class="heading">{{job_name}}</h1>
+
+<div class="row">
+ <form>
+ <div class="form-group">
+ <label for="job_status" class="form-label">status:</label>
+ <span class="form-text">{{job_status}}: {{message}}</span>
+ </div>
+
+{%if job.get("stdout", "").split("\n\n") | length < 3 %}
+{%set lines = 0%}
+{%else%}
+{%set lines = (job.get("stdout", "").split("\n\n") | length / 3) %}
+{%endif%}
+{%set totallines = job.get("totallines", lines+3) | int %}
+{%if totallines > 1000 %}
+{%set fraction = ((lines*1000)/totallines) %}
+{%else%}
+{%set fraction = (lines/totallines)%}
+{%endif%}
+
+ <div class="form-group">
+ <label for="job_{{job_id}}" class="form-label">inserting: </label>
+ <progress id="jobs_{{job_id}}"
+ value="{{(fraction)}}"
+ class="form-control">{{fraction*100}}</progress>
+ <span class="form-text text-muted">
+ {{"%.2f" | format(fraction * 100 | float)}}%</span>
+ </div>
+ </form>
+</div>
+
+
+{{stdout_output(job)}}
+
+{%endblock%}
diff --git a/uploader/templates/insert_success.html b/uploader/templates/insert_success.html
new file mode 100644
index 0000000..7e1fa8d
--- /dev/null
+++ b/uploader/templates/insert_success.html
@@ -0,0 +1,19 @@
+{%extends "base.html"%}
+{%from "stdout_output.html" import stdout_output%}
+
+{%block title%}Insertion Success{%endblock%}
+
+{%block contents%}
+<h1 class="heading">Insertion Success</h1>
+
+<div class="row">
+<p>Data inserted successfully!</p>
+
+<p>The following queries were run:</p>
+</div>
+
+<div class="row">
+ {{stdout_output(job)}}
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/login.html b/uploader/templates/login.html
new file mode 100644
index 0000000..e76c644
--- /dev/null
+++ b/uploader/templates/login.html
@@ -0,0 +1,12 @@
+{%extends "index.html"%}
+
+{%block title%}Data Upload{%endblock%}
+
+{%block pagetitle%}log in{%endblock%}
+
+{%block extrapageinfo%}
+<p class="text-dark">
+ You <strong>need to
+ <a href="{{authserver_authorise_uri()}}"
+ title="Sign in to the system">sign in</a></strong> to use this system.</p>
+{%endblock%}
diff --git a/uploader/templates/macro-table-pagination.html b/uploader/templates/macro-table-pagination.html
new file mode 100644
index 0000000..292c531
--- /dev/null
+++ b/uploader/templates/macro-table-pagination.html
@@ -0,0 +1,26 @@
+{%macro table_pagination(start_at, page_count, total_count, base_uri, name)%}
+{%set ns = namespace(forward_uri=base_uri, back_uri=base_uri)%}
+{%set ns.forward_uri="brr"%}
+ <div class="row">
+ <div class="col-md-2" style="text-align: start;">
+ {%if start_at > 0%}
+ <a href="{{base_uri +
+ '?start_at='+((start_at-page_count)|string) +
+ '&count='+(page_count|string)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
+ <div class="col-md-8" style="text-align: center;">
+ Displaying {{name}} {{start_at+1}} to {{start_at+page_count if start_at+page_count < total_count else total_count}} of {{total_count}}</div>
+ <div class="col-md-2" style="text-align: end;">
+ {%if start_at + page_count < total_count%}
+ <a href="{{base_uri +
+ '?start_at='+((start_at+page_count)|string) +
+ '&count='+(page_count|string)}}">
+ Next<span class="glyphicon glyphicon-forward"></span></a>
+ {%endif%}
+ </div>
+ </div>
+{%endmacro%}
diff --git a/uploader/templates/phenotypes/add-phenotypes-base.html b/uploader/templates/phenotypes/add-phenotypes-base.html
new file mode 100644
index 0000000..97b55f2
--- /dev/null
+++ b/uploader/templates/phenotypes/add-phenotypes-base.html
@@ -0,0 +1,331 @@
+{%extends "phenotypes/base.html"%}
+{%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%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="add-phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">Add Phenotypes</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <form id="frm-add-phenotypes"
+ method="POST"
+ enctype="multipart/form-data"
+ action="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.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">
+ {%block frm_add_phenotypes_documentation%}{%endblock%}
+ <p><strong class="text-warning">This will not update any existing phenotypes!</strong></p>
+ </div>
+
+ {%block frm_add_phenotypes_elements%}{%endblock%}
+
+ <div class="checkbox">
+ <label>
+ <input id="chk-published" type="checkbox" name="published?" />
+ These phenotypes are published</label>
+ </div>
+
+ <fieldset id="fldset-publication-info" class="hidden">
+ <legend>Publication Information</legend>
+ <div class="form-group">
+ <label for="txt-pubmed-id" class="form-label">Pubmed ID</label>
+ <div class="input-group">
+ <input id="txt-pubmed-id" name="pubmed-id" type="text"
+ class="form-control" />
+ <span class="input-group-btn">
+ <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.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-authors" class="form-label">Authors</label>
+ <input id="txt-publication-authors" name="publication-authors"
+ type="text" class="form-control" />
+ <span class="form-text text-muted">
+ Enter the authors in the following format &hellip;</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-title" class="form-label">
+ Publication Title</label>
+ <input id="txt-publication-title" name="publication-title" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter your publication's title.</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-abstract" class="form-label">
+ Publication Abstract</label>
+ <textarea id="txt-publication-abstract" name="publication-abstract"
+ class="form-control" rows="10"></textarea>
+ <span class="form-text text-muted">
+ Enter the abstract for your publication.</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-journal" class="form-label">Journal</label>
+ <input id="txt-publication-journal" name="journal" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the name of the journal where your work was published.</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-volume" class="form-label">Volume</label>
+ <input id="txt-publication-volume" name="publication-volume" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the volume in the following format &hellip;</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-pages" class="form-label">Pages</label>
+ <input id="txt-publication-pages" name="publication-pages" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the journal volume where your work was published.</span>
+ </div>
+
+ <div class="form-group">
+ <label for="select-publication-month" class="form-label">
+ Publication Month</label>
+ <select id="select-publication-month" name="publication-month"
+ class="form-control">
+ {%for month in monthnames%}
+ <option value="{{month | lower}}"
+ {%if current_month | lower == month | lower%}
+ selected="selected"
+ {%endif%}>{{month | capitalize}}</option>
+ {%endfor%}
+ </select>
+ <span class="form-text text-muted">
+ Select the month when the work was published.
+ <span class="text-danger">
+ This cannot be before, say 1600 and cannot be in the future!</span></span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-year" class="form-label">Publication Year</label>
+ <input id="txt-publication-year" name="publication-year" type="text"
+ class="form-control" value="{{current_year}}" />
+ <span class="form-text text-muted">
+ Enter the year your work was published.
+ <span class="text-danger">
+ This cannot be before, say 1600 and cannot be in the future!</span>
+ </span>
+ </div>
+ </fieldset>
+
+ <div class="form-group">
+ <input type="submit"
+ value="upload phenotypes"
+ class="btn btn-primary" />
+ </div>
+ </form>
+</div>
+
+<div class="row">
+ {%block page_documentation%}{%endblock%}
+</div>
+
+{%endblock%}
+
+
+{%block javascript%}
+<script type="text/javascript">
+ var remove_class = (element, classvalue) => {
+ new_classes = (element.attr("class") || "").split(" ").map((val) => {
+ return val.trim();
+ }).filter((val) => {
+ return ((val !== classvalue) &&
+ (val !== ""))
+ }).join(" ");
+
+ if(new_classes === "") {
+ element.removeAttr("class");
+ } else {
+ element.attr("class", new_classes);
+ }
+ };
+
+ var add_class = (element, classvalue) => {
+ remove_class(element, classvalue);
+ element.attr("class", (element.attr("class") || "") + " " + classvalue);
+ };
+
+ $("#chk-published").on("click", (event) => {
+ pub_details = $("#fldset-publication-info")
+ if(event.target.checked) {
+ // display the publication details
+ remove_class(pub_details, "hidden");
+ } else {
+ // hide the publication details
+ add_class(pub_details, "hidden");
+ }
+ });
+
+ var extract_details = (pubmed_id, details) => {
+ var months = {
+ "jan": "January",
+ "feb": "February",
+ "mar": "March",
+ "apr": "April",
+ "may": "May",
+ "jun": "June",
+ "jul": "July",
+ "aug": "August",
+ "sep": "September",
+ "oct": "October",
+ "nov": "November",
+ "dec": "December"
+ };
+ var _date = details[pubmed_id].pubdate.split(" ");
+ return {
+ "authors": details[pubmed_id].authors.map((authobj) => {
+ return authobj.name;
+ }),
+ "title": details[pubmed_id].title,
+ "journal": details[pubmed_id].fulljournalname,
+ "volume": details[pubmed_id].volume,
+ "pages": details[pubmed_id].pages,
+ "month": _date.length > 1 ? months[_date[1].toLowerCase()] : "jan",
+ "year": _date[0],
+ };
+ };
+
+ var update_publication_details = (details) => {
+ Object.entries(details).forEach((entry) => {;
+ switch(entry[0]) {
+ case "authors":
+ $("#txt-publication-authors").val(entry[1].join(", "));
+ break;
+ case "month":
+ $("#select-publication-month")
+ .children("option")
+ .each((index, child) => {
+ child.selected = child.value == entry[1].toLowerCase();
+ });
+ default:
+ $("#txt-publication-" + entry[0]).val(entry[1]);
+ break;
+ }
+ });
+ };
+
+ var fetch_publication_abstract = (pubmed_id, pub_details) => {
+ $.ajax("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi",
+ {
+ "method": "GET",
+ "data": {
+ "db": "pubmed",
+ "id": pubmed_id,
+ "rettype": "abstract",
+ "retmode": "xml"
+ },
+ "success": (data, textStatus, jqXHR) => {
+ update_publication_details({
+ ...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) => {},
+ "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>
+
+{%block more_javascript%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/add-phenotypes-raw-files.html b/uploader/templates/phenotypes/add-phenotypes-raw-files.html
new file mode 100644
index 0000000..7f8d8b0
--- /dev/null
+++ b/uploader/templates/phenotypes/add-phenotypes-raw-files.html
@@ -0,0 +1,732 @@
+{%extends "phenotypes/add-phenotypes-base.html"%}
+{%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%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="add-phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">Add Phenotypes</a>
+</li>
+{%endblock%}
+
+{%block frm_add_phenotypes_documentation%}
+<p>This page will allow you to upload all the separate files that make up your
+ phenotypes. Here, you will have to upload each separate file individually. If
+ you want instead to upload all your files as a single ZIP file,
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ use_bundle=true)}}"
+ title="">click here</a>.</p>
+{%endblock%}
+
+{%block frm_add_phenotypes_elements%}
+<fieldset id="fldset-file-metadata">
+ <legend>File(s) Metadata</legend>
+ <div class="form-group">
+ <label for="txt-file-separator" class="form-label">File Separator</label>
+ <div class="input-group">
+ <input id="txt-file-separator"
+ name="file-separator"
+ type="text"
+ value="&#9;"
+ class="form-control"
+ maxlength="1" />
+ <span class="input-group-btn">
+ <button id="btn-reset-file-separator" class="btn btn-info">Reset Default</button>
+ </span>
+ </div>
+ <span class="form-text text-muted">
+ Provide the character that separates the fields in your file(s). It should
+ be the same character for all files (if more than one is provided).<br />
+ A tab character will be assumed if you leave this field blank. See
+ <a href="#docs-file-separator"
+ title="Documentation for file-separator characters">
+ documentation for more information</a>.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-file-comment-character" class="form-label">File Comment-Character</label>
+ <div class="input-group">
+ <input id="txt-file-comment-character"
+ name="file-comment-character"
+ type="text"
+ value="#"
+ class="form-control"
+ maxlength="1" />
+ <span class="input-group-btn">
+ <button id="btn-reset-file-comment-character" class="btn btn-info">
+ Reset Default</button>
+ </span>
+ </div>
+ <span class="form-text text-muted">
+ This specifies that lines that begin with the character provided will be
+ considered comment lines and ignored in their entirety. See
+ <a href="#docs-file-comment-character"
+ title="Documentation for comment characters">
+ documentation for more information</a>.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-file-na" class="form-label">File "No-Value" Indicators</label>
+ <div class="input-group">
+ <input id="txt-file-na"
+ name="file-na"
+ type="text"
+ value="- NA N/A"
+ class="form-control" />
+ <span class="input-group-btn">
+ <button id="btn-reset-file-na" class="btn btn-info">Reset Default</button>
+ </span>
+ </div>
+ <span class="form-text text-muted">
+ This specifies strings in your file indicate that there is no value for a
+ particular cell (a cell is where a column and row intersect). Provide a
+ space-separated list of strings if you have more than one way of
+ indicating no values. See
+ <a href="#docs-file-na" title="Documentation for no-value fields">
+ documentation for more information</a>.</span>
+ </div>
+</fieldset>
+
+<fieldset id="fldset-data-files">
+ <legend>Data File(s)</legend>
+
+ <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,
+ <a href="#docs-file-phenotype-description"
+ title="Documentation of the phenotype data file format.">
+ 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 (below and to the right) 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 non-resumable-elements">
+ <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
+ <a href="#docs-file-phenotype-data"
+ title="Documentation of the phenotype data file format.">
+ the documentation for the expected format of the file</a>.</span>
+ </div>
+
+ {{display_resumable_elements(
+ "resumable-phenotype-data",
+ "phenotype data",
+ '<p>You can drop a CSV file that contains the phenotype data here,
+ or you can click the "Browse" button (below and to the right) 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-data"
+ title="Documentation of the phenotype data file format.">
+ "Phenotypes Data" documentation</a> section below.</p>')}}
+
+ {%if population.Family in families_with_se_and_n%}
+ <div class="form-group non-resumable-elements">
+ <label for="finput-phenotype-se" class="form-label">Phenotype: Standard Errors</label>
+ <input id="finput-phenotype-se"
+ 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,
+ computed from the data above.</span>
+ </div>
+ {{display_resumable_elements(
+ "resumable-phenotype-se",
+ "standard errors",
+ '<p>You can drop a CSV file that contains the computed standard-errors data
+ here, or you can click the "Browse" button (below and to the right) 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-se"
+ title="Documentation of the phenotype data file format.">
+ "Phenotypes Data" documentation</a> section below.</p>')}}
+
+
+ <div class="form-group non-resumable-elements">
+ <label for="finput-phenotype-n" class="form-label">Phenotype: Number of Samples/Individuals</label>
+ <input id="finput-phenotype-n"
+ 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
+ the computation of the standard errors above.</span>
+ </div>
+ {{display_resumable_elements(
+ "resumable-phenotype-n",
+ "number of samples/individuals",
+ '<p>You can drop a CSV file that contains the number of samples/individuals
+ used in computation of the standard-errors here, or you can click the
+ "Browse" button (below and to the right) 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-n"
+ title="Documentation of the phenotype data file format.">
+ "Phenotypes Data" documentation</a> section below.</p>')}}
+</fieldset>
+{%endif%}
+{%endblock%}
+
+
+{%block page_documentation%}
+<div class="row">
+ <h2 class="heading" id="docs-help">Help</h2>
+ <h3 class="subheading">Common Features</h3>
+ <p>The following are the common expectations for <strong>ALL</strong> the
+ files provided in the form above:
+ <ul>
+ <li>The file <strong>MUST</strong> be character-separated values (CSV)
+ text file</li>
+ <li>The first row in the file <strong>MUST</strong> be a heading row, and
+ will be composed of the list identifiers for all of
+ samples/individuals/cases involved in your study.</li>
+ <li>The first column of data in the file <strong>MUST</strong> be the
+ identifiers for all of the phenotypes you wish to upload.</li>
+ </ul>
+ </p>
+
+ <p>If you do not specify the separator character, then we will assume a
+ <strong>TAB</strong> character was used as your separator.</p>
+
+ <p>We also assume you might include comments lines in your files. In that
+ case, if you do not specify what character denotes that a line in your files
+ is a comment line, we will assume the <strong>#</strong> character.<br />
+ A comment <strong>MUST ALWAYS</strong> begin at the start of the line marked
+ with the comment character specified.</p>
+
+ <h3 class="subheading" id="docs-file-metadata">File Metadata</h3>
+ <p>We request some details about your files to help us parse and process the
+ files correctly. The details we collect are:</p>
+ <dl>
+ <dt id="docs-file-separator">File separator</dt>
+ <dd>The files you provide should be character-separated value (CSV) files.
+ We need to know what character you used to separate the values in your
+ file. Some common ones are the Tab character, the comma, etc.<br />
+ Providing that information makes it possible for the system to parse and
+ process your files correctly.<br>
+ <strong>NOTE:</strong> All the files you upload MUST use the same
+ separator.</dd>
+
+ <dt id="docs-file-comment-character">Comment character</dt>
+ <dd>We support use of comment lines in your files. We only support one type
+ of comment style, the <em>line comment</em>.<br />
+ This mean the comment begins at the start of the line, and the end of that
+ line indicates the end of that comment. If you have a really long comment,
+ then you need to break it across multiple lines, marking each line a
+ comment line.<br />
+ The "comment character" is the character at the start of the line that
+ indicates that the line is a line comment.</dd>
+
+ <dt id="docs-file-na">No-Value indicator(s)</dt>
+ <dd>Data in the real world is messy, and in some cases, entirely absent. You
+ need to indicate, in your files, that a particular field did not have a
+ value, and once you do that, you then need to let the system know how you
+ mark such fields. Common ways of indicating "empty values" are, leaving
+ the field blank, using a character such as '-', or using strings like
+ "NA", "N/A", "NULL", etc.<br />
+ Providing this information will help with parsing and processing such
+ no-value fields the correct way.</dd>
+ </dl>
+
+ <h3 class="subheading" id="docs-file-phenotype-description">
+ file: Phenotypes Descriptions</h3>
+ <p>The data in this file is a matrix of <em>phenotypes × metadata-fields</em>.
+ Please note we use the term "metadata-fields" above loosely, due to lack of
+ a good word for this.</p>
+ <p>The file <strong>MUST</strong> have columns in this order:
+ <dl>
+ <dt>Phenotype Identifiers</dt>
+ <dd>These are the names/identifiers for your phenotypes. These
+ names/identifiers are the same ones you will have in all the other files you are
+ uploading.</dd>
+
+ <dt>Descriptions</dt>
+ <dd>Each phenotype will need a description. Good description are necessary
+ to inform other people of what the data is about. Good description are
+ hard to construct, so we provide
+ <a href="https://info.genenetwork.org/faq.php#q-22"
+ title="How to write phenotype descriptions">
+ advice on describing your phenotypes.</a></dd>
+
+ <dt>Units</dt>
+ <dd>Each phenotype will need units for the measurements taken. If there are
+ none, then indicate the field is a no-value field.</dd>
+ </dl></p>
+ <p>You can add more columns after those three if you want to, but these 3
+ <strong>MUST</strong> be present.</p>
+ <p>The file would, for example, look like the following:</p>
+ <code>id,description,units,…<br />
+ pheno10001|Central nervous system, behavior, cognition; …|mg|…<br />
+ pheno10002|Aging, metabolism, central nervous system: …|mg|…<br />
+ â‹®<br /></code>
+
+ <p><strong>Note 01</strong>: The first usable row is the heading row.</p>
+ <p><strong>Note 02: </strong>This example demonstrates a subtle issue that
+ could make your CSV file invalid &mdash; the choice of your field separator
+ character.<br >
+ In the example above, we use the pipe character (<code>|</code>) as our
+ field separator. This is because, if we follow the advice on how to write
+ good descriptions, then we cannot use the comma as our separator &ndash; if
+ we did, then our CSV file would be invalid because the system would have no
+ way to tell the difference between the comma as a field separator, and the
+ comma as a way to separate the "general category and ontology terms".</p>
+
+ <h3 class="subheading">file: Phenotype Data, Standard Errors and/or Sample Counts</h3>
+ <span id="docs-file-phenotype-data"></span>
+ <span id="docs-file-phenotype-se"></span>
+ <span id="docs-file-phenotype-n"></span>
+ <p>The data is a matrix of <em>phenotypes × individuals</em>, e.g.</p>
+ <code>
+ # num-cases: 2549
+ # num-phenos: 13
+ id,IND001,IND002,IND003,IND004,…<br />
+ pheno10001,61.400002,54.099998,483,49.799999,…<br />
+ pheno10002,49,50.099998,403,45.5,…<br />
+ pheno10003,62.5,53.299999,501,62.900002,…<br />
+ pheno10004,53.099998,55.099998,403,NA,…<br />
+ â‹®<br /></code>
+
+ <p>where <code>IND001,IND002,IND003,IND004,…</code> are the
+ samples/individuals/cases in your study, and
+ <code>pheno10001,pheno10002,pheno10004,pheno10004,…</code> are the
+ identifiers for your phenotypes.</p>
+ <p>The lines beginning with the "<em>#</em>" symbol (i.e.
+ <code># num-cases: 2549</code> and <code># num-phenos: 13</code> are comment
+ lines and will be ignored</p>
+ <p>In this example, the comma (,) is used as the file separator.</p>
+</div>
+
+{%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 makePreviewUpdater = (preview_table) => {
+ return (data) => {
+ update_preview(
+ preview_table,
+ data,
+ filesMetadata(),
+ PREVIEW_ROWS);
+ };
+ };
+
+ 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 filesMetadata = () => {
+ 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 preview_table = $(mapentry[0]);
+ var file_input = $(mapentry[1]);
+ if(file_input.length === 1) {
+ readFirstNLines(
+ file_input[0].files[0],
+ 10,
+ [makePreviewUpdater(preview_table)]);
+ }
+ });
+ };
+
+ [
+ "#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) => {
+ readFirstNLines(
+ event.target.files[0],
+ 10,
+ [makePreviewUpdater(
+ $("#" + event.target.getAttribute("data-preview-table")))]);
+ });
+ });
+
+
+ var resumableDisplayFiles = (display_area, files) => {
+ files.forEach((file) => {
+ display_area.find(".file-display").remove();
+ 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 () => {/*Has no 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 processForm = (form) => {
+ var formdata = new FormData(form);
+ uploaded_files.forEach((msg) => {
+ formdata.delete(msg["file-input-name"]);
+ formdata.append(msg["file-input-name"], JSON.stringify({
+ "uploaded-file": msg["uploaded-file"],
+ "original-name": msg["original-name"]
+ }));
+ });
+ formdata.append("resumable-upload", "true");
+ return formdata;
+ }
+
+ var uploaded_files = new Set();
+ var submitForm = (new_file) => {
+ uploaded_files.add(new_file);
+ if(uploaded_files.size === resumables.length) {
+ var form = $("#frm-add-phenotypes");
+ if(form.length !== 1) {
+ // TODO: Handle error somehow?
+ alert("Could not find form!!!");
+ return false;
+ }
+
+ $.ajax({
+ "url": form.attr("action"),
+ "type": "POST",
+ "data": processForm(form[0]),
+ "processData": false,
+ "contentType": false,
+ "success": (data, textstatus, jqxhr) => {
+ // TODO: Redirect to endpoint that should come as part of the
+ // success/error message.
+ console.log("SUCCESS DATA: ", data);
+ console.log("SUCCESS STATUS: ", textstatus);
+ console.log("SUCCESS jqXHR: ", jqxhr);
+ window.location.assign(window.location.origin + data["redirect-to"]);
+ },
+ });
+ return false;
+ }
+ return false;
+ };
+
+ var uploadSuccess = (file_input_name) => {
+ return (file, message) => {
+ submitForm({...JSON.parse(message), "file-input-name": file_input_name});
+ };
+ };
+
+
+ var uploadError = () => {
+ return (message, file) => {
+ $("#frm-add-phenotypes input[type=submit]").removeAttr("disabled");
+ console.log("THE FILE:", file);
+ console.log("THE ERROR MESSAGE:", message);
+ };
+ };
+
+
+
+ var makeResumableObject = (form_id, file_input_id, resumable_element_id, preview_table_id) => {
+ var the_form = $("#" + form_id);
+ var file_input = $("#" + file_input_id);
+ var submit_button = the_form.find("input[type=submit]");
+ if(file_input.length != 1) {
+ return false;
+ }
+ var r = errorHandler(
+ fileSuccessHandler(
+ uploadStartHandler(
+ filesAddedHandler(
+ markResumableDragAndDropElement(
+ makeResumableElement(
+ the_form.attr("data-resumable-target"),
+ file_input.parent(),
+ $("#" + resumable_element_id),
+ submit_button,
+ ["csv", "tsv"]),
+ file_input.parent(),
+ $("#" + resumable_element_id),
+ $("#" + resumable_element_id + "-browse-button")),
+ (files) => {
+ // TODO: Also trigger preview!
+ resumableDisplayFiles(
+ $("#" + resumable_element_id + "-selected-files"), files);
+ files.forEach((file) => {
+ readFirstNLines(
+ file.file,
+ 10,
+ [makePreviewUpdater(
+ $("#" + preview_table_id))])
+ });
+ }),
+ startUpload($("#" + resumable_element_id + "-browse-button"),
+ $("#" + resumable_element_id + "-retry-button"),
+ $("#" + resumable_element_id + "-cancel-button"))),
+ uploadSuccess(file_input.attr("name"))),
+ uploadError());
+
+ /** Setup progress indicator **/
+ progressHandler(
+ r,
+ indicateProgress(r, $("#" + resumable_element_id + "-progress-bar")));
+
+ return r;
+ };
+
+ var resumables = [
+ ["frm-add-phenotypes", "finput-phenotype-descriptions", "resumable-phenotype-descriptions", "tbl-preview-pheno-desc"],
+ ["frm-add-phenotypes", "finput-phenotype-data", "resumable-phenotype-data", "tbl-preview-pheno-data"],
+ ["frm-add-phenotypes", "finput-phenotype-se", "resumable-phenotype-se", "tbl-preview-pheno-se"],
+ ["frm-add-phenotypes", "finput-phenotype-n", "resumable-phenotype-n", "tbl-preview-pheno-n"],
+ ].map((row) => {
+ return makeResumableObject(row[0], row[1], row[2], row[3]);
+ }).filter((val) => {
+ return Boolean(val);
+ });
+
+ $("#frm-add-phenotypes input[type=submit]").on("click", (event) => {
+ event.preventDefault();
+ // TODO: Check all the relevant files exist
+ // TODO: Verify that files are not duplicated
+ var filenames = [];
+ var nondupfiles = [];
+ resumables.forEach((r) => {
+ var fname = r.files[0].file.name;
+ filenames.push(fname);
+ if(!nondupfiles.includes(fname)) {
+ nondupfiles.push(fname);
+ }
+ });
+
+ // Check that all files were provided
+ if(resumables.length !== filenames.length) {
+ window.alert("You MUST provide all the files requested.");
+ event.target.removeAttribute("disabled");
+ return false;
+ }
+
+ // Check that there are no duplicate files
+ var duplicates = Object.entries(filenames.reduce(
+ (acc, curr, idx, arr) => {
+ acc[curr] = (acc[curr] || 0) + 1;
+ return acc;
+ },
+ {})).filter((entry) => {return entry[1] !== 1;});
+ if(duplicates.length > 0) {
+ var msg = "The file(s):\r\n";
+ msg = msg + duplicates.reduce(
+ (msgstr, afile) => {
+ return msgstr + " • " + afile[0] + "\r\n";
+ },
+ "");
+ msg = msg + "is(are) duplicated. Please fix and try again.";
+ window.alert(msg);
+ event.target.removeAttribute("disabled");
+ return false;
+ }
+ // TODO: Check all fields
+ // Start the uploads.
+ event.target.setAttribute("disabled", "disabled");
+ resumables.forEach((r) => {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
new file mode 100644
index 0000000..898fc0c
--- /dev/null
+++ b/uploader/templates/phenotypes/add-phenotypes-with-rqtl2-bundle.html
@@ -0,0 +1,207 @@
+{%extends "phenotypes/add-phenotypes-base.html"%}
+{%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%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="add-phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">Add Phenotypes</a>
+</li>
+{%endblock%}
+
+{%block frm_add_phenotypes_documentation%}
+<p>Select the zip file bundle containing information on the phenotypes you
+ wish to upload, then click the "Upload Phenotypes" button below to
+ upload the data.</p>
+<p>If you wish to upload the files individually instead,
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}"
+ title="">click here</a>.</p>
+<p>See the <a href="#section-file-formats">File Formats</a> section below
+ to get an understanding of what is expected of the bundle files you
+ upload.</p>
+{%endblock%}
+
+{%block frm_add_phenotypes_elements%}
+<div class="form-group">
+ <label for="finput-phenotypes-bundle" class="form-label">
+ Phenotypes Bundle</label>
+ <input type="file"
+ id="finput-phenotypes-bundle"
+ name="phenotypes-bundle"
+ accept="application/zip, .zip"
+ required="required"
+ class="form-control" />
+</div>
+{%endblock%}
+
+{%block page_documentation%}
+<div class="row">
+ <h2 class="heading" id="section-file-formats">File Formats</h2>
+ <p>We accept an extended form of the
+ <a href="https://kbroman.org/qtl2/assets/vignettes/input_files.html#format-of-the-data-files"
+ title="R/qtl2 software input file format documentation">
+ input files' format used with the R/qtl2 software</a> as a single ZIP
+ file</p>
+ <p>The files that are used for this feature are:
+ <ul>
+ <li>the <em>control</em> file</li>
+ <li><em>pheno</em> file(s)</li>
+ <li><em>phenocovar</em> file(s)</li>
+ <li><em>phenose</em> files(s)</li>
+ </ul>
+ </p>
+ <p>Other files within the bundle will be ignored, for this feature.</p>
+ <p>The following section will detail the expectations for each of the
+ different file types within the uploaded ZIP file bundle for phenotypes:</p>
+
+ <h3 class="subheading">Control File</h3>
+ <p>There <strong>MUST be <em>one, and only one</em></strong> file that acts
+ as the control file. This file can be:
+ <ul>
+ <li>a <em>JSON</em> file, or</li>
+ <li>a <em>YAML</em> file.</li>
+ </ul>
+ </p>
+
+ <p>The control file is useful for defining things about the bundle such as:</p>
+ <ul>
+ <li>The field separator value (default: <code>sep: ','</code>). There can
+ only ever be one field separator and it <strong>MUST</strong> be the same
+ one for <strong>ALL</strong> files in the bundle.</li>
+ <li>The comment character (default: <code>comment.char: '#'</code>). Any
+ line that starts with this character will be considered a comment line and
+ be ignored in its entirety.</li>
+ <li>Code for missing values (default: <code>na.strings: 'NA'</code>). You
+ can specify more than one code to indicate missing values, e.g.
+ <code>{…, "na.strings": ["NA", "N/A", "-"], …}</code></li>
+ </ul>
+
+ <h3 class="subheading"><em>pheno</em> File(s)</h3>
+ <p>These files are the main data files. You must have at least one of these
+ files in your bundle for it to be valid for this step.</p>
+ <p>The data is a matrix of <em>individuals × phenotypes</em> by default, as
+ below:<br />
+ <code>
+ id,10001,10002,10003,10004,…<br />
+ BXD1,61.400002,54.099998,483,49.799999,…<br />
+ BXD2,49,50.099998,403,45.5,…<br />
+ BXD5,62.5,53.299999,501,62.900002,…<br />
+ BXD6,53.099998,55.099998,403,NA,…<br />
+ â‹®<br /></code>
+ </p>
+ <p>If the <code>pheno_transposed</code> value is set to <code>True</code>,
+ then the data will be a <em>phenotypes × individuals</em> matrix as in the
+ example below:<br />
+ <code>
+ id,BXD1,BXD2,BXD5,BXD6,…<br />
+ 10001,61.400002,49,62.5,53.099998,…<br />
+ 10002,54.099998,50.099998,53.299999,55.099998,…<br />
+ 10003,483,403,501,403,…<br />
+ 10004,49.799999,45.5,62.900002,NA,…<br />
+ â‹®
+ </code>
+ </p>
+
+
+ <h3 class="subheading"><em>phenocovar</em> File(s)</h3>
+ <p>At least one phenotypes metadata file with the metadata values such as
+ descriptions, PubMed Identifier, publication titles (if present), etc.</p>
+ <p>The data in this/these file(s) is a matrix of
+ <em>phenotypes × phenotypes-covariates</em>. The first column is always the
+ phenotype names/identifiers — same as in the R/qtl2 format.</p>
+ <p><em>phenocovar</em> files <strong>should never be transposed</strong>!</p>
+ <p>This file <strong>MUST</strong> be present in the bundle, and have data for
+ the bundle to be considered valid by our system for this step.<br />
+ In addition to that, the following are the fields that <strong>must be
+ present</strong>, and
+ have values, in the file before the file is considered valid:
+ <ul>
+ <li><em>description</em>: A description for each phenotype. Useful
+ for users to know what the phenotype is about.</li>
+ <li><em>units</em>: The units of measurement for the phenotype,
+ e.g. milligrams for brain weight, centimetres/millimetres for
+ tail-length, etc.</li>
+ </ul></p>
+
+ <p>The following <em>optional</em> fields can also be provided:
+ <ul>
+ <li><em>pubmedid</em>: A PubMed Identifier for the publication where
+ the phenotype is published. If this field is not provided, the system will
+ assume your phenotype is not published.</li>
+ </ul>
+ </p>
+ <p>These files will be marked up in the control file with the
+ <code>phenocovar</code> key, as in the examples below:
+ <ol>
+ <li>JSON: single file<br />
+ <code>{<br />
+ &nbsp;&nbsp;â‹®,<br />
+ &nbsp;&nbsp;"phenocovar": "your_covariates_file.csv",<br />
+ &nbsp;&nbsp;â‹®<br />
+ }
+ </code>
+ </li>
+ <li>JSON: multiple files<br />
+ <code>{<br />
+ &nbsp;&nbsp;â‹®,<br />
+ &nbsp;&nbsp;"phenocovar": [<br />
+ &nbsp;&nbsp;&nbsp;&nbsp;"covariates_file_01.csv",<br />
+ &nbsp;&nbsp;&nbsp;&nbsp;"covariates_file_01.csv",<br />
+ &nbsp;&nbsp;&nbsp;&nbsp;â‹®<br />
+ &nbsp;&nbsp;],<br />
+ &nbsp;&nbsp;â‹®<br />
+ }
+ </code>
+ </li>
+ <li>YAML: single file or<br />
+ <code>
+ â‹®<br />
+ phenocovar: your_covariates_file.csv<br />
+ â‹®
+ </code>
+ </li>
+ <li>YAML: multiple files<br />
+ <code>
+ â‹®<br />
+ phenocovar:<br />
+ - covariates_file_01.csv<br />
+ - covariates_file_02.csv<br />
+ - covariates_file_03.csv<br />
+ …<br />
+ â‹®
+ </code>
+ </li>
+ </ol>
+ </p>
+
+ <h3 class="subheading"><em>phenose</em> and <em>phenonum</em> File(s)</h3>
+ <p>These are extensions to the R/qtl2 standard, i.e. these types ofs file are
+ not supported by the original R/qtl2 file format</p>
+ <p>We use these files to upload the standard errors (<em>phenose</em>) when
+ the data file (<em>pheno</em>) is average data. In that case, the
+ <em>phenonum</em> file(s) contains the number of individuals that were
+ involved when computing the averages.</p>
+ <p>Both types of files are matrices of <em>individuals × phenotypes</em> by
+ default. Like the related <em>pheno</em> files, if
+ <code>pheno_transposed: True</code>, then the file will be a matrix of
+ <em>phenotypes × individuals</em>.</p>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_pheno_dataset_card(species, population, dataset)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/base.html b/uploader/templates/phenotypes/base.html
new file mode 100644
index 0000000..adbc012
--- /dev/null
+++ b/uploader/templates/phenotypes/base.html
@@ -0,0 +1,19 @@
+{%extends "populations/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ {%if dataset is mapping%}
+ <a href="{{url_for('species.populations.phenotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">{{dataset.Name}}</a>
+ {%else%}
+ <a href="{{url_for('species.populations.phenotypes.index')}}">Phenotypes</a>
+ {%endif%}
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/create-dataset.html b/uploader/templates/phenotypes/create-dataset.html
new file mode 100644
index 0000000..8e45491
--- /dev/null
+++ b/uploader/templates/phenotypes/create-dataset.html
@@ -0,0 +1,108 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "macro-table-pagination.html" import table_pagination%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="create-dataset"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">Create Datasets</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>Create a new phenotype dataset.</p>
+</div>
+
+<div class="row">
+ <form id="frm-create-pheno-dataset"
+ action="{{url_for('species.populations.phenotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ method="POST">
+
+ <div class="form-group">
+ <label class="form-label" for="txt-dataset-name">Name</label>
+ {%if errors["dataset-name"] is defined%}
+ <small class="form-text text-muted danger">
+ <p>{{errors["dataset-name"]}}</p></small>
+ {%endif%}
+ <input type="text"
+ name="dataset-name"
+ id="txt-dataset-name"
+ value="{{original_formdata.get('dataset-name') or (population.InbredSetCode + 'Publish')}}"
+ {%if errors["dataset-name"] is defined%}
+ class="form-control danger"
+ {%else%}
+ class="form-control"
+ {%endif%}
+ required="required" />
+ <small class="form-text text-muted">
+ <p>A short representative name for the dataset.</p>
+ <p>Recommended: Use the population code and append "Publish" at the end.
+ <br />This field will only accept names composed of
+ letters ('A-Za-z'), numbers (0-9), hyphens and underscores.</p>
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="txt-dataset-fullname">Full Name</label>
+ {%if errors["dataset-fullname"] is defined%}
+ <small class="form-text text-muted danger">
+ <p>{{errors["dataset-fullname"]}}</p></small>
+ {%endif%}
+ <input id="txt-dataset-fullname"
+ name="dataset-fullname"
+ type="text"
+ value="{{original_formdata.get('dataset-fullname', '')}}"
+ {%if errors["dataset-fullname"] is defined%}
+ class="form-control danger"
+ {%else%}
+ class="form-control"
+ {%endif%}
+ required="required" />
+ <small class="form-text text-muted">
+ <p>A longer, descriptive name for the dataset. The name is meant for use
+ by humans, and therefore, it should be clear what the dataset contains
+ from the name.</p>
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="txt-dataset-shortname">Short Name</label>
+ <input id="txt-dataset-shortname"
+ name="dataset-shortname"
+ type="text"
+ class="form-control"
+ value="{{original_formdata.get('dataset-shortname') or (population.InbredSetCode + ' Publish')}}" />
+ <small class="form-text text-muted">
+ <p>An optional, short name for the dataset. <br />
+ If this is not provided, it will default to the value provided for the
+ <strong>Name</strong> field above.</p></small>
+ </div>
+
+ <div class="form-group">
+ <input type="submit"
+ class="btn btn-primary"
+ value="create phenotype dataset" />
+ </div>
+
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/edit-phenotype.html b/uploader/templates/phenotypes/edit-phenotype.html
new file mode 100644
index 0000000..32c903f
--- /dev/null
+++ b/uploader/templates/phenotypes/edit-phenotype.html
@@ -0,0 +1,332 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="edit-phenotype"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.edit_phenotype_data',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ xref_id=xref_id)}}">View Datasets</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <h2 class="heading">edit phenotype data</h2>
+ <p>The forms provided in this page help you update the data for the
+ phenotypes, and the publication information for the phenotype,
+ respectively.</p>
+</div>
+
+<div class="row">
+ <h3 class="subheading">Basic metadata</h3>
+ <form name="frm-phenotype-basic-metadata"
+ class="form-horizontal"
+ method="POST"
+ action="{{url_for(
+ 'species.populations.phenotypes.edit_phenotype_data',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ xref_id=xref_id)}}">
+ <input type="hidden" name="phenotype-id" value="{{phenotype.Id}}" />
+ <div class="form-group">
+ <label for="txt-pre-publication-description"
+ class="control-label col-sm-2">Pre-Publication Description</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-pre-publication-description"
+ name="pre-publication-description"
+ class="form-control"
+ value="{{phenotype['Pre_publication_description'] or ''}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-pre-publication-abbreviation"
+ class="control-label col-sm-2">Pre-Publication Abbreviation</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-pre-publication-abbreviation"
+ name="pre-publication-abbreviation"
+ class="form-control"
+ value="{{phenotype['Pre_publication_abbreviation'] or ''}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-post-publication-description"
+ class="control-label col-sm-2">Post-Publication Description</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-post-publication-description"
+ name="post-publication-description"
+ class="form-control"
+ value="{{phenotype['Post_publication_description'] or ''}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-post-publication-abbreviation"
+ class="control-label col-sm-2">Post-Publication Abbreviation</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-post-publication-abbreviation"
+ name="post-publication-abbreviation"
+ class="form-control"
+ value="{{phenotype['Post_publication_abbreviation'] or ''}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-original-description"
+ class="control-label col-sm-2">Original Description</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-original-description"
+ name="original-description"
+ class="form-control"
+ value="{{phenotype['Original_description'] or ''}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-units"
+ class="control-label col-sm-2">units</label>
+ <div class="col-sm-10">
+ <input type="text"
+ id="txt-units"
+ name="units"
+ class="form-control"
+ required="required"
+ value="{{phenotype['Units']}}" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <input type="submit"
+ name="submit"
+ class="btn btn-primary"
+ value="update basic metadata">
+ </div>
+ </div>
+ </form>
+</div>
+
+
+<div class="row">
+ <h3 class="subheading">phenotype data</h3>
+ <form id="frm-edit-phenotype-data"
+ class="form-horizontal"
+ method="POST"
+ action="{{url_for(
+ 'species.populations.phenotypes.edit_phenotype_data',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ xref_id=xref_id)}}">
+ <div style="max-height: 23.37em;overflow-y: scroll;">
+ <table class="table table-striped table-responsive table-form-table">
+ <thead style="position: sticky; top: 0;">
+ <tr>
+ <th>#</th>
+ <th>Sample</th>
+ <th>Value</th>
+ {%if population.Family in families_with_se_and_n%}
+ <th>Standard-Error</th>
+ <th>Number of Samples</th>
+ {%endif%}
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for item in phenotype.data%}
+ <tr>
+ <td>{{loop.index}}</td>
+ <td>{{item.StrainName}}</td>
+ <td>
+ <input type="text"
+ name="value-new::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.value}}"
+ class="form-control" />
+ <input type="hidden"
+ name="value-original::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.value}}" /></td>
+ {%if population.Family in families_with_se_and_n%}
+ <td>
+ <input type="text"
+ name="se-new::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.error or ''}}"
+ data-original-value="{{item.error or ''}}"
+ class="form-control" />
+ <input type="hidden"
+ name="se-original::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.error or ''}}" /></td>
+ <td>
+ <input type="text"
+ name="n-new::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.count or ''}}"
+ data-original-value="{{item.count or "-"}}"
+ class="form-control" />
+ <input type="hidden"
+ name="n-original::{{item.DataId}}::{{item.StrainId}}"
+ value="{{item.count or ''}}" /></td>
+ {%endif%}
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ </div>
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <input type="submit"
+ name="submit"
+ class="btn btn-primary"
+ value="update data" />
+ </div>
+ </div>
+ </form>
+</div>
+
+
+<div class="row">
+ <h3 class="subheading">publication information</h3>
+ <p>Use the form below to update the publication information for this
+ phenotype.</p>
+ <form id="frm-edit-phenotype-pub-data"
+ class="form-horizontal"
+ method="POST"
+ action="#">
+ <div class="form-group">
+ <label for="txt-pubmed-id" class="control-label col-sm-2">Pubmed ID</label>
+ <div class="col-sm-10">
+ <input id="txt-pubmed-id" name="pubmed-id" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter your publication's PubMed ID.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-authors" class="control-label col-sm-2">Authors</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-authors" name="publication-authors"
+ type="text" class="form-control" />
+ <span class="form-text text-muted">
+ Enter the authors.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-title" class="control-label col-sm-2">
+ Publication Title</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-title" name="publication-title" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter your publication's title.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-abstract" class="control-label col-sm-2">
+ Publication Abstract</label>
+ <div class="col-sm-10">
+ <textarea id="txt-publication-abstract" name="publication-abstract"
+ class="form-control" rows="10"></textarea>
+ <span class="form-text text-muted">
+ Enter the abstract for your publication.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-journal" class="control-label col-sm-2">Journal</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-journal" name="journal" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the name of the journal where your work was published.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-volume" class="control-label col-sm-2">Volume</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-volume" name="publication-volume" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the volume in the following format &hellip;</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-pages" class="control-label col-sm-2">Pages</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-pages" name="publication-pages" type="text"
+ class="form-control" />
+ <span class="form-text text-muted">
+ Enter the journal volume where your work was published.</span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="select-publication-month" class="control-label col-sm-2">
+ Publication Month</label>
+ <div class="col-sm-10">
+ <select id="select-publication-month" name="publication-month"
+ class="form-control">
+ {%for month in monthnames%}
+ <option value="{{month | lower}}"
+ {%if current_month | lower == month | lower%}
+ selected="selected"
+ {%endif%}>{{month | capitalize}}</option>
+ {%endfor%}
+ </select>
+ <span class="form-text text-muted">
+ Select the month when the work was published.
+ <span class="text-danger">
+ This cannot be before, say 1600 and cannot be in the future!</span></span>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-publication-year" class="control-label col-sm-2">Publication Year</label>
+ <div class="col-sm-10">
+ <input id="txt-publication-year" name="publication-year" type="text"
+ class="form-control" value="{{current_year}}" />
+ <span class="form-text text-muted">
+ Enter the year your work was published.
+ <span class="text-danger">
+ This cannot be before, say 1600 and cannot be in the future!</span>
+ </span>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <input type="submit"
+ name="submit"
+ class="btn btn-primary not-implemented"
+ value="update publication" />
+ </div>
+ </div>
+ </form>
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/index.html b/uploader/templates/phenotypes/index.html
new file mode 100644
index 0000000..689c28e
--- /dev/null
+++ b/uploader/templates/phenotypes/index.html
@@ -0,0 +1,21 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ {{select_species_form(url_for("species.populations.phenotypes.index"), species)}}
+</div>
+{%endblock%}
+
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/species.js"></script>
+{%endblock%}
diff --git a/uploader/templates/phenotypes/job-status.html b/uploader/templates/phenotypes/job-status.html
new file mode 100644
index 0000000..12963c1
--- /dev/null
+++ b/uploader/templates/phenotypes/job-status.html
@@ -0,0 +1,155 @@
+{%extends "phenotypes/base.html"%}
+{%from "cli-output.html" import cli_output%}
+{%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%}
+
+{%block extrameta%}
+{%if job and job.status not in ("success", "completed:success", "error", "completed:error")%}
+<meta http-equiv="refresh" content="5" />
+{%endif%}
+{%endblock%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="add-phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">View Datasets</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+
+{%if job%}
+<h4 class="subheading">Progress</h4>
+<div class="row" style="overflow:scroll;">
+ <p><strong>Process Status:</strong> {{job.status}}</p>
+ {%if metadata%}
+ <table class="table table-responsive">
+ <thead>
+ <tr>
+ <th>File</th>
+ <th>Status</th>
+ <th>Lines Processed</th>
+ <th>Total Errors</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for file,meta in metadata.items()%}
+ <tr>
+ <td>{{file}}</td>
+ <td>{{meta.status}}</td>
+ <td>{{meta.linecount}}</td>
+ <td>{{meta["total-errors"]}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ {%endif%}
+</div>
+
+<div class="row">
+ {%if job.status in ("completed:success", "success")%}
+ <p>
+ {%if errors | length == 0%}
+ <a href="{{url_for('species.populations.phenotypes.review_job_data',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ job_id=job_id)}}"
+ class="btn btn-primary"
+ title="Continue to process data">Continue</a>
+ {%else%}
+ <span class="text-muted"
+ disabled="disabled"
+ style="border: solid 2px;border-radius: 5px;padding: 0.3em;">
+ Cannot continue due to errors. Please fix the errors first.
+ </span>
+ {%endif%}
+ </p>
+ {%endif%}
+</div>
+
+<h4 class="subheading">Errors</h4>
+<div class="row" style="max-height: 20em; overflow: scroll;">
+ {%if errors | length == 0 %}
+ <p class="text-info">
+ <span class="glyphicon glyphicon-info-sign"></span>
+ No errors found so far
+ </p>
+ {%else%}
+ <table class="table table-responsive">
+ <thead style="position: sticky; top: 0; background: white;">
+ <tr>
+ <th>File</th>
+ <th>Row</th>
+ <th>Column</th>
+ <th>Value</th>
+ <th>Message</th>
+ </tr>
+ </thead>
+
+ <tbody style="font-size: 0.9em;">
+ {%for error in errors%}
+ <tr>
+ <td>{{error.filename}}</td>
+ <td>{{error.rowtitle}}</td>
+ <td>{{error.coltitle}}</td>
+ <td>{%if error.cellvalue | length > 25%}
+ {{error.cellvalue[0:24]}}&hellip;
+ {%else%}
+ {{error.cellvalue}}
+ {%endif%}
+ </td>
+ <td>
+ {%if error.message | length > 250 %}
+ {{error.message[0:249]}}&hellip;
+ {%else%}
+ {{error.message}}
+ {%endif%}
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ {%endif%}
+</div>
+
+<div class="row">
+ {{cli_output(job, "stdout")}}
+</div>
+
+<div class="row">
+ {{cli_output(job, "stderr")}}
+</div>
+
+{%else%}
+<div class="row">
+ <h3 class="text-danger">No Such Job</h3>
+ <p>Could not find a job with the ID: {{job_id}}</p>
+ <p>
+ Please go back to
+ <a href="{{url_for('species.populations.phenotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}"
+ title="'{{dataset.Name}}' dataset page">
+ the '{{dataset.Name}}' dataset page</a>
+ to upload new phenotypes or edit existing ones.</p>
+</div>
+{%endif%}
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_pheno_dataset_card(species, population, dataset)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/list-datasets.html b/uploader/templates/phenotypes/list-datasets.html
new file mode 100644
index 0000000..2cf2c7f
--- /dev/null
+++ b/uploader/templates/phenotypes/list-datasets.html
@@ -0,0 +1,68 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-datasets"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.list_datasets',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List Datasets</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ {%if datasets | length > 0%}
+ <p>The dataset(s) available for this population is/are:</p>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Full Name</th>
+ <th>Short Name</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for dataset in datasets%}
+ <tr>
+ <td><a href="{{url_for('species.populations.phenotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">{{dataset.Name}}</a></td>
+ <td>{{dataset.FullName}}</td>
+ <td>{{dataset.ShortName}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ {%else%}
+ <p>Phenotypes need to go into a dataset. We do not currently have a dataset
+ for species <strong>'{{species["FullName"]}} ({{species["Name"]}})'</strong>
+ phenotypes.</p>
+
+ <p>Do, please, create a new dataset by clicking on the "Create Dataset" button
+ below and following the prompts/instructions.</p>
+ <p><a href="{{url_for('species.populations.phenotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ class="btn btn-primary"
+ title="Create a new phenotype dataset.">create dataset</a></p>
+ {%endif%}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html b/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html
new file mode 100644
index 0000000..11b108b
--- /dev/null
+++ b/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html
@@ -0,0 +1,31 @@
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%macro display_pheno_dataset_card(species, population, dataset)%}
+{{display_population_card(species, population)}}
+
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Phenotypes' Dataset</h5>
+ <div class="card-text">
+ <table class="table">
+ <tbody>
+ <tr>
+ <td>Name</td>
+ <td>{{dataset.Name}}</td>
+ </tr>
+
+ <tr>
+ <td>Full Name</td>
+ <td>{{dataset.FullName}}</td>
+ </tr>
+
+ <tr>
+ <td>Short Name</td>
+ <td>{{dataset.ShortName}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+{%endmacro%}
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..f54c53e
--- /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" style="overflow: scroll;">
+ <table id="{{tableid}}" class="table table-condensed table-responsive">
+ <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%}
diff --git a/uploader/templates/phenotypes/macro-display-resumable-elements.html b/uploader/templates/phenotypes/macro-display-resumable-elements.html
new file mode 100644
index 0000000..b0bf1b5
--- /dev/null
+++ b/uploader/templates/phenotypes/macro-display-resumable-elements.html
@@ -0,0 +1,60 @@
+{%macro display_resumable_elements(id, title, help)%}
+<div id="{{id}}"
+ class="resumable-elements hidden"
+ style="background:#D4D4EE;border-radius: 5px;;padding: 1em;border-left: solid #B2B2CC 1px;border-bottom: solid #B2B2CC 2px;margin-top:0.3em;">
+ <strong style="line-height: 1.2em;">{{title | title}}</strong>
+
+ <span class="form-text text-muted">{{help | safe}}</span>
+
+ <div id="{{id}}-selected-files"
+ class="resumable-selected-files"
+ style="display:flex;flex-direction:row;flex-wrap: wrap;justify-content:space-around;gap:10px 20px;">
+ <div class="panel panel-info file-display-template hidden">
+ <div class="panel-heading filename">The Filename Goes Here!</div>
+ <div class="panel-body">
+ <ul>
+ <li>
+ <strong>Name</strong>:
+ <span class="filename">the file's name</span></li>
+
+ <li><strong>Size</strong>: <span class="filesize">0 MB</span></li>
+
+ <li>
+ <strong>Unique Identifier</strong>:
+ <span class="fileuniqueid">brrr</span></li>
+
+ <li>
+ <strong>Mime</strong>:
+ <span class="filemimetype">text/csv</span></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+
+ <a id="{{id}}-browse-button"
+ class="resumable-browse-button btn btn-info"
+ href="#"
+ style="margin-left: 80%;">Browse</a>
+
+ <div id="{{id}}-progress-bar" class="progress hidden">
+ <div class="progress-bar"
+ role="progress-bar"
+ aria-valuenow="60"
+ aria-valuemin="0"
+ aria-valuemax="100"
+ style="width: 0%;">
+ Uploading: 60%
+ </div>
+ </div>
+
+ <div id="{{id}}-cancel-resume-buttons">
+ <a id="{{id}}-resume-button"
+ class="resumable-resume-button btn btn-info hidden"
+ href="#">resume upload</a>
+
+ <a id="{{id}}-cancel-button"
+ class="resumable-cancel-button btn btn-danger hidden"
+ href="#">cancel upload</a>
+ </div>
+</div>
+{%endmacro%}
diff --git a/uploader/templates/phenotypes/review-job-data.html b/uploader/templates/phenotypes/review-job-data.html
new file mode 100644
index 0000000..7bc8c62
--- /dev/null
+++ b/uploader/templates/phenotypes/review-job-data.html
@@ -0,0 +1,101 @@
+{%extends "phenotypes/base.html"%}
+{%from "cli-output.html" import cli_output%}
+{%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%}
+
+{%block extrameta%}
+{%if not job%}
+<meta http-equiv="refresh"
+ content="20; url={{url_for('species.populations.phenotypes.view_dataset', species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}" />
+{%endif%}
+{%endblock%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="add-phenotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">View Datasets</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+
+{%if job%}
+<div class="row">
+ <h3 class="heading">Data Review</h3>
+ <p>The &#x201C;<strong>{{dataset.FullName}}</strong>&#x201D; dataset from the
+ &#x201C;<strong>{{population.FullName}}</strong>&#x201D; population of the
+ species &#x201C;<strong>{{species.SpeciesName}} ({{species.FullName}})</strong>&#x201D;
+ will be updated as follows:</p>
+
+ {%for ftype in ("phenocovar", "pheno", "phenose", "phenonum")%}
+ {%if summary.get(ftype, False)%}
+ <ul>
+ <li>A total of {{summary[ftype]["number-of-files"]}} files will be processed
+ adding {%if ftype == "phenocovar"%}(possibly){%endif%}
+ {{summary[ftype]["total-data-rows"]}} new
+ {%if ftype == "phenocovar"%}
+ phenotypes
+ {%else%}
+ {{summary[ftype]["description"]}} rows
+ {%endif%}
+ to the database.
+ </li>
+ </ul>
+ {%endif%}
+ {%endfor%}
+
+ <a href="#" class="not-implemented btn btn-primary">continue</a>
+</div>
+{%else%}
+<div class="row">
+ <h4 class="subheading">Invalid Job</h3>
+ <p class="text-danger">
+ Could not find a job with the ID: <strong>{{job_id}}.</p>
+ <p>You will be redirected in
+ <span id="countdown-element" class="text-info">20</span> second(s)</p>
+ <p class="text-muted">
+ <small>
+ If you are not redirected, please
+ <a href="{{url_for(
+ 'species.populations.phenotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">click here</a> to continue
+ </small>
+ </p>
+</div>
+{%endif%}
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_pheno_dataset_card(species, population, dataset)}}
+{%endblock%}
+
+
+{%block javascript%}
+<script type="text/javascript">
+ $(document).ready(function() {
+ var countdown = 20;
+ var countdown_element = $("#countdown-element");
+ if(countdown_element.length === 1) {
+ intv = window.setInterval(function() {
+ countdown = countdown - 1;
+ countdown_element.html(countdown);
+ }, 1000);
+ }
+ });
+</script>
+{%endblock%}
diff --git a/uploader/templates/phenotypes/select-population.html b/uploader/templates/phenotypes/select-population.html
new file mode 100644
index 0000000..cea0806
--- /dev/null
+++ b/uploader/templates/phenotypes/select-population.html
@@ -0,0 +1,31 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+
+<div class="row">
+ {{select_population_form(url_for("species.populations.phenotypes.select_population", species_id=species.SpeciesId), species, populations)}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/populations.js"></script>
+<script type="text/javascript">
+ $(function() {
+ populationDataTable(JSON.parse($("#tbl-select-population").attr("data-populations-list")));
+ });
+ </script>
+{%endblock%}
diff --git a/uploader/templates/phenotypes/view-dataset.html b/uploader/templates/phenotypes/view-dataset.html
new file mode 100644
index 0000000..c896214
--- /dev/null
+++ b/uploader/templates/phenotypes/view-dataset.html
@@ -0,0 +1,123 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "macro-table-pagination.html" import table_pagination%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="view-dataset"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.view_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}">View</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>The basic dataset details are:</p>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Full Name</th>
+ <th>Short Name</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr>
+ <td>{{dataset.Name}}</td>
+ <td>{{dataset.FullName}}</td>
+ <td>{{dataset.ShortName}}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<div class="row">
+ <p><a href="{{url_for('species.populations.phenotypes.add_phenotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id)}}"
+ title="Add a bunch of phenotypes"
+ class="btn btn-primary">Add phenotypes</a></p>
+</div>
+
+<div class="row">
+ <h2>Phenotype Data</h2>
+
+ <p>Click on any of the phenotypes in the table below to view and edit that
+ phenotype's data.</p>
+ <p>Use the search to filter through all the phenotypes and find specific
+ phenotypes of interest.</p>
+
+ <table id="tbl-phenotypes-list" class="table compact stripe">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Record</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+
+ <tbody></tbody>
+ </table>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
+
+
+{%block javascript%}
+<script type="text/javascript">
+ $(function() {
+ var data = {{phenotypes | tojson}};
+ $("#tbl-phenotypes-list").DataTable({
+ responsive: true,
+ lengthMenu: [10, 25, 50, 100, 1000, data.length],
+ language: {
+ processing: "Processing results… Please wait.",
+ loadingRecord: "Loading phenotypes — Please wait.",
+ info: "_START_ to _END_ of _TOTAL_ phenotypes",
+ lengthMenu: "Show _MENU_ phenotypes",
+ },
+ data: data,
+ columns: [
+ {data: "sequence_number"},
+ {
+ data: function(pheno) {
+ var spcs_id = {{species.SpeciesId}};
+ var pop_id = {{population.Id}};
+ var dtst_id = {{dataset.Id}};
+ return `<a href="/species/${spcs_id}` +
+ `/populations/${pop_id}` +
+ `/phenotypes/datasets/${dtst_id}` +
+ `/phenotype/${pheno.xref_id}` +
+ `" target="_blank">` +
+ `${pheno.InbredSetCode}_${pheno.xref_id}` +
+ `</a>`;
+ }
+ },
+ {data: function(pheno) {
+ return (pheno.Post_publication_description ||
+ pheno.Original_description ||
+ pheno.Pre_publication_description);
+ }}
+ ]
+ });
+ });
+</script>
+{%endblock%}
diff --git a/uploader/templates/phenotypes/view-phenotype.html b/uploader/templates/phenotypes/view-phenotype.html
new file mode 100644
index 0000000..21ac501
--- /dev/null
+++ b/uploader/templates/phenotypes/view-phenotype.html
@@ -0,0 +1,135 @@
+{%extends "phenotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Phenotypes{%endblock%}
+
+{%block pagetitle%}Phenotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="view-phenotype"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.phenotypes.view_phenotype',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ xref_id=xref_id)}}">View Phenotype</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <div class="panel panel-default">
+ <div class="panel-heading"><strong>Basic Phenotype Details</strong></div>
+
+ <table class="table">
+ <tbody>
+ <tr>
+ <td><strong>Phenotype</strong></td>
+ <td>{{phenotype.Post_publication_description or phenotype.Pre_publication_abbreviation or phenotype.Original_description}}
+ </tr>
+ <tr>
+ <td><strong>Database</strong></td>
+ <td>{{dataset.FullName}}</td>
+ </tr>
+ <tr>
+ <td><strong>Units</strong></td>
+ <td>{{phenotype.Units}}</td>
+ </tr>
+ {%for key,value in publish_data.items()%}
+ <tr>
+ <td><strong>{{key}}</strong></td>
+ <td>{{value}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="2" class="text-muted">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ No publication data found.
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ </div>
+</div>
+
+{%if "group:resource:edit-resource" in privileges
+or "group:resource:delete-resource" in privileges%}
+<div class="row">
+ <div class="btn-group btn-group-justified">
+ <div class="btn-group">
+ {%if "group:resource:edit-resource" in privileges%}
+ <a href="{{url_for('species.populations.phenotypes.edit_phenotype_data',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ dataset_id=dataset.Id,
+ xref_id=xref_id)}}"
+ title="Edit the values for the phenotype. This is meant to be used when you need to update only a few values."
+ class="btn btn-primary">Edit</a>
+ {%endif%}
+ </div>
+ <div class="btn-group"></div>
+ <div class="btn-group">
+ {%if "group:resource:delete-resource" in privileges%}
+ <a href="#"
+ title="Delete the entire phenotype. This is useful when you need to change data for most or all of the fields for this phenotype."
+ class="btn btn-danger not-implemented"
+ disabled="disabled">delete</a>
+ {%endif%}
+ </div>
+ </div>
+</div>
+{%endif%}
+
+<div class="row">
+ <div class="panel panel-default">
+ <div class="panel-heading"><strong>Phenotype Data</strong></div>
+ {%if "group:resource:view-resource" in privileges%}
+ <table class="table">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Sample</th>
+ <th>Value</th>
+ {%if has_se%}
+ <th>SE</th>
+ <th>N</th>
+ {%endif%}
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for item in phenotype.data%}
+ <tr>
+ <td>{{loop.index}}</td>
+ <td>{{item.StrainName}}</td>
+ <td>{{item.value}}</td>
+ {%if has_se%}
+ <td>{{item.error or "-"}}</td>
+ <td>{{item.count or "-"}}</td>
+ {%endif%}
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ {%else%}
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ You do not currently have privileges to view this phenotype in greater
+ detail.
+ </p>
+ {%endif%}
+ </div>
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/platforms/base.html b/uploader/templates/platforms/base.html
new file mode 100644
index 0000000..dac965f
--- /dev/null
+++ b/uploader/templates/platforms/base.html
@@ -0,0 +1,13 @@
+{%extends "species/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="platforms"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.platforms.index')}}">
+ Sequencing Platforms</a>
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/platforms/create-platform.html b/uploader/templates/platforms/create-platform.html
new file mode 100644
index 0000000..0866d5e
--- /dev/null
+++ b/uploader/templates/platforms/create-platform.html
@@ -0,0 +1,124 @@
+{%extends "platforms/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Platforms &mdash; Create Platforms{%endblock%}
+
+{%block pagetitle%}Platforms &mdash; Create Platforms{%endblock%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="create-platform"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.platforms.create_platform',
+ species_id=species.SpeciesId)}}">create platform</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <h2>Create New Platform</h2>
+
+ <p>You can create a new genetic sequencing platform below.</p>
+</div>
+
+<div class="row">
+ <form id="frm-create-platform"
+ method="POST"
+ action="{{url_for('species.platforms.create_platform',
+ species_id=species.SpeciesId)}}">
+
+ <div class="form-group">
+ <label for="txt-geo-platform" class="form-label">GEO Platform</label>
+ <input type="text"
+ id="txt-geo-platform"
+ name="geo-platform"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>This is the platform's
+ <a href="https://www.ncbi.nlm.nih.gov/geo/browse/?view=platforms&tax={{species.TaxonomyId}}"
+ title="Platforms for '{{species.FullName}}' on NCBI">
+ accession value on NCBI</a>. If you do not know the value, click the
+ link and search on NCBI for species '{{species.FullName}}'.</p></small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-platform-name" class="form-label">Platform Name</label>
+ <input type="text"
+ id="txt-platform-name"
+ name="platform-name"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>This is name of the genetic sequencing platform.</p></small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-platform-shortname" class="form-label">
+ Platform Short Name</label>
+ <input type="text"
+ id="txt-platform-shortname"
+ name="platform-shortname"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>Use the following conventions for this field:
+ <ol>
+ <li>Start with a 4-letter vendor code, e.g. "Affy" for "Affymetrix", "Illu" for "Illumina", etc.</li>
+ <li>Append an underscore to the 4-letter vendor code</li>
+ <li>Use the name of the array given by the vendor, e.g. U74AV2, MOE430A, etc.</li>
+ </ol>
+ </p>
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-platform-title" class="form-label">Platform Title</label>
+ <input type="text"
+ id="txt-platform-title"
+ name="platform-title"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>The full platform title. Sometimes, this is the same as the Platform
+ Name above.</p></small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-go-tree-value" class="form-label">GO Tree Value</label>
+ <input type="text"
+ id="txt-go-tree-value"
+ name="go-tree-value"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>This is a Chip identification value useful for analysis with the
+ <strong>
+ <a href="https://www.geneweaver.org/"
+ title="Go to the GeneWeaver site."
+ target="_blank">GeneWeaver</a></strong>
+ and
+ <strong>
+ <a href="https://www.webgestalt.org/"
+ title="Go to the WEB-based GEne SeT AnaLysis Toolkit site."
+ target="_blank">WebGestalt</a></strong>
+ tools.<br />
+ This can be left blank for custom platforms.</p></small>
+ </div>
+
+ <div class="form-group">
+ <input type="submit"
+ value="create new platform"
+ class="btn btn-primary" />
+ </div>
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/platforms/index.html b/uploader/templates/platforms/index.html
new file mode 100644
index 0000000..555b444
--- /dev/null
+++ b/uploader/templates/platforms/index.html
@@ -0,0 +1,25 @@
+{%extends "platforms/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Platforms{%endblock%}
+
+{%block pagetitle%}Platforms{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>In this section, you will be able to view and manage the sequencing
+ platforms that are currently supported by GeneNetwork.</p>
+</div>
+
+<div class="row">
+ {{select_species_form(url_for("species.platforms.index"), species)}}
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/species.js"></script>
+{%endblock%}
diff --git a/uploader/templates/platforms/list-platforms.html b/uploader/templates/platforms/list-platforms.html
new file mode 100644
index 0000000..a6bcfdc
--- /dev/null
+++ b/uploader/templates/platforms/list-platforms.html
@@ -0,0 +1,93 @@
+{%extends "platforms/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Platforms &mdash; List Platforms{%endblock%}
+
+{%block pagetitle%}Platforms &mdash; List Platforms{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>View the list of the genetic sequencing platforms that are currently
+ supported by GeneNetwork.</p>
+ <p>If you cannot find the platform you wish to use, you can add it by clicking
+ the "New Platform" button below.</p>
+ <p><a href="{{url_for('species.platforms.create_platform',
+ species_id=species.SpeciesId)}}"
+ title="Create a new genetic sequencing platform for species {{species.FullName}}"
+ class="btn btn-primary">Create Platform</a></p>
+</div>
+
+<div class="row">
+ <h2>Supported Platforms</h2>
+ {%if platforms is defined and platforms | length > 0%}
+ <p>There are {{total_platforms}} platforms supported by GeneNetwork</p>
+
+ <div class="row">
+ <div class="col-md-2" style="text-align: start;">
+ {%if start_from > 0%}
+ <a href="{{url_for('species.platforms.list_platforms',
+ species_id=species.SpeciesId,
+ start_from=start_from-count,
+ count=count)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
+ <div class="col-md-8" style="text-align: center;">
+ Displaying platforms {{start_from+1}} to {{start_from+count if start_from+count < total_platforms else total_platforms}} of
+ {{total_platforms}}
+ </div>
+ <div class="col-md-2" style="text-align: end;">
+ {%if start_from + count < total_platforms%}
+ <a href="{{url_for('species.platforms.list_platforms',
+ species_id=species.SpeciesId,
+ start_from=start_from+count,
+ count=count)}}">
+ Next
+ <span class="glyphicon glyphicon-forward"></span>
+ </a>
+ {%endif%}
+ </div>
+ </div>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Platform Name</th>
+ <th><a href="https://www.ncbi.nlm.nih.gov/geo/browse/?view=platforms&tax={{species.TaxonomyId}}"
+ title="Gene Expression Omnibus: Platforms section"
+ target="_blank">GEO Platform</a></th>
+ <th>Title</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for platform in platforms%}
+ <tr>
+ <td>{{platform.sequence_number}}</td>
+ <td>{{platform.GeneChipName}}</td>
+ <td><a href="https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc={{platform.GeoPlatform}}"
+ title="View platform on the Gene Expression Omnibus"
+ target="_blank">{{platform.GeoPlatform}}</a></td>
+ <td>{{platform.Title}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ {%else%}
+ <p class="text-warning">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ There are no platforms supported at this time!</p>
+ {%endif%}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/populations/base.html b/uploader/templates/populations/base.html
new file mode 100644
index 0000000..9db8083
--- /dev/null
+++ b/uploader/templates/populations/base.html
@@ -0,0 +1,18 @@
+{%extends "species/base.html"%}
+
+{%block lvl2_breadcrumbs%}
+<li {%if activelink=="populations"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ {%if population is mapping%}
+ <a href="{{url_for('species.populations.view_population',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">{{population.Name}}</a>
+ {%else%}
+ <a href="{{url_for('species.populations.index')}}">Populations</a>
+ {%endif%}
+</li>
+{%block lvl3_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/populations/create-population.html b/uploader/templates/populations/create-population.html
new file mode 100644
index 0000000..c0c4f45
--- /dev/null
+++ b/uploader/templates/populations/create-population.html
@@ -0,0 +1,258 @@
+{%extends "populations/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Create Population{%endblock%}
+
+{%block pagetitle%}Create Population{%endblock%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="create-population"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.create_population',
+ species_id=species.SpeciesId)}}">create population</a>
+</li>
+{%endblock%}
+
+
+{%block contents%}
+<div class="row">
+ <p>The population is the next hierarchical node under Species. Data is grouped under a specific population, under a particular species.</p>
+ <p>
+ This page enables you to create a new population, in the case that you
+ cannot find the population you want in the
+ <a
+ href="{{url_for('species.populations.list_species_populations',
+ species_id=species.SpeciesId)}}"
+ title="Population for species '{{species.FullName}}'.">
+ list of species populations
+ </a>
+ </p>
+</div>
+
+<div class="row">
+ <form method="POST"
+ action="{{url_for('species.populations.create_population',
+ species_id=species.SpeciesId,
+ return_to=return_to)}}">
+
+ <legend>Create Population</legend>
+
+ {{flash_all_messages()}}
+
+ <input type="hidden" name="return_to" value="{{return_to}}">
+
+ <div {%if errors.population_fullname%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="txt-population-fullname" class="form-label">Full Name</label>
+ {%if errors.population_fullname%}
+ <small class="form-text text-danger">{{errors.population_fullname}}</small>
+ {%endif%}
+ <input type="text"
+ id="txt-population-fullname"
+ name="population_fullname"
+ required="required"
+ minLength="3"
+ maxLength="100"
+ value="{{error_values.population_fullname or ''}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>
+ This is a descriptive name for your population &mdash; useful for
+ humans.
+ </p>
+ </small>
+ </div>
+
+ <div {%if errors.population_name%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="txt-population-name" class="form-label">Name</label>
+ {%if errors.population_name%}
+ <small class="form-text text-danger">{{errors.population_name}}</small>
+ {%endif%}
+ <input type="text"
+ id="txt-population-name"
+ name="population_name"
+ required="required"
+ minLength="3"
+ maxLength="30"
+ value="{{error_values.population_name or ''}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p>
+ This is a short representative, but constrained name for your
+ population.
+ <br />
+ The field will only accept letters ('A-Za-z'), numbers (0-9), hyphens
+ and underscores. Any other character will cause the name to be
+ rejected.
+ </p>
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-population-code" class="form-label">Population Code</label>
+ <input type="text"
+ id="txt-population-code"
+ name="population_code"
+ maxLength="5"
+ minLength="3"
+ value="{{error_values.population_code or ''}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ <p class="form-text text-muted">
+ This is a 3-character code for your population, that is prepended to
+ the phenotype identifiers. e.g. For the "BXD Family" population, the
+ code is "BXD" and therefore, the phenotype identifiers for the
+ population look like the following examples: <em>BXD_10148</em>,
+ <em>BXD_10180</em>, <em>BXD_10197</em>, etc.
+ </p>
+ </small>
+ </div>
+
+ <div {%if errors.population_description%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="txt-population-description" class="form-label">
+ Description
+ </label>
+ {%if errors.population_description%}
+ <small class="form-text text-danger">{{errors.population_description}}</small>
+ {%endif%}
+ <textarea
+ id="txt-population-description"
+ name="population_description"
+ required="required"
+ class="form-control"
+ rows="5">{{error_values.population_description or ''}}</textarea>
+ <small class="form-text text-muted">
+ <p>
+ This is a more detailed description for your population. This is
+ useful to communicate with other researchers some details regarding
+ your population, and what its about.
+ <br />
+ Put, here, anything that describes your population but does not go
+ cleanly under metadata.
+ </p>
+ </small>
+ </div>
+
+ <div {%if errors.population_family%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="select-population-family" class="form-label">Family</label>
+ <select id="select-population-family"
+ name="population_family"
+ class="form-control"
+ required="required">
+ <option value="">Please select a family</option>
+ {%for family in families%}
+ <option value="{{family}}"
+ {%if error_values.population_family == family%}
+ selected="selected"
+ {%endif%}>{{family}}</option>
+ {%endfor%}
+ </select>
+ <small class="form-text text-muted">
+ <p>
+ This is a rough grouping of the populations in GeneNetwork into lists
+ of common types of populations.
+ </p>
+ </small>
+ </div>
+
+ <div {%if errors.population_mapping_method_id%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="select-population-mapping-methods"
+ class="form-label">Mapping Methods</label>
+
+ <select id="select-population-mapping-methods"
+ name="population_mapping_method_id"
+ class="form-control"
+ required="required">
+ <option value="">Select appropriate mapping methods</option>
+ {%for mmethod in mapping_methods%}
+ <option value="{{mmethod.id}}"
+ {%if error_values.population_mapping_method_id == mmethod.id%}
+ selected="selected"
+ {%endif%}>{{mmethod.value}}</option>
+ {%endfor%}
+ </select>
+
+ <small class="form-text text-muted">
+ <p>Select the mapping methods that your population will support.</p>
+ </small>
+ </div>
+
+ <div {%if errors.population_genetic_type%}
+ class="form-group has-error"
+ {%else%}
+ class="form-group"
+ {%endif%}>
+ <label for="select-population-genetic-type"
+ class="form-label">Genetic Type</label>
+ <select id="select-population-genetic-type"
+ name="population_genetic_type"
+ class="form-control">
+ <option value="">Select proper genetic type</option>
+ {%for gtype in genetic_types%}
+ <option value="{{gtype}}"
+ {%if error_values.population_genetic_type == gtype%}
+ selected="selected"
+ {%endif%}>{{gtype}}</option>
+ {%endfor%}
+ </select>
+ <small class="form-text text-muted text-danger">
+ <p>
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ This might be a poorly named field.
+ </p>
+ <p>
+ It probably has more to do with the mating crosses/crossings used to
+ produce the individuals in the population. I am no biologist, however,
+ and I'm leaving this here to remind myself to confirm this.
+ </p>
+ <p>
+ I still don't know what riset is.<br />
+ … probably something to do with Recombinant Inbred Strains
+ </p>
+ <p>
+ Possible resources for this:
+ <ul>
+ <li>https://www.informatics.jax.org/silver/chapters/3-2.shtml</li>
+ <li>https://www.informatics.jax.org/silver/chapters/9-2.shtml</li>
+ </ul>
+ </p>
+ </small>
+ </div>
+
+ <div class="form-group">
+ <input type="submit"
+ value="create population"
+ class="btn btn-primary" />
+ </div>
+
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/populations/index.html b/uploader/templates/populations/index.html
new file mode 100644
index 0000000..d2bee77
--- /dev/null
+++ b/uploader/templates/populations/index.html
@@ -0,0 +1,28 @@
+{%extends "populations/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Populations{%endblock%}
+
+{%block pagetitle%}Populations{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ Your experiment data will relate to a particular population from a
+ particular species. Let us know what species it is you want to work with
+ below.
+ </p>
+</div>
+
+<div class="row">
+ {{select_species_form(url_for("species.populations.index"), species)}}
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/species.js"></script>
+{%endblock%}
diff --git a/uploader/templates/populations/list-populations.html b/uploader/templates/populations/list-populations.html
new file mode 100644
index 0000000..f780e94
--- /dev/null
+++ b/uploader/templates/populations/list-populations.html
@@ -0,0 +1,93 @@
+{%extends "populations/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Populations{%endblock%}
+
+{%block pagetitle%}Populations{%endblock%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="list-populations"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.list_species_populations',
+ species_id=species.SpeciesId)}}">List populations</a>
+</li>
+{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+<div class="row">
+ <p>
+ The following populations/groups exist for the '{{species.FullName}}'
+ species.
+ </p>
+ <p>
+ Click on the population's name to select and continue using the population.
+ </p>
+</div>
+
+<div class="row">
+ <p>
+ If the population you need for the species '{{species.FullName}}' does not
+ exist, click on the "Create Population" button below to create a new one.
+ </p>
+ <p>
+ <a href="{{url_for('species.populations.create_population',
+ species_id=species.SpeciesId)}}"
+ title="Create a new population for species '{{species.FullName}}'."
+ class="btn btn-danger">
+ Create Population
+ </a>
+ </p>
+</div>
+
+<div class="row">
+ <table class="table">
+ <caption>Populations for {{species.FullName}}</caption>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Name</th>
+ <th>Full Name</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for population in populations%}
+ <tr>
+ <td>{{population["sequence_number"]}}</td>
+ <td>
+ <a href="{{url_for('species.populations.view_population',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ title="Population '{{population.FullName}}' for species '{{species.FullName}}'.">
+ {{population.Name}}
+ </a>
+ </td>
+ <td>{{population.FullName}}</td>
+ <td>{{population.Description}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="3">
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-mark"></span>
+ There were no populations found for {{species.FullName}}!
+ </p>
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/populations/macro-display-population-card.html b/uploader/templates/populations/macro-display-population-card.html
new file mode 100644
index 0000000..16b477f
--- /dev/null
+++ b/uploader/templates/populations/macro-display-population-card.html
@@ -0,0 +1,41 @@
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%macro display_population_card(species, population)%}
+{{display_species_card(species)}}
+
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Population</h5>
+ <div class="card-text">
+ <table class="table">
+ <tbody>
+ <tr>
+ <td>Name</td>
+ <td>{{population.Name}}</td>
+ </tr>
+
+ <tr>
+ <td>Full Name</td>
+ <td>{{population.FullName}}</td>
+ </tr>
+
+ <tr>
+ <td>Code</td>
+ <td>{{population.InbredSetCode}}</td>
+ </tr>
+
+ <tr>
+ <td>Genetic Type</td>
+ <td>{{population.GeneticType}}</td>
+ </tr>
+
+ <tr>
+ <td>Family</td>
+ <td>{{population.Family}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+{%endmacro%}
diff --git a/uploader/templates/populations/macro-select-population.html b/uploader/templates/populations/macro-select-population.html
new file mode 100644
index 0000000..14b0510
--- /dev/null
+++ b/uploader/templates/populations/macro-select-population.html
@@ -0,0 +1,52 @@
+{%from "macro-step-indicator.html" import step_indicator%}
+
+{%macro select_population_form(form_action, species, populations)%}
+<form method="GET" action="{{form_action}}" class="form-horizontal">
+
+ <h2>{{step_indicator("2")}} What population do you want to work with?</h2>
+
+ {%if populations | length != 0%}
+
+ <p class="form-text">Search for, and select the population from the table
+ below and click "Continue"</p>
+
+ <div class="radio">
+ <label class="control-label" for="rdo-cant-find-population">
+ <input type="radio" id="rdo-cant-find-population"
+ name="population_id" value="CREATE-POPULATION" />
+ I cannot find the population I want &mdash; create it!
+ </label>
+ </div>
+
+ <div class="col-sm-offset-10 col-sm-2">
+ <input type="submit" value="continue" class="btn btn-primary" />
+ </div>
+
+ <div style="margin-top:3em;">
+ <table id="tbl-select-population" class="table compact stripe"
+ data-populations-list='{{populations | tojson}}'>
+ <thead>
+ <tr>
+ <th></th>
+ <th>Population</th>
+ </tr>
+ </thead>
+
+ <tbody></tbody>
+ </table>
+ </div>
+
+ {%else%}
+ <p class="form-text">
+ There are no populations currently defined for {{species['FullName']}}
+ ({{species['SpeciesName']}}).<br />
+ Click "Continue" to create the first!</p>
+ <input type="hidden" name="population_id" value="CREATE-POPULATION" />
+
+ <div class="col-sm-offset-10 col-sm-2">
+ <input type="submit" value="continue" class="btn btn-primary" />
+ </div>
+ {%endif%}
+
+</form>
+{%endmacro%}
diff --git a/uploader/templates/populations/rqtl2/create-tissue-success.html b/uploader/templates/populations/rqtl2/create-tissue-success.html
new file mode 100644
index 0000000..d6fe154
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/create-tissue-success.html
@@ -0,0 +1,106 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Select Tissue</h2>
+
+<div class="row">
+ <p>You have successfully added a new tissue, organ or biological material with
+ the following details:</p>
+</div>
+
+<div class="row">
+ {{flash_all_messages()}}
+
+ <form id="frm-create-tissue-display"
+ method="POST"
+ action="#">
+ <legend class="heading">Create Tissue</legend>
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file" value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+
+ <div class="form-group">
+ <label>Name</label>
+ <label>{{tissue.TissueName}}</label>
+ </div>
+
+ <div class="form-group">
+ <label>Short Name</label>
+ <label>{{tissue.Short_Name}}</label>
+ </div>
+
+ {%if tissue.BIRN_lex_ID%}
+ <div class="form-group">
+ <label>BIRN Lex ID</label>
+ <label>{{tissue.BIRN_lex_ID}}</label>
+ </div>
+ {%endif%}
+
+ {%if tissue.BIRN_lex_Name%}
+ <div class="form-group">
+ <label>BIRN Lex Name</label>
+ <label>{{tissue.BIRN_lex_Name}}</label>
+ </div>
+ {%endif%}
+ </form>
+
+ <div id="action-buttons"
+ style="width:65ch;display:inline-grid;column-gap:5px;">
+
+ <form id="frm-create-tissue-success-continue"
+ method="POST"
+ action="{{url_for('expression-data.rqtl2.select_dataset_info',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ style="display: inline; width: 100%; grid-column: 1 / 2;
+ padding-top: 0.5em; text-align: center; border: none;
+ background-color: inherit;">
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file" value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+ </div>
+</div>
+
+<div class="row">
+ <p style="display:inline;width:100%;grid-column:2/3;text-align:center;
+ color:#336699;font-weight:bold;">
+ OR
+ </p>
+</div>
+
+<div class="row">
+ <form id="frm-create-tissue-success-select-existing"
+ method="POST"
+ action="{{url_for('expression-data.rqtl2.select_tissue',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ style="display: inline; width: 100%; grid-column: 3 / 4;
+ padding-top: 0.5em; text-align: center; border: none;
+ background-color: inherit;">
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file" value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+
+ <button type="submit" class="btn btn-primary">
+ select from existing tissues</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/index.html b/uploader/templates/populations/rqtl2/index.html
new file mode 100644
index 0000000..ec6ffb8
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/index.html
@@ -0,0 +1,54 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Data Upload{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 data upload</h1>
+
+<h2>R/qtl2 Upload</h2>
+
+<div class="row">
+ <form method="POST" action="{{url_for('expression-data.rqtl2.select_species')}}"
+ id="frm-rqtl2-upload">
+ <legend class="heading">upload R/qtl2 bundle</legend>
+ {{flash_messages("error-rqtl2")}}
+
+ <div class="form-group">
+ <label for="select:species" class="form-label">Species</label>
+ <select id="select:species"
+ name="species_id"
+ required="required"
+ class="form-control">
+ <option value="">Select species</option>
+ {%for spec in species%}
+ <option value="{{spec.SpeciesId}}">{{spec.MenuName}}</option>
+ {%endfor%}
+ </select>
+ <small class="form-text text-muted">
+ Data that you upload to the system should belong to a know species.
+ Here you can select the species that you wish to upload data for.
+ </small>
+ </div>
+
+ <input type="submit" class="btn btn-primary" value="submit" />
+ </form>
+</div>
+
+<div class="row">
+ <h2 class="heading">R/qtl2 Bundles</h2>
+
+ <div class="explainer">
+ <p>This feature combines and extends the two upload methods below. Instead of
+ uploading one item at a time, the R/qtl2 bundle you upload can contain both
+ the genotypes data (samples/individuals/cases and their data) and the
+ expression data.</p>
+ <p>The R/qtl2 bundle, additionally, can contain extra metadata, that neither
+ of the methods below can handle.</p>
+
+ <a href="{{url_for('expression-data.rqtl2.select_species')}}"
+ title="Upload a zip bundle of R/qtl2 files">
+ <button class="btn btn-primary">upload R/qtl2 bundle</button></a>
+ </div>
+</div>
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/no-such-job.html b/uploader/templates/populations/rqtl2/no-such-job.html
new file mode 100644
index 0000000..b17004f
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/no-such-job.html
@@ -0,0 +1,13 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 job status</h1>
+
+<h2>R/qtl2 Upload: No Such Job</h2>
+
+<p class="alert-danger">No job with ID {{jobid}} was found.</p>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-job-error.html b/uploader/templates/populations/rqtl2/rqtl2-job-error.html
new file mode 100644
index 0000000..9817518
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-job-error.html
@@ -0,0 +1,39 @@
+{%extends "base.html"%}
+{%from "cli-output.html" import cli_output%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 job status</h1>
+
+<h2>R/qtl2 Upload: Job Status</h2>
+
+<div class="explainer">
+ <p>The processing of the R/qtl2 bundle you uploaded has failed. We have
+ provided some information below to help you figure out what the problem
+ could be.</p>
+ <p>If you find that you cannot figure out what the problem is on your own,
+ please contact the team running the system for assistance, providing the
+ following details:
+ <ul>
+ <li>R/qtl2 bundle you uploaded</li>
+ <li>This URL: <strong>{{request_url()}}</strong></li>
+ <li>(maybe) a screenshot of this page</li>
+ </ul>
+ </p>
+</div>
+
+<h4>stdout</h4>
+{{cli_output(job, "stdout")}}
+
+<h4>stderr</h4>
+{{cli_output(job, "stderr")}}
+
+<h4>Log</h4>
+<div class="cli-output">
+ {%for msg in messages%}
+ {{msg}}<br />
+ {%endfor%}
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-job-results.html b/uploader/templates/populations/rqtl2/rqtl2-job-results.html
new file mode 100644
index 0000000..4ecd415
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-job-results.html
@@ -0,0 +1,24 @@
+{%extends "base.html"%}
+{%from "cli-output.html" import cli_output%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 job status</h1>
+
+<h2>R/qtl2 Upload: Job Status</h2>
+
+<div class="explainer">
+ <p>The processing of the R/qtl2 bundle you uploaded has completed
+ successfully.</p>
+ <p>You should now be able to use GeneNetwork to run analyses on your data.</p>
+</div>
+
+<h4>Log</h4>
+<div class="cli-output">
+ {%for msg in messages%}
+ {{msg}}<br />
+ {%endfor%}
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-job-status.html b/uploader/templates/populations/rqtl2/rqtl2-job-status.html
new file mode 100644
index 0000000..e896f88
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-job-status.html
@@ -0,0 +1,20 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block extrameta%}
+<meta http-equiv="refresh" content="3">
+{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 job status</h1>
+
+<h2>R/qtl2 Upload: Job Status</h2>
+
+<h4>Log</h4>
+<div class="cli-output">
+ <pre>{{"\n".join(messages)}}</pre>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-qc-job-error.html b/uploader/templates/populations/rqtl2/rqtl2-qc-job-error.html
new file mode 100644
index 0000000..90e8887
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-qc-job-error.html
@@ -0,0 +1,120 @@
+{%extends "base.html"%}
+{%from "cli-output.html" import cli_output%}
+
+{%block title%}R/qtl2 bundle: QC Job Error{%endblock%}
+
+{%macro errors_table(tableid, errors)%}
+<table id="{{tableid}}" class="table error-table">
+ <caption>{{caption}}</caption>
+ <thead>
+ <tr>
+ <th>Line</th>
+ <th>Column</th>
+ <th>Value</th>
+ <th>Message</th>
+ </tr>
+ </thead>
+ <tbody>
+ {%for error in errors%}
+ <tr>
+ <td>{{error.line}}</td>
+ <td>{{error.column}}</td>
+ <td>{{error.value}}</td>
+ <td>{{error.message}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="4">No errors to display here.</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+</table>
+{%endmacro%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 bundle: QC job Error</h1>
+
+<div class="explainer">
+ <p>The R/qtl2 bundle has failed some <emph>Quality Control</emph> checks.</p>
+ <p>We list below some of the errors that need to be fixed before the data can
+ be uploaded onto GeneNetwork.</p>
+</div>
+
+{%if errorsgeneric | length > 0%}
+<h2 class="heading">Generic Errors ({{errorsgeneric | length}})</h3>
+<div class="explainer">
+ We found the following generic errors in your R/qtl2 bundle:
+</div>
+
+<h3>Missing Files</h3>
+<div class="explainer">
+ <p>These files are listed in the bundle's control file, but do not actually
+ exist in the bundle</p>
+</div>
+<table id="tbl-errors-missing-files" class="table error-table">
+ <thead>
+ <tr>
+ <th>Control File Key</th>
+ <th>Bundle File Name</th>
+ <th>Message</th>
+ </tr>
+ </thead>
+ <tbody>
+ {%for error in (errorsgeneric | selectattr("type", "equalto", "MissingFile"))%}
+ <tr>
+ <td>{{error.controlfilekey}}</td>
+ <td>{{error.filename}}</td>
+ <td>{{error.message}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+</table>
+
+<h3>Other Generic Errors</h3>
+{{errors_table("tbl-errors-generic", errorsgeneric| selectattr("type", "ne", "MissingFile"))}}
+{%endif%}
+
+{%if errorsgeno | length > 0%}
+<h2 class="heading">Geno Errors ({{errorsgeno | length}})</h3>
+<div class="explainer">
+ We found the following errors in the 'geno' file in your R/qtl2 bundle:
+</div>
+{{errors_table("tbl-errors-geno", errorsgeno[0:50])}}
+{%endif%}
+
+{%if errorspheno | length > 0%}
+<h2 class="heading">Pheno Errors ({{errorspheno | length}})</h3>
+<div class="explainer">
+ We found the following errors in the 'pheno' file in your R/qtl2 bundle:
+</div>
+{{errors_table("tbl-errors-pheno", errorspheno[0:50])}}
+{%endif%}
+
+{%if errorsphenose | length > 0%}
+<h2 class="heading">Phenose Errors ({{errorsphenose | length}})</h3>
+<div class="explainer">
+ We found the following errors in the 'phenose' file in your R/qtl2 bundle:
+</div>
+{{errors_table("tbl-errors-phenose", errorsphenose[0:50])}}
+{%endif%}
+
+{%if errorsphenocovar | length > 0%}
+<h2 class="heading">Phenocovar Errors ({{errorsphenocovar | length}})</h3>
+<div class="explainer">
+ We found the following errors in the 'phenocovar' file in your R/qtl2 bundle:
+</div>
+{{errorsphenocovar}}
+{%endif%}
+
+<h4>stdout</h4>
+{{cli_output(job, "stdout")}}
+
+<h4>stderr</h4>
+{{cli_output(job, "stderr")}}
+
+<h4>Log</h4>
+<div class="cli-output">
+ <pre>{{"\n".join(messages)}}</pre>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-qc-job-results.html b/uploader/templates/populations/rqtl2/rqtl2-qc-job-results.html
new file mode 100644
index 0000000..b3c3a8f
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-qc-job-results.html
@@ -0,0 +1,66 @@
+{%extends "base.html"%}
+{%from "cli-output.html" import cli_output%}
+
+{%block title%}R/qtl2 bundle: QC job results{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 bundle: QC job results</h1>
+
+<div class="row">
+ <p>The R/qtl2 bundle you uploaded has passed all automated quality-control
+ checks successfully.</p>
+ <p>You may now continue to load the data into GeneNetwork for the bundle, with
+ the following details:</p>
+</div>
+
+<div class="row">
+ <form id="form-qc-job-results"
+ action="{{url_for('expression-data.rqtl2.select_dataset_info',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ method="POST">
+ <div class="form-group">
+ <legend>Species</legend>
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+
+ <span class="form-label">Name</span>
+ <span class="form-text">{{species.Name | capitalize}}</span>
+
+ <span class="form-label">Scientific</span>
+ <span class="form-text">{{species.FullName | capitalize}}</span>
+ </div>
+
+ <div class="form-group">
+ <legend>population</legend>
+ <input type="hidden" name="population_id" value="{{population.Id}}" />
+
+ <span class="form-label">Name</span>
+ <span class="form-text">{{population.InbredSetName}}</span>
+
+ <span class="form-label">Full Name</span>
+ <span class="form-text">{{population.FullName}}</span>
+
+ <span class="form-label">Genetic Type</span>
+ <span class="form-text">{{population.GeneticType}}</span>
+
+ <span class="form-label">Description</span>
+ <span class="form-text">{{population.Description or "-"}}</span>
+ </div>
+
+ <div class="form-group">
+ <legend>R/qtl2 Bundle File</legend>
+ <input type="hidden" name="rqtl2_bundle_file" value="{{rqtl2bundle}}" />
+ <input type="hidden" name="original-filename" value="{{rqtl2bundleorig}}" />
+
+ <span class="form-label">Original Name</span>
+ <span class="form-text">{{rqtl2bundleorig}}</span>
+
+ <span class="form-label">Internal Name</span>
+ <span class="form-text">{{rqtl2bundle[0:25]}}&hellip;</span>
+ </div>
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-qc-job-status.html b/uploader/templates/populations/rqtl2/rqtl2-qc-job-status.html
new file mode 100644
index 0000000..f4a6266
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-qc-job-status.html
@@ -0,0 +1,41 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block extrameta%}
+<meta http-equiv="refresh" content="3">
+{%endblock%}
+
+{%block contents%}
+<h1 class="heading">R/qtl2 bundle: QC job status</h1>
+
+{%if geno_percent%}
+<p>
+ <h2>Checking 'geno' file:</h2>
+ <progress id="prg-geno-checking" value="{{geno_percent}}" max="100">
+ {{geno_percent}}%</progress>
+ {{geno_percent}}%</p>
+{%endif%}
+
+{%if pheno_percent%}
+<p>
+ <h2>Checking 'pheno' file:</h2>
+ <progress id="prg-pheno-checking" value="{{pheno_percent}}" max="100">
+ {{pheno_percent}}%</progress>
+ {{pheno_percent}}%</p>
+{%endif%}
+
+{%if phenose_percent%}
+<p>
+ <h2>Checking 'phenose' file:</h2>
+ <progress id="prg-phenose-checking" value="{{phenose_percent}}" max="100">
+ {{phenose_percent}}%</progress>
+ {{phenose_percent}}%</p>
+{%endif%}
+
+<h4>Log</h4>
+<div class="cli-output">
+ <pre>{{"\n".join(messages)}}</pre>
+</div>
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/rqtl2-qc-job-success.html b/uploader/templates/populations/rqtl2/rqtl2-qc-job-success.html
new file mode 100644
index 0000000..f126835
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/rqtl2-qc-job-success.html
@@ -0,0 +1,37 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}R/qtl2 Bundle: Quality Control Successful{%endblock%}
+
+{%block contents%}
+<h2 class="heading">R/qtl2 Bundle: Quality Control Successful</h2>
+
+<div class="row">
+ <p>The R/qtl2 bundle you uploaded has passed <emph>all</emph> quality control
+ checks successfully, and is now ready for uploading into the database.</p>
+ <p>Click "Continue" below to proceed.</p>
+</div>
+
+<!--
+ The "action" on this form takes us to the next step, where we can
+ select all the other data necessary to enter the data into the database.
+ -->
+<div class="row">
+ <form id="frm-upload-rqtl2-bundle"
+ action="{{url_for('expression-data.rqtl2.select_dataset_info',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ method="POST"
+ enctype="multipart/form-data">
+ {{flash_all_messages()}}
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/select-geno-dataset.html b/uploader/templates/populations/rqtl2/select-geno-dataset.html
new file mode 100644
index 0000000..3233abc
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/select-geno-dataset.html
@@ -0,0 +1,69 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Select Genotypes Dataset</h2>
+
+<div class="row">
+ <p>Your R/qtl2 files bundle could contain a "geno" specification. You will
+ therefore need to select from one of the existing Genotype datasets or
+ create a new one.</p>
+ <p>This is the dataset where your data will be organised under.</p>
+</div>
+
+<div class="row">
+ <form id="frm-upload-rqtl2-bundle"
+ action="{{url_for('expression-data.rqtl2.select_geno_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ method="POST"
+ enctype="multipart/form-data">
+ <legend class="heading">select from existing genotype datasets</legend>
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+
+ {{flash_messages("error-rqtl2-select-geno-dataset")}}
+
+ <div class="form-group">
+ <legend>Datasets</legend>
+ <label for="select:geno-datasets" class="form-label">Dataset</label>
+ <select id="select:geno-datasets"
+ name="geno-dataset-id"
+ required="required"
+ {%if datasets | length == 0%}
+ disabled="disabled"
+ {%endif%}
+ class="form-control"
+ aria-describedby="help-geno-dataset-select-dataset">
+ <option value="">Select dataset</option>
+ {%for dset in datasets%}
+ <option value="{{dset['Id']}}">{{dset["Name"]}} ({{dset["FullName"]}})</option>
+ {%endfor%}
+ </select>
+ <span id="help-geno-dataset-select-dataset" class="form-text text-muted">
+ Select from the existing genotype datasets for species
+ {{species.SpeciesName}} ({{species.FullName}}).
+ </span>
+ </div>
+
+ <button type="submit" class="btn btn-primary">select dataset</button>
+ </form>
+</div>
+
+<div class="row">
+ <p>If the genotype dataset you need does not currently exist for your dataset,
+ go the <a href="{{url_for(
+ 'species.populations.genotypes.create_dataset',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Create a new genotypes dataset for {{species.FullName}}">
+ genotypes page to create the genotype dataset</a></p>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/select-population.html b/uploader/templates/populations/rqtl2/select-population.html
new file mode 100644
index 0000000..ded425f
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/select-population.html
@@ -0,0 +1,57 @@
+{%extends "expression-data/index.html"%}
+{%from "flash_messages.html" import flash_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Select Grouping/Population{%endblock%}
+
+{%block contents%}
+<h1 class="heading">Select grouping/population</h1>
+
+<div class="row">
+ <p>The data is organised in a hierarchical form, beginning with
+ <em>species</em> at the very top. Under <em>species</em> the data is
+ organised by <em>population</em>, sometimes referred to as <em>grouping</em>.
+ (In some really old documents/systems, you might see this referred to as
+ <em>InbredSet</em>.)</p>
+ <p>In this section, you get to define what population your data is to be
+ organised by.</p>
+</div>
+
+<div class="row">
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.select_population',
+ species_id=species.SpeciesId)}}">
+ <legend class="heading">select grouping/population</legend>
+ {{flash_messages("error-select-population")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+
+ <div class="form-group">
+ <label for="select:inbredset" class="form-label">population</label>
+ <select id="select:inbredset"
+ name="inbredset_id"
+ required="required"
+ class="form-control">
+ <option value="">Select a grouping/population</option>
+ {%for pop in populations%}
+ <option value="{{pop.InbredSetId}}">
+ {{pop.InbredSetName}} ({{pop.FullName}})</option>
+ {%endfor%}
+ </select>
+ <span class="form-text text-muted">Select the population for your data from
+ the list below.</span>
+ </div>
+
+ <button type="submit" class="btn btn-primary" />select population</button>
+</form>
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
+
+
+{%block javascript%}
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/select-probeset-dataset.html b/uploader/templates/populations/rqtl2/select-probeset-dataset.html
new file mode 100644
index 0000000..74f8f69
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/select-probeset-dataset.html
@@ -0,0 +1,191 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Phenotype(ProbeSet) Dataset</h2>
+
+<div class="row">
+ <p>The R/qtl2 bundle you uploaded contains (a) "<strong>pheno</strong>"
+ file(s). This data needs to be organised under a dataset.</p>
+ <p>This page gives you the ability to do that.</p>
+</div>
+
+{%if datasets | length > 0%}
+<div class="row">
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.select_probeset_dataset',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:select-probeset-dataset">
+ <legend class="heading">Select from existing ProbeSet datasets</legend>
+ {{flash_messages("error-rqtl2")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+ <input type="hidden" name="probe-study-id" value="{{probe_study.Id}}" />
+
+ <div class="form-group">
+ <label class="form-label" for="select:probe-dataset">Dataset</label>
+ <select id="select:probe-dataset"
+ name="probe-dataset-id"
+ required="required"
+ {%if datasets | length == 0%}disabled="disabled"{%endif%}
+ class="form-control"
+ aria-describedby="help-probe-dataset">
+ <option value="">Select a dataset</option>
+ {%for dataset in datasets%}
+ <option value={{dataset.Id}}>
+ {{dataset.Name}}
+ {%if dataset.FullName%}
+ -- ({{dataset.FullName}})
+ {%endif%}
+ </option>
+ {%endfor%}
+ </select>
+
+ <span id="help-probe-dataset" class="form-text text-muted">
+ Select from existing ProbeSet datasets.</span>
+ </div>
+
+ <button type="submit" class="btn btn-primary" />select dataset</button>
+</form>
+</div>
+
+<div class="row">
+ <p style="color:#FE3535; padding-left:20em; font-weight:bolder;">OR</p>
+</div>
+{%endif%}
+
+<div class="row">
+ <p>Create an entirely new ProbeSet dataset for your data.</p>
+</div>
+
+<div class="row">
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.create_probeset_dataset',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:create-probeset-dataset">
+ <legend class="heading">Create a new ProbeSet dataset</legend>
+ {{flash_messages("error-rqtl2-create-probeset-dataset")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+ <input type="hidden" name="probe-study-id" value="{{probe_study.Id}}" />
+
+ <div class="form-group">
+ <label class="form-label" for="select:average">averaging method</label>
+ <select id="select:average"
+ name="averageid"
+ required="required"
+ class="form-control"
+ aria-describedby="help-average">
+ <option value="">Select averaging method</option>
+ {%for avgmethod in avgmethods%}
+ <option value="{{avgmethod.Id}}">
+ {{avgmethod.Name}}
+ {%if avgmethod.Normalization%}
+ ({{avgmethod.Normalization}})
+ {%endif%}
+ </option>
+ {%endfor%}
+ </select>
+
+ <span id="help-average" class="form-text text-muted">
+ Select the averaging method used for your data.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="txt:datasetname">Name</label>
+ <input type="text" id="txt:datasetname" name="datasetname"
+ required="required"
+ maxlength="40"
+ title="Name of the dataset, e.g 'BXDMicroArray_ProbeSet_June03'. (Required)"
+ class="form-control"
+ aria-describedby="help-dataset-name" />
+
+ <span id="help-dataset-name" class="form-text text-muted">
+ Provide a name for the dataset e.g. "BXDMicroArray_ProbeSet_June03". This
+ is mandatory <strong>MUST</strong> be provided.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="txt:datasetfullname">Full Name</label>
+ <input type="text" id="txt:datasetfullname" name="datasetfullname"
+ required="required"
+ maxlength="100"
+ title="A longer name for the dataset, e.g. 'UTHSC Brain mRNA U74Av2 (Jun03) MAS5'. (Required)"
+ class="form-control"
+ aria-describedby="help-dataset-fullname" />
+
+ <span id="help-dataset-fullname" class="form-text text-muted">
+ Provide a longer, more descriptive name for the dataset e.g.
+ "UTHSC Brain mRNA U74Av2 (Jun03) MAS5". This is mandatory and
+ <strong>MUST</strong> be provided.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="txt:datasetshortname">Short Name</label>
+ <input type="text" id="txt:datasetshortname" name="datasetshortname"
+ maxlength="100"
+ title="An abbreviated name for the dataset, e.g 'Br_U_0603_M'. (Optional)"
+ class="form-control"
+ aria-describedby="help-dataset-shortname" />
+
+ <span id="help-dataset-shortname" class="form-text text-muted">
+ Provide a longer, more descriptive name for the dataset e.g. "Br_U_0603_M".
+ This is optional.
+ </span>
+ </div>
+
+ <div class="form-check">
+ <input type="checkbox" id="chk:public" name="datasetpublic"
+ checked="checked"
+ title="Whether or not the dataset is accessible by the general public."
+ class="form-check-input"
+ aria-describedby="help-public" />
+ <label class="form-check-label" for="chk:datasetpublic">Public?</label>
+
+ <span id="help-public" class="form-text text-muted">
+ Check to specify that the dataset will be publicly available. Uncheck to
+ limit access to the dataset.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="select:datasetdatascale">Data Scale</label>
+ <select id="select:datasetdatascale"
+ name="datasetdatascale"
+ required="required"
+ class="form-control"
+ aria-describedby="help-dataset-datascale">
+ <option value="log2" selected="selected">log2</option>
+ <option value="z_score">z_score</option>
+ <option value="log2_ratio">log2_ratio</option>
+ <option value="linear">linear</option>
+ <option value="linear_positive">linear_positive</option>
+ </select>
+
+ <span id="help-dataset-datascale" class="form-text text-muted">
+ Select from a list of scaling methods.
+ </span>
+ </div>
+
+ <button type="submit" class="btn btn-primary">create dataset</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/select-probeset-study-id.html b/uploader/templates/populations/rqtl2/select-probeset-study-id.html
new file mode 100644
index 0000000..e3fd9cc
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/select-probeset-study-id.html
@@ -0,0 +1,143 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages %}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Phenotype(ProbeSet) Study</h2>
+
+<div class="row">
+ <p>The R/qtl2 bundle you uploaded contains (a) "<strong>pheno</strong>"
+ file(s). This data needs to be organised under a study.</p>
+ <p>In this page, you can either select from a existing dataset:</p>
+
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.select_probeset_study',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:select-probeset-study">
+ <legend class="heading">Select from existing ProbeSet studies</legend>
+ {{flash_messages("error-rqtl2-select-probeset-study")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+
+ <div>
+ <label for="select:probe-study" class="form-label">Study</label>
+ <select id="select:probe-study"
+ name="probe-study-id"
+ required="required"
+ aria-describedby="help-select-probeset-study"
+ {%if studies | length == 0%}disabled="disabled"{%endif%}
+ class="form-control">
+ <option value="">Select a study</option>
+ {%for study in studies%}
+ <option value={{study.Id}}>
+ {{study.Name}}
+ {%if study.FullName%}
+ -- ({{study.FullName}})
+ {%endif%}
+ </option>
+ {%endfor%}
+ </select>
+ <small id="help-select-probeset-study" class="form-text text-muted">
+ Select from existing ProbeSet studies.
+ </small>
+ </div>
+
+ <button type="submit" class="btn btn-primary">select study</button>
+ </form>
+</div>
+
+<div class="row">
+ <p style="color:#FE3535; padding-left:20em; font-weight:bolder;">OR</p>
+</div>
+
+<div class="row">
+
+ <p>Create a new ProbeSet dataset below:</p>
+
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.create_probeset_study',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:create-probeset-study">
+ <legend class="heading">Create new ProbeSet study</legend>
+
+ {{flash_messages("error-rqtl2-create-probeset-study")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="tissueid" value="{{tissue.Id}}" />
+
+ <div>
+ <label for="select:platform" class="form-label">Platform</label>
+ <select id="select:platform"
+ name="platformid"
+ required="required"
+ aria-describedby="help-select-platform"
+ {%if platforms | length == 0%}disabled="disabled"{%endif%}
+ class="form-control">
+ <option value="">Select a platform</option>
+ {%for platform in platforms%}
+ <option value="{{platform.GeneChipId}}">
+ {{platform.GeneChipName}} ({{platform.Name}})
+ </option>
+ {%endfor%}
+ </select>
+ <small id="help-select-platform" class="form-text text-muted">
+ Select from a list of known genomics platforms.
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt:studyname" class="form-label">Study name</label>
+ <input type="text" id="txt:studyname" name="studyname"
+ placeholder="Name of the study. (Required)"
+ required="required"
+ maxlength="100"
+ class="form-control" />
+ <span class="form-text text-muted" id="help-study-name">
+ Provide a name for the study.</span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt:studyfullname" class="form-label">Full Study Name</label>
+ <input type="text"
+ id="txt:studyfullname"
+ name="studyfullname"
+ placeholder="Longer name of the study. (Optional)"
+ maxlength="100"
+ class="form-control" />
+ <span class="form-text text-muted" id="help-study-full-name">
+ Provide a longer, more descriptive name for the study. This is optional
+ and you can leave it blank.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt:studyshortname" class="form-label">Short Study Name</label>
+ <input type="text"
+ id="txt:studyshortname"
+ name="studyshortname"
+ placeholder="Shorter name of the study. (Optional)"
+ maxlength="100"
+ class="form-control" />
+ <span class="form-text text-muted" id="help-study-short-name">
+ Provide a shorter name for the study. This is optional and you can leave
+ it blank.
+ </span>
+ </div>
+
+ <button type="submit" class="btn btn-primary">create study</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/select-tissue.html b/uploader/templates/populations/rqtl2/select-tissue.html
new file mode 100644
index 0000000..fe3080a
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/select-tissue.html
@@ -0,0 +1,115 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Tissue</h2>
+
+<div class="row">
+ <p>The data you are uploading concerns a tissue, cell, organ, or other
+ biological material used in an experiment.</p>
+ <p>Select the appropriate biological material below</p>
+</div>
+
+{%if tissues | length > 0%}
+<div class="row">
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.select_tissue',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:select-probeset-dataset">
+ <legend class="heading">Select from existing ProbeSet datasets</legend>
+ {{flash_messages("error-select-tissue")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+
+ <div class="form-group">
+ <label class="form-label" for="select-tissue">Tissue</label>
+ <select id="select-tissue"
+ name="tissueid"
+ required="required"
+ {%if tissues | length == 0%}disabled="disabled"{%endif%}
+ class="form-control"
+ aria-describedby="help-select-tissue">
+ <option value="">Select a tissue</option>
+ {%for tissue in tissues%}
+ <option value={{tissue.Id}}>
+ {{tissue.Name}}
+ {%if tissue.Short_Name%}
+ -- ({{tissue.Short_Name}})
+ {%endif%}
+ </option>
+ {%endfor%}
+ </select>
+
+ <span id="help-select-tissue" class="form-text text-muted">
+ Select from existing biological material.</span>
+ </div>
+
+ <button type="submit" class="btn btn-primary">use selected</button>
+ </form>
+</div>
+
+<div class="row">
+ <p style="color:#FE3535; padding-left:20em; font-weight:bolder;">OR</p>
+</div>
+{%endif%}
+
+<div class="row">
+ <p>If you cannot find the biological material in the drop-down above, add it
+ to the system below.</p>
+
+ <form method="POST"
+ action="{{url_for('expression-data.rqtl2.create_tissue',
+ species_id=species.SpeciesId, population_id=population.Id)}}"
+ id="frm:create-probeset-dataset">
+ <legend class="heading">Add new tissue, organ or biological material</legend>
+ {{flash_messages("error-create-tissue")}}
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+
+ <div class="form-group">
+ <label class="form-label" for="tissue-name">name</label>
+ <input type="text"
+ id="txt-tissuename"
+ name="tissuename"
+ required="required"
+ title = "A name to identify the tissue, organ or biological material."
+ class="form-control"
+ aria-describedby="help-tissue-name" />
+
+ <span class="form-text text-muted" id="help-tissue-name">
+ A name to identify the tissue, organ or biological material.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-shortname" class="form-label">short name</label>
+ <input type="text" id="txt-tissueshortname" name="tissueshortname"
+ required="required"
+ maxlength="7"
+ title="A short name (e.g. 'Mam') for the biological material."
+ class="form-control"
+ aria-describedby="help-tissue-short-name" />
+
+ <span class="form-text text-muted" id="help-tissue-short-name">
+ Provide a short name for the tissue, organ or biological material used in
+ the experiment.
+ </span>
+ </div>
+
+ <button type="submit" class="btn btn-primary" />add new material</button>
+</form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/summary-info.html b/uploader/templates/populations/rqtl2/summary-info.html
new file mode 100644
index 0000000..0adba2e
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/summary-info.html
@@ -0,0 +1,65 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Summary</h2>
+
+<div class="row">
+ <p>This is the information you have provided to accompany the R/qtl2 bundle
+ you have uploaded. Please verify the information is correct before
+ proceeding.</p>
+</div>
+
+<div class="row">
+ <dl>
+ <dt>Species</dt>
+ <dd>{{species.SpeciesName}} ({{species.FullName}})</dd>
+
+ <dt>Population</dt>
+ <dd>{{population.InbredSetName}}</dd>
+
+ {%if geno_dataset%}
+ <dt>Genotype Dataset</dt>
+ <dd>{{geno_dataset.Name}} ({{geno_dataset.FullName}})</dd>
+ {%endif%}
+
+ {%if tissue%}
+ <dt>Tissue</dt>
+ <dd>{{tissue.TissueName}} ({{tissue.Name}}, {{tissue.Short_Name}})</dd>
+ {%endif%}
+
+ {%if probe_study%}
+ <dt>ProbeSet Study</dt>
+ <dd>{{probe_study.Name}} ({{probe_study.FullName}})</dd>
+ {%endif%}
+
+ {%if probe_dataset%}
+ <dt>ProbeSet Dataset</dt>
+ <dd>{{probe_dataset.Name2}} ({{probe_dataset.FullName}})</dd>
+ {%endif%}
+ </dl>
+</div>
+
+<div class="row">
+ <form id="frm:confirm-rqtl2bundle-details"
+ action="{{url_for('expression-data.rqtl2.confirm_bundle_details',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ method="POST"
+ enctype="multipart/form-data">
+ <legend class="heading">Create ProbeSet dataset</legend>
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file" value="{{rqtl2_bundle_file}}" />
+ <input type="hidden" name="geno-dataset-id" value="{{geno_dataset.Id}}" />
+ <input type="hidden" name="probe-study-id" value="{{probe_study.Id}}" />
+ <input type="hidden" name="probe-dataset-id" value="{{probe_dataset.Id}}" />
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-01.html b/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-01.html
new file mode 100644
index 0000000..9d45c5f
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-01.html
@@ -0,0 +1,276 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "upload_progress_indicator.html" import upload_progress_indicator%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+{%macro rqtl2_file_help()%}
+<span class="form-text text-muted">
+ <p>
+ Provide a valid R/qtl2 zip file here. In particular, ensure your zip bundle
+ contains exactly one control file and the corresponding files mentioned in
+ the control file.
+ </p>
+ <p>
+ The control file can be either a YAML or JSON file. <em>ALL</em> other data
+ files in the zip bundle should be CSV files.
+ </p>
+ <p>See the
+ <a href="https://kbroman.org/qtl2/assets/vignettes/input_files.html"
+ target="_blank">
+ R/qtl2 file format specifications
+ </a>
+ for more details.
+ </p>
+</span>
+{%endmacro%}
+{{upload_progress_indicator()}}
+
+<div id="resumable-file-display-template"
+ class="panel panel-info"
+ style="display: none">
+ <div class="panel-heading"></div>
+ <div class="panel-body"></div>
+</div>
+
+
+<h2 class="heading">Upload R/qtl2 Bundle</h2>
+
+<div id="resumable-drop-area"
+ style="display:none;background:#eeeeee;min-height:12em;border-radius:0.5em;padding:1em;">
+ <p>
+ <a id="resumable-browse-button" href="#"
+ class="btn btn-info">Browse</a>
+ </p>
+ <p class="form-text text-muted">
+ You can drag and drop your file here, or click the browse button.
+ Click on the file to remove it.
+ </p>
+ {{rqtl2_file_help()}}
+ <div id="resumable-selected-files"
+ style="display:flex;flex-direction:row;flex-wrap: wrap;justify-content:space-around;gap:10px 20px;"></div>
+ <div id="resumable-class-buttons" style="text-align: right;">
+ <button id="resumable-upload-button"
+ class="btn btn-primary"
+ style="display: none">start upload</button>
+ <button id="resumable-cancel-upload-button"
+ class="btn btn-danger"
+ style="display: none">cancel upload</button>
+ </div>
+ <div id="resumable-progress-bar" class="progress" style="display: none">
+ <div class="progress-bar"
+ role="progress-bar"
+ aria-valuenow="60"
+ aria-valuemin="0"
+ aria-valuemax="100"
+ style="width: 0%;">
+ Uploading: 60%
+ </div>
+ </div>
+</div>
+
+<form id="frm-upload-rqtl2-bundle"
+ action="{{url_for('expression-data.rqtl2.upload_rqtl2_bundle',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ method="POST"
+ enctype="multipart/form-data"
+ data-resumable-target="{{url_for(
+ 'expression-data.rqtl2.upload_rqtl2_bundle_chunked_post',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}">
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+
+ {{flash_all_messages()}}
+
+ <div class="form-group">
+ <legend class="heading">file upload</legend>
+ <label for="file-rqtl2-bundle" class="form-label">R/qtl2 bundle</label>
+ <input type="file" id="file-rqtl2-bundle" name="rqtl2_bundle_file"
+ accept="application/zip, .zip"
+ required="required"
+ class="form-control" />
+ {{rqtl2_file_help()}}
+ </div>
+
+ <button type="submit"
+ class="btn btn-primary"
+ data-toggle="modal"
+ data-target="#upload-progress-indicator">upload R/qtl2 bundle</button>
+</form>
+
+{%endblock%}
+
+{%block javascript%}
+<script src="{{url_for('base.node_modules',
+ filename='resumablejs/resumable.js')}}"></script>
+<script type="text/javascript" src="/static/js/upload_progress.js"></script>
+<script type="text/javascript">
+ function readBinaryFile(file) {
+ return new Promise((resolve, reject) => {
+ var _reader = new FileReader();
+ _reader.onload = (event) => {resolve(_reader.result);};
+ _reader.readAsArrayBuffer(file);
+ });
+ }
+
+ function computeFileChecksum(file) {
+ return readBinaryFile(file)
+ .then((content) => {
+ return window.crypto.subtle.digest(
+ "SHA-256", new Uint8Array(content));
+ }).then((digest) => {
+ return Uint8ArrayToHex(new Uint8Array(digest))
+ });
+ }
+
+ function Uint8ArrayToHex(arr) {
+ var toHex = (val) => {
+ _hex = val.toString(16);
+ if(_hex.length < 2) {
+ return "0" + val;
+ }
+ return _hex;
+ };
+ _hexstr = ""
+ arr.forEach((val) => {_hexstr += toHex(val)});
+ return _hexstr
+ }
+
+ var r = Resumable({
+ target: $("#frm-upload-rqtl2-bundle").attr("data-resumable-target"),
+ fileType: ["zip"],
+ maxFiles: 1,
+ forceChunkSize: true,
+ generateUniqueIdentifier: (file, event) => {
+ return computeFileChecksum(file).then((checksum) => {
+ var _relativePath = (file.webkitRelativePath
+ || file.relativePath
+ || file.fileName
+ || file.name);
+ return checksum + "-" + _relativePath.replace(
+ /[^a-zA-Z0-9_-]/img, "");
+ });
+ }
+ });
+
+ if(r.support) {
+ //Hide form and display drag&drop UI
+ $("#frm-upload-rqtl2-bundle").css("display", "none");
+ $("#resumable-drop-area").css("display", "block");
+
+ // Define UI elements for browse and drag&drop
+ r.assignDrop(document.getElementById("resumable-drop-area"));
+ r.assignBrowse(document.getElementById("resumable-browse-button"));
+
+ // Event handlers
+
+ function display_files(files) {
+ displayArea = $("#resumable-selected-files")
+ displayArea.empty();
+ files.forEach((file) => {
+ var displayElement = $(
+ "#resumable-file-display-template").clone();
+ displayElement.removeAttr("id");
+ displayElement.css("display", "");
+ displayElement.find(".panel-heading").text(file.fileName);
+ list = $("<ul></ul>");
+ list.append($("<li><strong>Name</strong>: "
+ + (file.name
+ || file.fileName
+ || file.relativePath
+ || file.webkitRelativePath)
+ + "</li>"));
+ list.append($("<li><strong>Size</strong>: "
+ + (file.size / (1024*1024)).toFixed(2)
+ + " MB</li>"));
+ list.append($("<li><strong>Unique Identifier</strong>: "
+ + file.uniqueIdentifier + "</li>"));
+ list.append($("<li><strong>Mime</strong>: "
+ + file.file.type
+ + "</li>"));
+ displayElement.find(".panel-body").append(list);
+ displayElement.appendTo("#resumable-selected-files");
+ });
+ }
+
+ r.on("filesAdded", function(files) {
+ display_files(files);
+ $("#resumable-upload-button").css("display", "");
+ $("#resumable-upload-button").on("click", (event) => {
+ r.upload();
+ });
+ });
+
+ r.on("uploadStart", (event) => {
+ $("#resumable-upload-button").css("display", "none");
+ $("#resumable-cancel-upload-button").css("display", "");
+ $("#resumable-cancel-upload-button").on("click", (event) => {
+ r.files.forEach((file) => {
+ if(file.isUploading()) {
+ file.abort();
+ }
+ });
+ $("#resumable-cancel-upload-button").css("display", "none");
+ $("#resumable-upload-button").on("click", (event) => {
+ r.files.forEach((file) => {file.retry();});
+ });
+ $("#resumable-upload-button").css("display", "");
+ });
+ });
+
+ r.on("progress", () => {
+ var progress = (r.progress() * 100).toFixed(2);
+ var pbar = $("#resumable-progress-bar > .progress-bar");
+ $("#resumable-progress-bar").css("display", "");
+ pbar.css("width", progress+"%");
+ pbar.attr("aria-valuenow", progress);
+ pbar.text("Uploading: " + progress + "%");
+ })
+
+ r.on("fileSuccess", (file, message) => {
+ if(message != "OK") {
+ var uri = (window.location.protocol
+ + "//"
+ + window.location.host
+ + message);
+ window.location.replace(uri);
+ }
+ });
+
+ r.on("error", (message, file) => {
+ filename = (file.webkitRelativePath
+ || file.relativePath
+ || file.fileName
+ || file.name);
+ jsonmsg = JSON.parse(message);
+ alert("There was an error while uploading your file '"
+ + filename
+ + "'. The error message was:\n\n\t"
+ + jsonmsg.error
+ + " ("
+ + jsonmsg.statuscode
+ + "): " + jsonmsg.message);
+ })
+ } else {
+ setup_upload_handlers(
+ "frm-upload-rqtl2-bundle", make_data_uploader(
+ function (form) {
+ var formdata = new FormData();
+ formdata.append(
+ "species_id",
+ form.querySelector('input[name="species_id"]').value);
+ formdata.append(
+ "population_id",
+ form.querySelector('input[name="population_id"]').value);
+ formdata.append(
+ "rqtl2_bundle_file",
+ form.querySelector("#file-rqtl2-bundle").files[0]);
+ return formdata;
+ }));
+ }
+</script>
+{%endblock%}
diff --git a/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-02.html b/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-02.html
new file mode 100644
index 0000000..8210ed0
--- /dev/null
+++ b/uploader/templates/populations/rqtl2/upload-rqtl2-bundle-step-02.html
@@ -0,0 +1,33 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Upload R/qtl2 Bundle{%endblock%}
+
+{%block contents%}
+<h2 class="heading">Upload R/qtl2 Bundle</h2>
+
+<div class="row">
+ <p>You have successfully uploaded the zipped bundle of R/qtl2 files.</p>
+ <p>The next step is to select the various extra information we need to figure
+ out what to do with the data. You will select/create the relevant studies
+ and/or datasets to organise the data in the steps that follow.</p>
+ <p>Click "Continue" below to proceed.</p>
+
+ <form id="frm-upload-rqtl2-bundle"
+ action="{{url_for('expression-data.rqtl2.select_dataset_info',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ method="POST"
+ enctype="multipart/form-data">
+ {{flash_all_messages()}}
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id"
+ value="{{population.InbredSetId}}" />
+ <input type="hidden" name="rqtl2_bundle_file"
+ value="{{rqtl2_bundle_file}}" />
+
+ <button type="submit" class="btn btn-primary">continue</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/populations/view-population.html b/uploader/templates/populations/view-population.html
new file mode 100644
index 0000000..b23caeb
--- /dev/null
+++ b/uploader/templates/populations/view-population.html
@@ -0,0 +1,102 @@
+{%extends "populations/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Populations{%endblock%}
+
+{%block pagetitle%}Populations{%endblock%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="view-population"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.view_population',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}">view</a>
+</li>
+{%endblock%}
+
+
+{%block contents%}
+<div class="row">
+ <h2>Population Details</h2>
+
+ {{flash_all_messages()}}
+
+ <dl>
+ <dt>Name</dt>
+ <dd>{{population.Name}}</dd>
+
+ <dt>FullName</dt>
+ <dd>{{population.FullName}}</dd>
+
+ <dt>Code</dt>
+ <dd>{{population.InbredSetCode}}</dd>
+
+ <dt>Genetic Type</dt>
+ <dd>{{population.GeneticType}}</dd>
+
+ <dt>Family</dt>
+ <dd>{{population.Family}}</dd>
+
+ <dt>Description</dt>
+ <dd><pre>{{population.Description or "-"}}</pre></dd>
+ </dl>
+</div>
+
+<div class="row">
+ … maybe provide a way to organise populations in the same family here …
+</div>
+
+<div class="row">
+ <h3>Actions</h3>
+
+ <p>
+ Click any of the following links to use this population in performing the
+ subsequent operations.
+ </p>
+
+ <nav class="nav">
+ <ul>
+ <li>
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Manage samples: Add new or delete existing.">
+ manage samples</a>
+ </li>
+ <li>
+ <a href="{{url_for('species.populations.genotypes.list_genotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Manage genotypes for {{species.FullName}}">Manage Genotypes</a>
+ </li>
+ <li>
+ <a href="{{url_for('species.populations.phenotypes.list_datasets',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Manage phenotype data.">manage phenotype data</a>
+ </li>
+ <li>
+ <a href="#" title="Manage expression data"
+ class="not-implemented">manage expression data</a>
+ </li>
+ <li>
+ <a href="#" title="Manage individual data"
+ class="not-implemented">manage individual data</a>
+ </li>
+ <li>
+ <a href="#" title="Manage RNA-Seq data"
+ class="not-implemented">manage RNA-Seq data</a>
+ </li>
+ </ul>
+ </nav>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/samples/base.html b/uploader/templates/samples/base.html
new file mode 100644
index 0000000..291782b
--- /dev/null
+++ b/uploader/templates/samples/base.html
@@ -0,0 +1,12 @@
+{%extends "populations/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="samples"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.samples.index')}}">Samples</a>
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/samples/index.html b/uploader/templates/samples/index.html
new file mode 100644
index 0000000..ee98734
--- /dev/null
+++ b/uploader/templates/samples/index.html
@@ -0,0 +1,23 @@
+{%extends "samples/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Populations{%endblock%}
+
+{%block pagetitle%}Populations{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>GeneNetwork has a selection of different species of organisms to choose from. Within those species, there are the populations of interest for a variety of experiments, from which you, the researcher, picked your samples (or individuals or cases) from. Here you can provide some basic details about your samples.</p>
+ <p>To start off, we will need to know what species and population your samples belong to. Please provide that information in the next sections.</p>
+
+ {{select_species_form(url_for("species.populations.samples.index"), species)}}
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/species.js"></script>
+{%endblock%}
diff --git a/uploader/templates/samples/list-samples.html b/uploader/templates/samples/list-samples.html
new file mode 100644
index 0000000..185e784
--- /dev/null
+++ b/uploader/templates/samples/list-samples.html
@@ -0,0 +1,132 @@
+{%extends "samples/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Samples &mdash; List Samples{%endblock%}
+
+{%block pagetitle%}Samples &mdash; List Samples{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-samples"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ You selected the population "{{population.FullName}}" from the
+ "{{species.FullName}}" species.
+ </p>
+</div>
+
+{%if samples | length > 0%}
+<div class="row">
+ <p>
+ This population already has <strong>{{total_samples}}</strong>
+ samples/individuals entered. You can explore the list of samples in this
+ population in the table below.
+ </p>
+</div>
+
+<div class="row">
+ <div class="col-md-2">
+ {%if offset > 0:%}
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ from=offset-count,
+ count=count)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
+
+ <div class="col-md-8" style="text-align: center;">
+ Samples {{offset}} &mdash; {{offset+(count if offset + count < total_samples else total_samples - offset)}} / {{total_samples}}
+ </div>
+
+ <div class="col-md-2">
+ {%if offset + count < total_samples:%}
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ from=offset+count,
+ count=count)}}">
+ Next
+ <span class="glyphicon glyphicon-forward"></span>
+ </a>
+ {%endif%}
+ </div>
+</div>
+<div class="row">
+ <table class="table">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Name</th>
+ <th>Auxilliary Name</th>
+ <th>Symbol</th>
+ <th>Alias</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for sample in samples%}
+ <tr>
+ <td>{{sample.sequence_number}}</td>
+ <td>{{sample.Name}}</td>
+ <td>{{sample.Name2}}</td>
+ <td>{{sample.Symbol or "-"}}</td>
+ <td>{{sample.Alias or "-"}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+
+ <p>
+ <a href="#"
+ title="Add samples for population '{{population.FullName}}' from species
+ '{{species.FullName}}'."
+ class="btn btn-danger">
+ delete all samples
+ </a>
+ </p>
+</div>
+
+{%else%}
+
+<div class="row">
+ <p>
+ There are no samples entered for this population. Do please go ahead and add
+ the samples for this population by clicking on the button below.
+ </p>
+
+ <p>
+ <a href="{{url_for('species.populations.samples.upload_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Add samples for population '{{population.FullName}}' from species
+ '{{species.FullName}}'."
+ class="btn btn-primary">
+ add samples
+ </a>
+ </p>
+</div>
+{%endif%}
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/samples/select-population.html b/uploader/templates/samples/select-population.html
new file mode 100644
index 0000000..1cc7573
--- /dev/null
+++ b/uploader/templates/samples/select-population.html
@@ -0,0 +1,26 @@
+{%extends "samples/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Samples &mdash; Select Population{%endblock%}
+
+{%block pagetitle%}Samples &mdash; Select Population{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ {{select_population_form(
+ url_for("species.populations.samples.select_population", species_id=species.SpeciesId), species, populations)}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/populations.js"></script>
+{%endblock%}
diff --git a/uploader/templates/samples/upload-failure.html b/uploader/templates/samples/upload-failure.html
new file mode 100644
index 0000000..458ab55
--- /dev/null
+++ b/uploader/templates/samples/upload-failure.html
@@ -0,0 +1,37 @@
+{%extends "base.html"%}
+{%from "cli-output.html" import cli_output%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Samples Upload Failure{%endblock%}
+
+{%block contents%}
+<div class="row">
+<h2 class="heading">{{job.job_name[0:50]}}&hellip;</h2>
+
+<p>There was a failure attempting to upload the samples.</p>
+
+<p>Here is some information to help with debugging the issue. Provide this
+ information to the developer/maintainer.</p>
+
+<h3>Debugging Information</h3>
+<ul>
+ <li><strong>job id</strong>: {{job.job_id}}</li>
+ <li><strong>status</strong>: {{job.status}}</li>
+ <li><strong>job type</strong>: {{job["job-type"]}}</li>
+</ul>
+</div>
+
+<div class="row">
+<h4>stdout</h4>
+{{cli_output(job, "stdout")}}
+</div>
+
+<div class="row">
+<h4>stderr</h4>
+{{cli_output(job, "stderr")}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/samples/upload-progress.html b/uploader/templates/samples/upload-progress.html
new file mode 100644
index 0000000..677d457
--- /dev/null
+++ b/uploader/templates/samples/upload-progress.html
@@ -0,0 +1,31 @@
+{%extends "samples/base.html"%}
+{%from "cli-output.html" import cli_output%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block extrameta%}
+<meta http-equiv="refresh" content="5">
+{%endblock%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+<div class="row" style="overflow-x: clip;">
+<h2 class="heading">{{job.job_name[0:50]}}&hellip;</h2>
+
+<p>
+<strong>status</strong>:
+<span>{{job["status"]}} ({{job.get("message", "-")}})</span><br />
+</p>
+
+<p>saving to database...</p>
+</div>
+
+<div class="row">
+ {{cli_output(job, "stdout")}}
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/samples/upload-samples.html b/uploader/templates/samples/upload-samples.html
new file mode 100644
index 0000000..25d3290
--- /dev/null
+++ b/uploader/templates/samples/upload-samples.html
@@ -0,0 +1,160 @@
+{%extends "samples/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Samples &mdash; Upload Samples{%endblock%}
+
+{%block pagetitle%}Samples &mdash; Upload Samples{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="uploade-samples"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.samples.upload_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ You can now upload the samples for the "{{population.FullName}}" population
+ from the "{{species.FullName}}" species here.
+ </p>
+ <p>
+ Upload a <strong>character-separated value (CSV)</strong> file that contains
+ details about your samples. The CSV file should have the following fields:
+ <dl>
+ <dt>Name</dt>
+ <dd>The primary name/identifier for the sample/individual.</dd>
+
+ <dt>Name2</dt>
+ <dd>A secondary name for the sample. This can simply be the same as
+ <strong>Name</strong> above. This field <strong>MUST</strong> contain a
+ value.</dd>
+
+ <dt>Symbol</dt>
+ <dd>A symbol for the sample. This can be a strain name, e.g. 'BXD60' for
+ species that have strains. This field can be left empty for species like
+ Humans that do not have strains..</dd>
+
+ <dt>Alias</dt>
+ <dd>An alias for the sample. Can be an empty field, or take on the same
+ value as that of the Symbol.</dd>
+ </dl>
+ </p>
+</div>
+
+<div class="row">
+ <form id="form-samples"
+ method="POST"
+ action="{{url_for('species.populations.samples.upload_samples',
+ species_id=species.SpeciesId,
+ population_id=population.InbredSetId)}}"
+ enctype="multipart/form-data">
+ <legend class="heading">upload samples</legend>
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+ <input type="hidden" name="population_id" value="{{population.Id}}" />
+
+ <div class="form-group">
+ <label for="file-samples" class="form-label">select file</label>
+ <input type="file" name="samples_file" id="file:samples"
+ accept="text/csv, text/tab-separated-values"
+ class="form-control" />
+ </div>
+
+ <div class="form-group">
+ <label for="select:separator" class="form-label">field separator</label>
+ <select id="select:separator"
+ name="separator"
+ required="required"
+ class="form-control">
+ <option value="">Select separator for your file: (default is comma)</option>
+ <option value="&#x0009;">TAB</option>
+ <option value="&#x0020;">Space</option>
+ <option value=",">Comma</option>
+ <option value=";">Semicolon</option>
+ <option value="other">Other</option>
+ </select>
+ <input id="txt:separator"
+ type="text"
+ name="other_separator"
+ class="form-control" />
+ <small class="form-text text-muted">
+ If you select '<strong>Other</strong>' for the field separator value,
+ enter the character that separates the fields in your CSV file in the form
+ field below.
+ </small>
+ </div>
+
+ <div class="form-group form-check">
+ <input id="chk:heading"
+ type="checkbox"
+ name="first_line_heading"
+ class="form-check-input" />
+ <label for="chk:heading" class="form-check-label">
+ first line is a heading?</label>
+ <small class="form-text text-muted">
+ Select this if the first line in your file contains headings for the
+ columns.
+ </small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt:delimiter" class="form-label">field delimiter</label>
+ <input id="txt:delimiter"
+ type="text"
+ name="field_delimiter"
+ maxlength="1"
+ class="form-control" />
+ <small class="form-text text-muted">
+ 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.
+ </small>
+ </div>
+
+ <button type="submit"
+ class="btn btn-primary">upload samples file</button>
+ </form>
+</div>
+
+<div class="row">
+ <h3>Preview File Content</h3>
+
+ <table id="tbl:samples-preview" class="table">
+ <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 in the form above to preview the data.</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
+
+{%block javascript%}
+<script src="/static/js/upload_samples.js" type="text/javascript"></script>
+{%endblock%}
diff --git a/uploader/templates/samples/upload-success.html b/uploader/templates/samples/upload-success.html
new file mode 100644
index 0000000..881d466
--- /dev/null
+++ b/uploader/templates/samples/upload-success.html
@@ -0,0 +1,36 @@
+{%extends "samples/base.html"%}
+{%from "cli-output.html" import cli_output%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Job Status{%endblock%}
+
+{%block contents%}
+
+<div class="row" style="overflow-x: clip;">
+ <h2 class="heading">{{job.job_name[0:50]}}&hellip;</h2>
+
+ <p>
+ <strong>status</strong>:
+ <span>{{job["status"]}} ({{job.get("message", "-")}})</span><br />
+ </p>
+
+ <p>Successfully uploaded the samples.</p>
+ <p>
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="View population samples">
+ View samples
+ </a>
+ </p>
+</div>
+
+<div class="row">
+ {{cli_output(job, "stdout")}}
+</div>
+
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/select_dataset.html b/uploader/templates/select_dataset.html
new file mode 100644
index 0000000..2f07de8
--- /dev/null
+++ b/uploader/templates/select_dataset.html
@@ -0,0 +1,161 @@
+{%extends "base.html"%}
+{%from "dbupdate_hidden_fields.html" import hidden_fields%}
+
+{%block title%}Select Dataset{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" href="/static/css/two-column-with-separator.css" />
+{%endblock%}
+
+{%block contents%}
+<h2 class="heading">{{filename}}: select dataset</h2>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.final_confirmation')}}"
+ id="select-dataset-form" class="two-col-sep-col1">
+ <legend class="heading">choose existing dataset</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, totallines=totallines)}}
+
+ <div class="form-group">
+ <label for="datasetid" class="form-label">dataset:</label>
+ <select id="datasetid" name="datasetid" class="form-control"
+ {%if datasets | length == 0:%}
+ disabled="disabled"
+ {%endif%}>
+ {%for dataset in datasets:%}
+ <option value="{{dataset['Id']}}">
+ [{{dataset["Name"]}}] - {{dataset["FullName"]}}
+ </option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <button type="submit" class="btn btn-primary"
+ {%if datasets | length == 0:%}
+ disabled="disabled"
+ {%endif%} />update database</button>
+</form>
+</div>
+
+<div class="row">
+ <p class="two-col-sep-separator">OR</p>
+</div>
+
+<div class="row">
+ <form method="POST" id="create-dataset-form"
+ action="{{url_for('dbinsert.create_dataset')}}"
+ class="two-col-sep-col2">
+ <legend class="heading">create new dataset</legend>
+ {{hidden_fields(
+ filename, filetype, species=species, genechipid=genechipid,
+ studyid=studyid, totallines=totallines)}}
+
+ {%with messages = get_flashed_messages(with_categories=true)%}
+ {%if messages:%}
+ <ul>
+ {%for category, message in messages:%}
+ <li class="{{category}}">{{message}}</li>
+ {%endfor%}
+ </ul>
+ {%endif%}
+ {%endwith%}
+
+ <div class="form-group">
+ <label for="avgid" class="form-label">average:</label>
+ <select id="avgid" name="avgid" required="required" class="form-control">
+ <option value="">Select averaging method</option>
+ {%for method in avgmethods:%}
+ <option value="{{method['AvgMethodId']}}"
+ {%if avgid is defined and method['AvgMethodId'] | int == avgid | int%}
+ selected="selected"
+ {%endif%}>
+ {{method["Name"]}}
+ </option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <div class="form-group">
+ <label for="datasetname" class="form-label">name:</label>
+ <input id="datasetname" name="datasetname" type="text"
+ class="form-control"
+ {%if datasetname is defined %}
+ value="{{datasetname}}"
+ {%endif%} />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetname2" class="form-label">name 2:</label>
+ <input id="datasetname2" name="datasetname2" type="text"
+ required="required" class="form-control"
+ {%if datasetname2 is defined %}
+ value="{{datasetname2}}"
+ {%endif%} />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetfullname" class="form-label">full name:</label>
+ <input id="datasetfullname" name="datasetfullname" type="text"
+ required="required" class="form-control"
+ {%if datasetfullname is defined %}
+ value="{{datasetfullname}}"
+ {%endif%} />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetshortname" class="form-label">short name:</label>
+ <input id="datasetshortname" name="datasetshortname" type="text"
+ required="required" class="form-control"
+ {%if datasetshortname is defined %}
+ value="{{datasetshortname}}"
+ {%endif%} />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetpublic" class="form-label">public:</label>
+ <input id="datasetpublic" name="datasetpublic" type="number"
+ required="required" min="0" max="2"
+ {%if datasetpublic is defined %}
+ value="{{datasetpublic | int}}"
+ {%else%}
+ value="0"
+ {%endif%}
+ class="form-control" />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetconfidentiality">confidentiality:</label>
+ <input id="datasetconfidentiality" name="datasetconfidentiality"
+ type="number" required="required" min="0" max="2"
+ {%if datasetconfidentiality is defined %}
+ value="{{datasetconfidentiality | int}}"
+ {%else%}
+ value="0"
+ {%endif%}
+ class="form-control" />
+ </div>
+
+ <div class="form-group">
+ <label for="datasetdatascale" class="form-label">data scale:</label>
+ <select id="datasetdatascale" name="datasetdatascale" class="form-control">
+ <option value="">None</option>
+ {%for dscale in datascales:%}
+ <option value="{{dscale}}"
+ {%if datasetdatascale is defined and dscale == datasetdatascale%}
+ selected="selected"
+ {%elif dscale == "log2":%}
+ selected="selected"
+ {%endif%}>
+ {{dscale}}
+ </option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <button type="submit" class="btn btn-primary">create new dataset</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/select_platform.html b/uploader/templates/select_platform.html
new file mode 100644
index 0000000..d9bc68f
--- /dev/null
+++ b/uploader/templates/select_platform.html
@@ -0,0 +1,82 @@
+{%extends "base.html"%}
+
+{%block title%}Select Dataset{%endblock%}
+
+{%block contents%}
+<h2 class="heading">{{filename}}: select platform</h2>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.select_study')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}">
+ <input type="hidden" name="filename" value="{{filename}}" />
+ <input type="hidden" name="filetype" value="{{filetype}}" />
+ <input type="hidden" name="totallines" value="{{totallines}}" />
+
+ <div class="form-group">
+ <label for="species" class="form-label">species</label>
+ <select id="species" name="species" class="form-control">
+ {%for row in species:%}
+ <option value="{{row['SpeciesId']}}"
+ {%if row["Name"] == default_species:%}
+ selected="selected"
+ {%endif%}>
+ {{row["MenuName"]}}
+ </option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <table id="genechips-table" class="table">
+ <caption>select platform</caption>
+ <thead>
+ <tr>
+ <th>Select</th>
+ <th>GeneChip ID</th>
+ <th>GeneChip Name</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for chip in genechips:%}
+ <tr>
+ <td>
+ <input type="radio" name="genechipid" value="{{chip['GeneChipId']}}"
+ required="required" />
+ </td>
+ <td>{{chip["GeneChipId"]}}</td>
+ <td>{{chip["GeneChipName"]}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="5">No chips found for selected species</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+
+ <button type="submit" class="btn btn-primary">submit platform</button>
+ </form>
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script type="text/javascript" src="/static/js/utils.js"></script>
+<script type="text/javascript" src="/static/js/select_platform.js"></script>
+<script type="text/javascript">
+ document.getElementById(
+ "species").addEventListener("change", update_genechips);
+ document.getElementById(
+ "genechips-table").getElementsByTagName(
+ "tbody")[0].addEventListener(
+ "click",
+ function(event) {
+ if(event.target.tagName.toLowerCase() == "td") {
+ return select_row_radio(event.target.parentElement);
+ }
+ if(event.target.tagName.toLowerCase() == "td") {
+ return select_row_radio(event.target);
+ }
+ return false;
+ });
+</script>
+{%endblock%}
diff --git a/uploader/templates/select_study.html b/uploader/templates/select_study.html
new file mode 100644
index 0000000..648ad4c
--- /dev/null
+++ b/uploader/templates/select_study.html
@@ -0,0 +1,108 @@
+{%extends "base.html"%}
+{%from "dbupdate_hidden_fields.html" import hidden_fields%}
+
+{%block title%}Select Dataset{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" href="/static/css/two-column-with-separator.css" />
+{%endblock%}
+
+{%block contents%}
+<h2 class="heading">{{filename}}: select study</h2>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.select_dataset')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col1">
+ <legend class="heading">Select from existing study</legend>
+ {{hidden_fields(filename, filetype, species=species, genechipid=genechipid,
+ totallines=totallines)}}
+
+ <div class="form-group">
+ <label class="form-label" for="study">study:</label>
+ <select id="study" name="studyid" class="form-control">
+ {%for study in studies:%}
+ <option value="{{study['Id']}}">{{study["Name"]}}</option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <button type="submit"
+ class="btn btn-primary"
+ {%if studies | length == 0:%}
+ disabled="disabled"
+ {%endif%} />submit selected study</button>
+</form>
+</div>
+
+<div class="row">
+ <p class="two-col-sep-separator">OR</p>
+</div>
+
+<div class="row">
+ <form method="POST" action="{{url_for('dbinsert.create_study')}}"
+ id="select-platform-form" data-genechips="{{genechips_data}}"
+ class="two-col-sep-col2">
+ {%with messages = get_flashed_messages(with_categories=true)%}
+ {%if messages:%}
+ <ul>
+ {%for category, message in messages:%}
+ <li class="{{category}}">{{message}}</li>
+ {%endfor%}
+ </ul>
+ {%endif%}
+ {%endwith%}
+ <legend class="heading">Create new study</legend>
+ {{hidden_fields(filename, filetype, species=species, genechipid=genechipid,
+ totallines=totallines)}}
+
+ <div class="form-group">
+ <label class="form-label" for="studyname">name:</label>
+ <input type="text" id="studyname" name="studyname" class="form-control"
+ required="required"
+ {%if studyname:%}
+ value="{{studyname}}"
+ {%endif%} />
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="group">group:</label>
+ <select id="group" name="inbredsetid" class="form-control"
+ required="required">
+ <option value="">Select group</option>
+ {%for family in groups:%}
+ <optgroup label="{{family}}">
+ {%for group in groups[family]:%}
+ <option value="{{group['InbredSetId']}}"
+ {%if group["InbredSetId"] == selected_group:%}
+ selected="selected"
+ {%endif%}>
+ {{group["FullName"]}}
+ </option>
+ {%endfor%}
+ </optgroup>
+ {%endfor%}
+ </select>
+ </div>
+
+ <div class="form-group">
+ <label class="form-label" for="tissue">tissue:</label>
+ <select id="tissue" name="tissueid" class="form-control"
+ required="required">
+ <option value="">Select type</option>
+ {%for tissue in tissues:%}
+ <option value="{{tissue['TissueId']}}"
+ {%if tissue["TissueId"] == selected_tissue:%}
+ selected="selected"
+ {%endif%}>
+ {{tissue["Name"]}}
+ </option>
+ {%endfor%}
+ </select>
+ </div>
+
+ <button type="submit" class="btn btn-primary">create study</button>
+ </form>
+</div>
+
+{%endblock%}
diff --git a/uploader/templates/species/base.html b/uploader/templates/species/base.html
new file mode 100644
index 0000000..f64f72b
--- /dev/null
+++ b/uploader/templates/species/base.html
@@ -0,0 +1,17 @@
+{%extends "base.html"%}
+
+{%block lvl1_breadcrumbs%}
+<li {%if activelink=="species"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ {%if species is mapping%}
+ <a href="{{url_for('species.view_species', species_id=species.SpeciesId)}}">
+ {{species.Name}}</a>
+ {%else%}
+ <a href="{{url_for('species.list_species')}}">Species</a>
+ {%endif%}
+</li>
+{%block lvl2_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/species/create-species.html b/uploader/templates/species/create-species.html
new file mode 100644
index 0000000..138dbaa
--- /dev/null
+++ b/uploader/templates/species/create-species.html
@@ -0,0 +1,148 @@
+{%extends "species/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Create Species{%endblock%}
+
+{%block pagetitle%}Create Species{%endblock%}
+
+{%block lvl2_breadcrumbs%}
+<li {%if activelink=="create-species"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.create_species')}}">Create</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+<div class="row">
+ <form id="frm-create-species"
+ method="POST"
+ action="{{url_for('species.create_species', return_to=return_to)}}"
+ class="form-horizontal">
+ <legend>Create Species</legend>
+
+ {{flash_all_messages()}}
+
+ <input type="hidden" name="return_to" value="{{return_to}}">
+
+ <div class="form-group">
+ <label for="txt-taxonomy-id" class="control-label col-sm-2">
+ Taxonomy ID</label>
+ <div class="col-sm-10">
+ <div class="input-group">
+ <input id="txt-taxonomy-id"
+ name="species_taxonomy_id"
+ type="text"
+ class="form-control" />
+ <span class="input-group-btn">
+ <button id="btn-search-taxonid" class="btn btn-info">Search</button>
+ </span>
+ </div>
+ <small class="form-text text-small text-muted">
+ Use
+ <a href="https://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/"
+ title="NCBI's Taxonomy Browser homepage"
+ target="_blank">
+ NCBI's Taxonomy Browser homepage</a> to search for the species you
+ want. If the species exists on NCBI, they will have a Taxonomy ID. Copy
+ that Taxonomy ID to this field, and click "Search" to auto-fill the
+ details.<br />
+ This field is optional.</small>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-name" class="control-label col-sm-2">Common Name</label>
+ <div class="col-sm-10">
+ <input id="txt-species-name"
+ name="common_name"
+ type="text"
+ class="form-control"
+ required="required" />
+ <small class="form-text text-muted">This is the day-to-day term used by
+ laymen, e.g. Mouse (instead of Mus musculus), round worm (instead of
+ Ascaris lumbricoides), etc.<br />
+ For species without this, just enter the scientific name.
+ </small>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-scientific" class="control-label col-sm-2">
+ Scientific Name</label>
+ <div class="col-sm-10">
+ <input id="txt-species-scientific"
+ name="scientific_name"
+ type="text"
+ class="form-control"
+ required="required" />
+ <small class="form-text text-muted">This is the scientific name for the
+ species e.g. Homo sapiens, Mus musculus, etc.</small>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="select-species-family" class="control-label col-sm-2">Family</label>
+ <div class="col-sm-10">
+ <select id="select-species-family"
+ name="species_family"
+ required="required"
+ class="form-control">
+ <option value="ungrouped">I do not know what to pick</option>
+ {%for family in families%}
+ <option value="{{family}}">{{family}}</option>
+ {%endfor%}
+ </select>
+ <small class="form-text text-muted">
+ This is a rough grouping of the species.</small>
+ </div>
+ </div>
+
+ <div class="col-sm-offset-2 col-sm-10">
+ <input type="submit"
+ value="create new species"
+ class="btn btn-primary" />
+ </div>
+
+ </form>
+</div>
+{%endblock%}
+
+{%block javascript%}
+<script>
+ var lastTaxonId = null;
+
+ var fetch_taxonomy = (taxonId) => {
+ var uri = (
+ "https://rest.uniprot.org/taxonomy/" + encodeURIComponent(taxonId));
+ $.get(
+ uri,
+ {},
+ (data, textStatus, jqXHR) => {
+ if(textStatus == "success") {
+ lastTaxonId = taxonId;
+ $("#txt-species-scientific").val(data.scientificName);
+ $("#txt-species-name").val(data.commonName);
+ return false;
+ }
+ msg = (
+ "Request to '${uri}' failed with message '${textStatus}'. "
+ + "Please try again later, or fill the details manually.");
+ alert(msg);
+ console.error(msg, data, textStatus);
+ return false;
+ },
+ "json");
+ };
+
+ $("#btn-search-taxonid").on("click", (event) => {
+ event.preventDefault();
+ taxonId = $("#txt-taxonomy-id").val();
+ if((taxonId !== "") && (taxonId !== lastTaxonId)) {
+ fetch_taxonomy(taxonId);
+ }
+ });
+</script>
+{%endblock%}
diff --git a/uploader/templates/species/edit-species.html b/uploader/templates/species/edit-species.html
new file mode 100644
index 0000000..5a26455
--- /dev/null
+++ b/uploader/templates/species/edit-species.html
@@ -0,0 +1,177 @@
+{%extends "species/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}Edit Species{%endblock%}
+
+{%block pagetitle%}Edit Species{%endblock%}
+
+{%block css%}
+<style type="text/css">
+ .card {
+ margin-top: 0.3em;
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 0.3em;
+ border-color: #AAAAAA;
+ padding: 0.5em;
+ }
+</style>
+{%endblock%}
+
+{%block lvl2_breadcrumbs%}
+<li {%if activelink=="edit-species"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.edit_species_extra',
+ species_id=species.SpeciesId)}}">Edit</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+<div class="row">
+ <form id="frm-edit-species"
+ method="POST"
+ action="{{url_for('species.edit_species_extra',
+ species_id=species.SpeciesId)}}">
+
+ <legend>Edit Extra Detail for Species '{{species.FullName}}'</legend>
+
+ <input type="hidden" name="species_id" value="{{species.SpeciesId}}" />
+
+ <div class="form-group">
+ <label for="lbl-species-taxonid" class="form-label">
+ Taxonomy Id
+ </label>
+ <label id="lbl-species-taxonid"
+ disabled="disabled"
+ class="form-control">{{species.TaxonomyId}}</label>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-name" class="form-label">
+ Common Name
+ </label>
+ <input type="text"
+ id="txt-species-name"
+ name="species_name"
+ required="required"
+ value="{{species.SpeciesName}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ This is the layman's name for the species, e.g. mouse</mall>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-fullname" class="form-label">
+ Scientific Name
+ </label>
+ <input type="text"
+ id="txt-species-fullname"
+ name="species_fullname"
+ required="required"
+ value="{{species.FullName}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ A scientific name for the species that mostly adheres to the biological
+ binomial nomenclature system.</small>
+ </div>
+
+ <div class="form-group">
+ <label for="select-species-family" class="form-label">
+ Family
+ </label>
+ <select id="select-species-family"
+ name="species_family"
+ class="form-control">
+ <option value="">Select the family</option>
+ {%for family in families%}
+ <option value="{{family}}"
+ {%if species.Family == family%}
+ selected="selected"
+ {%endif%}>{{family}}</option>
+ {%endfor%}
+ </select>
+ <small class="form-text text-muted">
+ A general classification for the species. This is mostly for use in
+ GeneNetwork's menus.</small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-familyorderid" class="form-label">
+ Family Order Id
+ </label>
+ <input type="number"
+ id="txt-species-familyorderid"
+ name="species_familyorderid"
+ value="{{species.FamilyOrderId}}"
+ required="required"
+ class="form-control" />
+ <small class="form-text text-muted">
+ This is a number that determines the order of the "Family" groupings
+ above in the GeneNetwork menus. This is an integer value that's manually
+ assigned.</small>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-species-orderid" class="form-label">
+ Order Id
+ </label>
+ <input type="number"
+ id="txt-species-orderid"
+ name="species_orderid"
+ value="{{species.OrderId or (max_order_id + 5)}}"
+ class="form-control" />
+ <small class="form-text text-muted">
+ This integer value determines the order of the species in relation to
+ each other, but also within the respective "Family" groups.</small>
+ </div>
+
+ <div class="form-group">
+ <input type="submit" value="Submit Changes" class="btn btn-primary" />
+ </div>
+
+ </form>
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Family Order</h5>
+ <div class="card-text">
+ <p>The current family order is as follows</p>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Family Order Id</th>
+ <th>Family</th>
+ </tr>
+ </thead>
+ <tbody>
+ {%for item in family_order%}
+ <tr>
+ <td>{{item[0]}}</td>
+ <td>{{item[1]}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Order ID</h5>
+ <div class="card-text">
+ <p>The current largest OrderID is: {{max_order_id}}</p>
+ <p>We recommend giving a new species an order ID that is five more than
+ the current highest i.e. {{max_order_id + 5}}.</p>
+ </div>
+ </div>
+</div>
+{%endblock%}
diff --git a/uploader/templates/species/list-species.html b/uploader/templates/species/list-species.html
new file mode 100644
index 0000000..64084b0
--- /dev/null
+++ b/uploader/templates/species/list-species.html
@@ -0,0 +1,75 @@
+{%extends "species/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}List Species{%endblock%}
+
+{%block pagetitle%}List Species{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+<div class="row">
+ <p>
+ All data in GeneNetwork revolves around species. This is the core of the
+ system.</p>
+ <p>Here you can see a list of all the species available in GeneNetwork.
+ Click on the link besides each species to view greater detail on the species,
+ and access further operations that are possible for said species.</p>
+</div>
+
+<div class="row">
+ <p>If you cannot find the species you are looking for below, click the button
+ below to create it</p>
+ <p><a href="{{url_for('species.create_species')}}"
+ title="Add a new species to GeneNetwork"
+ class="btn btn-danger">Create Species</a></p>
+</div>
+
+<div class="row">
+ <table class="table">
+ <caption>Available Species</caption>
+ <thead>
+ <tr>
+ <th></td>
+ <th title="A common, layman's name for the species.">Common Name</th>
+ <th title="The scientific name for the species">Organism Name</th>
+ <th title="An identifier for the species in the NCBI taxonomy database">
+ Taxonomy ID
+ </th>
+ <th title="A generic grouping used internally by GeneNetwork for organising species.">
+ Family
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {%for species in allspecies%}
+ <tr>
+ <td>{{species["sequence_number"]}}</td>
+ <td>{{species["SpeciesName"]}}</td>
+ <td>
+ <a href="{{url_for('species.view_species',
+ species_id=species['SpeciesId'])}}"
+ title="View details in GeneNetwork on {{species['FullName']}}">
+ {{species["FullName"]}}
+ </a>
+ </td>
+ <td>
+ <a href="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={{species['TaxonomyId']}}"
+ title="View species details on NCBI"
+ target="_blank">{{species["TaxonomyId"]}}</a>
+ </td>
+ <td>{{species.Family}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="3">
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-mark"></span>
+ There were no species found!
+ </p>
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+</div>
+{%endblock%}
diff --git a/uploader/templates/species/macro-display-species-card.html b/uploader/templates/species/macro-display-species-card.html
new file mode 100644
index 0000000..166c7b9
--- /dev/null
+++ b/uploader/templates/species/macro-display-species-card.html
@@ -0,0 +1,22 @@
+{%macro display_species_card(species)%}
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Species</h5>
+ <div class="card-text">
+ <table class="table">
+ <tbody>
+ <tr>
+ <td>Common Name</td>
+ <td>{{species.SpeciesName}}</td>
+ </tr>
+
+ <tr>
+ <td>Scientific Name</td>
+ <td>{{species.FullName}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+{%endmacro%}
diff --git a/uploader/templates/species/macro-select-species.html b/uploader/templates/species/macro-select-species.html
new file mode 100644
index 0000000..3714ae4
--- /dev/null
+++ b/uploader/templates/species/macro-select-species.html
@@ -0,0 +1,59 @@
+{%from "macro-step-indicator.html" import step_indicator%}
+
+{%macro select_species_form(form_action, species)%}
+<form method="GET" action="{{form_action}}" class="form-horizontal">
+
+ <h2>{{step_indicator("1")}} What species do you want to work with?</h2>
+
+ {%if species | length != 0%}
+
+ <p class="form-text">Search for, and select the species from the table below
+ and click "Continue"</p>
+
+ <div class="radio">
+ <label for="rdo-cant-find-species"
+ style="font-weight: 1;">
+ <input id="rdo-cant-find-species" type="radio" name="species_id"
+ value="CREATE-SPECIES" />
+ I could not find the species I want (create it).
+ </label>
+ </div>
+
+ <div class="col-sm-offset-10 col-sm-2">
+ <input type="submit"
+ class="btn btn-primary"
+ value="continue" />
+ </div>
+
+ <div style="margin-top:3em;">
+ <table id="tbl-select-species" class="table compact stripe"
+ data-species-list='{{species | tojson}}'>
+ <div class="">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Species Name</th>
+ </tr>
+ </thead>
+
+ <tbody></tbody>
+ </table>
+ </div>
+
+ {%else%}
+
+ <label class="control-label" for="rdo-cant-find-species">
+ <input id="rdo-cant-find-species" type="radio" name="species_id"
+ value="CREATE-SPECIES" />
+ There are no species to select from. Create the first one.</label>
+
+ <div class="col-sm-offset-10 col-sm-2">
+ <input type="submit"
+ class="btn btn-primary col-sm-offset-1"
+ value="continue" />
+ </div>
+
+ {%endif%}
+
+</form>
+{%endmacro%}
diff --git a/uploader/templates/species/view-species.html b/uploader/templates/species/view-species.html
new file mode 100644
index 0000000..2d02f7e
--- /dev/null
+++ b/uploader/templates/species/view-species.html
@@ -0,0 +1,90 @@
+{%extends "species/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}View Species{%endblock%}
+
+{%block pagetitle%}View Species{%endblock%}
+
+{%block lvl2_breadcrumbs%}
+<li {%if activelink=="view-species"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.view_species', species_id=species.SpeciesId)}}">View</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+<div class="row">
+ <h2>Details on species {{species.FullName}}</h2>
+
+ <dl>
+ <dt>Common Name</dt>
+ <dd>{{species.SpeciesName}}</dd>
+
+ <dt>Scientific Name</dt>
+ <dd>{{species.FullName}}</dd>
+
+ <dt>Taxonomy ID</dt>
+ <dd>{{species.TaxonomyId}}</dd>
+ </dl>
+
+ <h3>Actions</h3>
+
+ <p>
+ You can proceed to perform any of the following actions for species
+ {{species.FullName}}
+ </p>
+
+ <ol>
+ <li>
+ <a href="{{url_for('species.populations.list_species_populations',
+ species_id=species.SpeciesId)}}"
+ title="Create/Edit populations for {{species.FullName}}">
+ Manage populations</a>
+ </li>
+ <li>
+ <a href="{{url_for('species.platforms.list_platforms',
+ species_id=species.SpeciesId)}}"
+ title="Create/Edit sequencing platforms for {{species.FullName}}">
+ Manage sequencing platforms</a>
+ </li>
+ </ol>
+
+
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+<div class="card">
+ <div class="card-body">
+ <h5 class="card-title">Species Extras</h5>
+ <div class="card-text">
+ <p>Some extra internal-use details (mostly for UI concerns on GeneNetwork)</p>
+ <p>
+ <small>
+ If you do not understand what the following are about, simply ignore them
+ &mdash;
+ They have no bearing whatsoever on your data, or its analysis.
+ </small>
+ </p>
+ <dl>
+ <dt>Family</dt>
+ <dd>{{species.Family}}</dd>
+
+ <dt>FamilyOrderId</dt>
+ <dd>{{species.FamilyOrderId}}</dd>
+
+ <dt>OrderId</dt>
+ <dd>{{species.OrderId}}</dd>
+ </dl>
+ </div>
+ <a href="{{url_for('species.edit_species_extra',
+ species_id=species.SpeciesId)}}"
+ class="card-link"
+ title="Edit the species' internal-use details.">Edit</a>
+ </div>
+</div>
+{%endblock%}
diff --git a/uploader/templates/stdout_output.html b/uploader/templates/stdout_output.html
new file mode 100644
index 0000000..85345a9
--- /dev/null
+++ b/uploader/templates/stdout_output.html
@@ -0,0 +1,8 @@
+{%macro stdout_output(job)%}
+
+<h4>STDOUT Output</h4>
+<div class="cli-output">
+ <pre>{{job.get("stdout", "")}}</pre>
+</div>
+
+{%endmacro%}
diff --git a/uploader/templates/unhandled_exception.html b/uploader/templates/unhandled_exception.html
new file mode 100644
index 0000000..cfb0c0b
--- /dev/null
+++ b/uploader/templates/unhandled_exception.html
@@ -0,0 +1,24 @@
+{%extends "base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+
+{%block title%}System Error{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" href="/static/css/two-column-with-separator.css" />
+{%endblock%}
+
+{%block contents%}
+<div class="row">
+ {{flash_all_messages()}}
+ <h1>Exception!</h1>
+
+ <p>An error has occured, and your request has been aborted. Please notify the
+ administrator to try and get this fixed.</p>
+ <p>The system has failed with the following error:</p>
+</div>
+<div class="row">
+ <pre>
+ {{trace}}
+ </pre>
+</div>
+{%endblock%}
diff --git a/uploader/templates/upload_progress_indicator.html b/uploader/templates/upload_progress_indicator.html
new file mode 100644
index 0000000..e274e83
--- /dev/null
+++ b/uploader/templates/upload_progress_indicator.html
@@ -0,0 +1,35 @@
+{%macro upload_progress_indicator()%}
+<div id="upload-progress-indicator" class="modal fade" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h3 class="modal-title">Uploading file</h3>
+ </div>
+
+ <div class="modal-body">
+ <form id="frm-cancel-upload" style="border-style: none;">
+ <div class="form-group">
+ <span id="progress-filename" class="form-text">No file selected!</span>
+ <progress id="progress-bar" value="0" max="100" class="form-control">
+ 0</progress>
+ </div>
+
+ <div class="form-group">
+ <span class="form-text text-muted" id="progress-text">
+ Uploading 0%</span>
+ <span class="form-text text-muted" id="progress-extra-text">
+ Processing</span>
+ </div>
+ </form>
+ </div>
+
+ <div class="modal-footer">
+ <button id="btn-cancel-upload"
+ type="button"
+ class="btn btn-danger"
+ data-dismiss="modal">Cancel</button>
+ </div>
+ </div>
+ </div>
+</div>
+{%endmacro%}
diff --git a/uploader/templates/worker_failure.html b/uploader/templates/worker_failure.html
new file mode 100644
index 0000000..b65b140
--- /dev/null
+++ b/uploader/templates/worker_failure.html
@@ -0,0 +1,24 @@
+{%extends "base.html"%}
+
+{%block title%}Worker Failure{%endblock%}
+
+{%block contents%}
+<h1 class="heading">Worker Failure</h1>
+
+<p>
+ There was a critical failure launching the job to parse your file.
+ This is our fault and (probably) has nothing to do with the file you uploaded.
+</p>
+
+<p>
+ Please notify the developers of this issue when you encounter it,
+ providing the link to this page, or the information below.
+</p>
+
+<h4>Debugging Information</h4>
+
+<ul>
+ <li><strong>job id</strong>: {{job_id}}</li>
+</ul>
+
+{%endblock%}