diff options
Diffstat (limited to 'uploader/templates')
47 files changed, 3222 insertions, 434 deletions
diff --git a/uploader/templates/background-jobs/default-success-page.html b/uploader/templates/background-jobs/default-success-page.html new file mode 100644 index 0000000..5732456 --- /dev/null +++ b/uploader/templates/background-jobs/default-success-page.html @@ -0,0 +1,17 @@ +{%extends "phenotypes/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Success{%endblock%} + +{%block pagetitle%}Background Jobs: Success{%endblock%} + +{%block contents%} +{{flash_all_messages()}} + +<div class="row"> + <p>Job <strong>{{job.job_id}}</strong>, + {%if job.get("metadata", {}).get("job-type")%} + of type '<em>{{job.metadata["job-type"]}}</em> + {%endif%}' completed successfully.</p> +</div> +{%endblock%} diff --git a/uploader/templates/background-jobs/sui-default-success-page.html b/uploader/templates/background-jobs/sui-default-success-page.html new file mode 100644 index 0000000..5732456 --- /dev/null +++ b/uploader/templates/background-jobs/sui-default-success-page.html @@ -0,0 +1,17 @@ +{%extends "phenotypes/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Success{%endblock%} + +{%block pagetitle%}Background Jobs: Success{%endblock%} + +{%block contents%} +{{flash_all_messages()}} + +<div class="row"> + <p>Job <strong>{{job.job_id}}</strong>, + {%if job.get("metadata", {}).get("job-type")%} + of type '<em>{{job.metadata["job-type"]}}</em> + {%endif%}' completed successfully.</p> +</div> +{%endblock%} diff --git a/uploader/templates/base.html b/uploader/templates/base.html index 3c0d0d4..d521ccb 100644 --- a/uploader/templates/base.html +++ b/uploader/templates/base.html @@ -45,7 +45,7 @@ <aside id="nav-sidebar"> <ul class="nav flex-column"> <li {%if activemenu=="home"%}class="activemenu"{%endif%}> - <a href="/" >Home</a></li> + <a href="{{url_for('base.index')}}" >Home</a></li> <li {%if activemenu=="publications"%}class="activemenu"{%endif%}> <a href="{{url_for('publications.index')}}" title="View and manage publications.">Publications</a></li> diff --git a/uploader/templates/flash_messages.html b/uploader/templates/flash_messages.html index b7af178..b42e64e 100644 --- a/uploader/templates/flash_messages.html +++ b/uploader/templates/flash_messages.html @@ -1,11 +1,11 @@ {%macro flash_all_messages()%} {%with messages = get_flashed_messages(with_categories=true)%} {%if messages:%} -<ul> +<div> {%for category, message in messages:%} - <li class="{{category}}">{{message}}</li> + <div class="alert {{category}}">{{message}}</div> {%endfor%} -</ul> +</div> {%endif%} {%endwith%} {%endmacro%} @@ -13,13 +13,13 @@ {%macro flash_messages(filter_class)%} {%with messages = get_flashed_messages(with_categories=true)%} {%if messages:%} -<ul> +<div> {%for category, message in messages:%} {%if filter_class in category%} - <li class="{{category}}">{{message}}</li> + <div class="alert {{category}}">{{message}}</div> {%endif%} {%endfor%} -</ul> +</div> {%endif%} {%endwith%} {%endmacro%} diff --git a/uploader/templates/jobs/job-error.html b/uploader/templates/jobs/job-error.html new file mode 100644 index 0000000..b3015fc --- /dev/null +++ b/uploader/templates/jobs/job-error.html @@ -0,0 +1,17 @@ +{%extends "base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Error{%endblock%} + +{%block pagetitle%}Background Jobs: Error{%endblock%} + +{%block contents%} + +<h1>Background Jobs: Error</h1> +<p>Job <strong>{{job["job_id"]}}</strong> failed!</p> +<p>The error details are in the "STDERR" section below.</p> + +<h2>STDERR</h2> +<pre>{{job["stderr"]}}</pre> +{%endblock%} diff --git a/uploader/templates/jobs/job-status.html b/uploader/templates/jobs/job-status.html index 2750fcd..83c02fd 100644 --- a/uploader/templates/jobs/job-status.html +++ b/uploader/templates/jobs/job-status.html @@ -13,7 +13,7 @@ {%block contents%} <p>Status: {{job["metadata"]["status"]}}</p> -<p>Status: {{job_type}}</p> +<p>Job Type: {{job["metadata"]["job-type"]}}</p> <h2>STDOUT</h2> <pre>{{job["stdout"]}}</pre> @@ -21,6 +21,4 @@ <h2>STDERR</h2> <pre>{{job["stderr"]}}</pre> -<hr /> -<p>The Job: {{job["metadata"]}}</p> {%endblock%} diff --git a/uploader/templates/jobs/sui-job-error.html b/uploader/templates/jobs/sui-job-error.html new file mode 100644 index 0000000..1a839a6 --- /dev/null +++ b/uploader/templates/jobs/sui-job-error.html @@ -0,0 +1,17 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs: Error{%endblock%} + +{%block pagetitle%}Background Jobs: Error{%endblock%} + +{%block contents%} + +<h1>Background Jobs: Error</h1> +<p>Job <strong>{{job["job_id"]}}</strong> failed!</p> +<p>The error details are in the "STDERR" section below.</p> + +<h2>STDERR</h2> +<pre>{{job["stderr"]}}</pre> +{%endblock%} diff --git a/uploader/templates/jobs/sui-job-not-found.html b/uploader/templates/jobs/sui-job-not-found.html new file mode 100644 index 0000000..96c8586 --- /dev/null +++ b/uploader/templates/jobs/sui-job-not-found.html @@ -0,0 +1,11 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}Background Jobs{%endblock%} + +{%block pagetitle%}Background Jobs{%endblock%} + +{%block contents%} +<p>Could not find job with ID: {{job_id}}</p> +{%endblock%} diff --git a/uploader/templates/jobs/sui-job-status.html b/uploader/templates/jobs/sui-job-status.html new file mode 100644 index 0000000..fc5e532 --- /dev/null +++ b/uploader/templates/jobs/sui-job-status.html @@ -0,0 +1,24 @@ +{%extends "sui-base.html"%} + +{%from "flash_messages.html" import flash_all_messages%} + +{%block extrameta%} +<meta http-equiv="refresh" content="5" /> +{%endblock%} + +{%block title%}Background Jobs{%endblock%} + +{%block pagetitle%}Background Jobs{%endblock%} + +{%block contents%} + +<p>Status: {{job["metadata"]["status"]}}</p> +<p>Job Type: {{job["metadata"]["job-type"]}}</p> + +<h2>STDOUT</h2> +<pre>{{job["stdout"]}}</pre> + +<h2>STDERR</h2> +<pre>{{job["stderr"]}}</pre> + +{%endblock%} diff --git a/uploader/templates/macro-forms.html b/uploader/templates/macro-forms.html new file mode 100644 index 0000000..0ccab32 --- /dev/null +++ b/uploader/templates/macro-forms.html @@ -0,0 +1,9 @@ +{%macro add_http_feature_flags()%} +{%for flag in http_feature_flags():%} +{%if (request.args.get(flag) or request.form.get(flag) or ""):%} +<input type="hidden" + name="{{flag}}" + value="{{(request.args.get(flag) or request.form.get(flag))}}" /> +{%endif%} +{%endfor%} +{%endmacro%} diff --git a/uploader/templates/phenotypes/add-phenotypes-base.html b/uploader/templates/phenotypes/add-phenotypes-base.html index a7aaeb0..9909c20 100644 --- a/uploader/templates/phenotypes/add-phenotypes-base.html +++ b/uploader/templates/phenotypes/add-phenotypes-base.html @@ -92,48 +92,54 @@ [ {data: "index"}, { + searchable: true, data: (pub) => { - if(pub.PubMed_ID) { - return `<a href="https://pubmed.ncbi.nlm.nih.gov/` + - `${pub.PubMed_ID}/" target="_blank" ` + - `title="Link to publication on NCBI.">` + - `${pub.PubMed_ID}</a>`; - } - return ""; + if(pub.PubMed_ID) { + return `<a href="https://pubmed.ncbi.nlm.nih.gov/` + + `${pub.PubMed_ID}/" target="_blank" ` + + `title="Link to publication on NCBI.">` + + `${pub.PubMed_ID}</a>`; + } + return ""; } }, { + searchable: true, data: (pub) => { - var title = "⸻"; - if(pub.Title) { - title = pub.Title - } - return `<a href="/publications/view/${pub.Id}" ` + + var title = "⸻"; + if(pub.Title) { + title = pub.Title + } + return `<a href="/publications/view/${pub.Id}" ` + `target="_blank" ` + `title="Link to view publication details">` + `${title}</a>`; } }, { + searchable: true, data: (pub) => { - authors = pub.Authors.split(",").map( - (item) => {return item.trim();}); - if(authors.length > 1) { - return authors[0] + ", et. al."; - } - return authors[0]; + authors = pub.Authors.split(",").map( + (item) => {return item.trim();}); + if(authors.length > 1) { + return authors[0] + ", et. al."; + } + return authors[0]; } } ], { + serverSide: true, ajax: { url: "/publications/list", dataSrc: "publications" }, select: "single", + paging: true, scrollY: 700, - paging: false, deferRender: true, + scroller: true, + scrollCollapse: true, layout: { topStart: "info", topEnd: "search" diff --git a/uploader/templates/phenotypes/add-phenotypes-raw-files.html b/uploader/templates/phenotypes/add-phenotypes-raw-files.html index 1f7ec66..67b56e3 100644 --- a/uploader/templates/phenotypes/add-phenotypes-raw-files.html +++ b/uploader/templates/phenotypes/add-phenotypes-raw-files.html @@ -105,115 +105,213 @@ </div> </fieldset> -<fieldset id="fldset-data-files"> +<fieldset id="fldset-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>')}} - {{display_preview_table("tbl-preview-pheno-desc", "phenotype descriptions")}} - - - <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>')}} - {{display_preview_table("tbl-preview-pheno-data", "phenotype data")}} - - {%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>')}} - {{display_preview_table("tbl-preview-pheno-se", "standard errors")}} + <fieldset id="fldset-descriptions-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-descriptions-transposed" + name="phenotype-descriptions-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-descriptions-transposed" + class="form-check-label"> + Description file transposed?</label> + </div> + + <div class="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>Drag and drop the CSV file that contains the descriptions of your + phenotypes here.</p> + + <p>The CSV file should be a matrix of + <strong>phenotypes × descriptions</strong> i.e. The first column + contains the phenotype names/identifiers whereas the first row is a list + of metadata fields like, "description", "units", etc.</p> + + <p>If the format is transposed (i.e. + <strong>descriptions × phenotypes</strong>) select the checkbox above. + </p> + + <p>Please see the + <a href="#docs-file-phenotype-description" + title="Documentation of the phenotype data file format."> + "Phenotypes Descriptions" documentation</a> section below for more + information on the expected format of the file provided here.</p>')}} + {{display_preview_table( + "tbl-preview-pheno-desc", "phenotype descriptions")}} + </div> + </fieldset> + + + <fieldset id="fldset-data-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-data-transposed" + name="phenotype-data-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-data-transposed" class="form-check-label"> + Data file transposed?</label> + </div> + + <div class="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>Drag and drop a CSV file that contains the phenotypes numerical data + here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + <p>Please see the + <a href="#docs-file-phenotype-data" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format for the file provided here.</p>')}} + {{display_preview_table("tbl-preview-pheno-data", "phenotype data")}} + </div> + </fieldset> - <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>')}} - {{display_preview_table("tbl-preview-pheno-n", "number of samples/individuals")}} + {%if population.Family in families_with_se_and_n%} + <fieldset id="fldset-se-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-se-transposed" + name="phenotype-se-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-se-transposed" class="form-check-label"> + Standard-Errors file transposed?</label> + </div> + <div class="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>Drag and drop a CSV file that contains the phenotypes standard-errors + data here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + + <p>Please see the + <a href="#docs-file-phenotype-se" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format of the file provided here.</p>')}} + + {{display_preview_table("tbl-preview-pheno-se", "standard errors")}} + </div> + </fieldset> + + + <fieldset id="fldset-n-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-n-transposed" + name="phenotype-n-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-n-transposed" class="form-check-label"> + Counts file transposed?</label> + </div> + <div class="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>Drag and drop a CSV file that contains the samples\' phenotypes counts + data here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + + <p>Please see the + <a href="#docs-file-phenotype-se" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format of the file provided here.</p>')}} + + {{display_preview_table("tbl-preview-pheno-n", "number of samples/individuals")}} + </div> + </fieldset> </fieldset> {%endif%} {%endblock%} @@ -326,15 +424,15 @@ <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> + <p>The data is a matrix of <em>samples(or individuals) × phenotypes</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 /> + id,pheno10001,pheno10002,pheno10003,pheno10004,53.099998,…<br /> + IND001,61.400002,49,62.5,55.099998,…<br /> + IND002,54.099998,50.099998,53.299999,55.099998,…<br /> + IND003,483,403,501,403,…<br /> + IND004,49.799999,45.5,62.900002,NA,…<br /> ⋮<br /></code> <p>where <code>IND001,IND002,IND003,IND004,…</code> are the diff --git a/uploader/templates/phenotypes/create-dataset.html b/uploader/templates/phenotypes/create-dataset.html index 8e45491..19a2b34 100644 --- a/uploader/templates/phenotypes/create-dataset.html +++ b/uploader/templates/phenotypes/create-dataset.html @@ -42,7 +42,7 @@ <input type="text" name="dataset-name" id="txt-dataset-name" - value="{{original_formdata.get('dataset-name') or (population.InbredSetCode + 'Publish')}}" + value="{{original_formdata.get('dataset-name') or (population.Name + 'Publish')}}" {%if errors["dataset-name"] is defined%} class="form-control danger" {%else%} @@ -51,7 +51,7 @@ 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. + <p>Recommended: Use the population name 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> @@ -86,7 +86,7 @@ name="dataset-shortname" type="text" class="form-control" - value="{{original_formdata.get('dataset-shortname') or (population.InbredSetCode + ' Publish')}}" /> + value="{{original_formdata.get('dataset-shortname') or (population.Name + '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 diff --git a/uploader/templates/phenotypes/edit-phenotype.html b/uploader/templates/phenotypes/edit-phenotype.html index 32c903f..115d6af 100644 --- a/uploader/templates/phenotypes/edit-phenotype.html +++ b/uploader/templates/phenotypes/edit-phenotype.html @@ -201,130 +201,6 @@ </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 …</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%} diff --git a/uploader/templates/phenotypes/job-status.html b/uploader/templates/phenotypes/job-status.html index 12963c1..257f726 100644 --- a/uploader/templates/phenotypes/job-status.html +++ b/uploader/templates/phenotypes/job-status.html @@ -105,7 +105,7 @@ <td>{{error.filename}}</td> <td>{{error.rowtitle}}</td> <td>{{error.coltitle}}</td> - <td>{%if error.cellvalue | length > 25%} + <td>{%if error.cellvalue is not none and error.cellvalue | length > 25%} {{error.cellvalue[0:24]}}… {%else%} {{error.cellvalue}} diff --git a/uploader/templates/phenotypes/load-phenotypes-success.html b/uploader/templates/phenotypes/load-phenotypes-success.html new file mode 100644 index 0000000..645be16 --- /dev/null +++ b/uploader/templates/phenotypes/load-phenotypes-success.html @@ -0,0 +1,42 @@ +{%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=="load-phenotypes-success"%} + 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%} +<div class="row"> + <p>You have successfully loaded + <!-- maybe indicate the number of phenotypes here? -->your + new phenotypes into the database.</p> + <!-- TODO: Maybe notify user that they have sole access. --> + <!-- TODO: Maybe provide a link to go to GeneNetwork to view the data. --> + <p>View your data + <a href="{{search_page_uri}}" + target="_blank">on GeneNetwork2</a>. + You might need to login to GeneNetwork2 to view specific traits.</p> +</div> +{%endblock%} + +{%block sidebarcontents%} +{{display_pheno_dataset_card(species, population, dataset)}} +{%endblock%} + + +{%block more_javascript%}{%endblock%} diff --git a/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html b/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html index 11b108b..641421d 100644 --- a/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html +++ b/uploader/templates/phenotypes/macro-display-pheno-dataset-card.html @@ -1,4 +1,4 @@ -{%from "populations/macro-display-population-card.html" import display_population_card%} +{%from "populations/macro-display-population-card.html" import display_population_card, display_sui_population_card%} {%macro display_pheno_dataset_card(species, population, dataset)%} {{display_population_card(species, population)}} @@ -29,3 +29,29 @@ </div> </div> {%endmacro%} + +{%macro display_sui_pheno_dataset_card(species, population, dataset)%} +{{display_sui_population_card(species, population)}} + +<div class="row"> + <table class="table"> + <caption>Current dataset</caption> + <tbody> + <tr> + <th>Name</th> + <td>{{dataset.Name}}</td> + </tr> + + <tr> + <th>Full Name</th> + <td>{{dataset.FullName}}</td> + </tr> + + <tr> + <th>Short Name</th> + <td>{{dataset.ShortName}}</td> + </tr> + </tbody> + </table> +</div> +{%endmacro%} diff --git a/uploader/templates/phenotypes/review-job-data.html b/uploader/templates/phenotypes/review-job-data.html index 1343c19..859df74 100644 --- a/uploader/templates/phenotypes/review-job-data.html +++ b/uploader/templates/phenotypes/review-job-data.html @@ -35,6 +35,9 @@ {%if job%} <div class="row"> <h3 class="heading">Data Review</h3> + <p class="text-info"><strong> + The data has <em>NOT</em> been added/saved yet. Review the details below + and click "Continue" to save the data.</strong></p> <p>The “<strong>{{dataset.FullName}}</strong>” dataset from the “<strong>{{population.FullName}}</strong>” population of the species “<strong>{{species.SpeciesName}} ({{species.FullName}})</strong>” diff --git a/uploader/templates/phenotypes/sui-add-phenotypes-base.html b/uploader/templates/phenotypes/sui-add-phenotypes-base.html new file mode 100644 index 0000000..1e71267 --- /dev/null +++ b/uploader/templates/phenotypes/sui-add-phenotypes-base.html @@ -0,0 +1,155 @@ +{%extends "phenotypes/sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%endblock%} + +{%block contents%} +{{super()}} +{{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%} + + <fieldset id="fldset-publication-info"> + <legend>Publication Information</legend> + <input type="hidden" name="publication-id" id="txt-publication-id" /> + <span class="form-text text-muted"> + Select a publication for your data. <br /> + Can't find a publication you can use? Go ahead and + <a href="{{url_for( + 'publications.create_publication', + return_to='species.populations.phenotypes.add_phenotypes', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}">create a new publication</a>.</span> + <table id="tbl-select-publication" class="table compact stripe"> + <thead> + <tr> + <th>#</th> + <th>PubMed ID</th> + <th>Title</th> + <th>Authors</th> + </tr> + </thead> + + <tbody></tbody> + </table> + </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"> + $(function() { + var publicationsDataTable = buildDataTable( + "#tbl-select-publication", + [], + [ + {data: "index"}, + { + searchable: true, + data: (pub) => { + if(pub.PubMed_ID) { + return `<a href="https://pubmed.ncbi.nlm.nih.gov/` + + `${pub.PubMed_ID}/" target="_blank" ` + + `title="Link to publication on NCBI.">` + + `${pub.PubMed_ID}</a>`; + } + return ""; + } + }, + { + searchable: true, + data: (pub) => { + var title = "⸻"; + if(pub.Title) { + title = pub.Title + } + return `<a href="/publications/view/${pub.Id}" ` + + `target="_blank" ` + + `title="Link to view publication details">` + + `${title}</a>`; + } + }, + { + searchable: true, + data: (pub) => { + authors = pub.Authors.split(",").map( + (item) => {return item.trim();}); + if(authors.length > 1) { + return authors[0] + ", et. al."; + } + return authors[0]; + } + } + ], + { + serverSide: true, + ajax: { + url: "/publications/list", + dataSrc: "publications" + }, + select: "single", + paging: true, + scrollY: 700, + deferRender: true, + scroller: true, + scrollCollapse: true, + layout: { + topStart: "info", + topEnd: "search" + } + }); + publicationsDataTable.on("select", (event, datatable, type, indexes) => { + indexes.forEach((element, index, thearray) => { + let row = datatable.row(element).node(); + console.debug(datatable.row(element).data()); + $("#frm-add-phenotypes #txt-publication-id").val( + datatable.row(element).data().Id); + }); + }); + publicationsDataTable.on("deselect", (event, datatable, type, indexes) => { + indexes.forEach((element, index, thearray) => { + let row = datatable.row(element).node(); + $("#frm-add-phenotypes #txt-publication-id").val(null); + }); + }); + }); +</script> + +{%block more_javascript%}{%endblock%} +{%endblock%} diff --git a/uploader/templates/phenotypes/sui-add-phenotypes-raw-files.html b/uploader/templates/phenotypes/sui-add-phenotypes-raw-files.html new file mode 100644 index 0000000..6038617 --- /dev/null +++ b/uploader/templates/phenotypes/sui-add-phenotypes-raw-files.html @@ -0,0 +1,829 @@ +{%extends "phenotypes/sui-add-phenotypes-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} +{%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 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="	" + 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-files"> + <legend>Data File(s)</legend> + + <fieldset id="fldset-descriptions-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-descriptions-transposed" + name="phenotype-descriptions-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-descriptions-transposed" + class="form-check-label"> + Description file transposed?</label> + </div> + + <div class="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>Drag and drop the CSV file that contains the descriptions of your + phenotypes here.</p> + + <p>The CSV file should be a matrix of + <strong>phenotypes × descriptions</strong> i.e. The first column + contains the phenotype names/identifiers whereas the first row is a list + of metadata fields like, "description", "units", etc.</p> + + <p>If the format is transposed (i.e. + <strong>descriptions × phenotypes</strong>) select the checkbox above. + </p> + + <p>Please see the + <a href="#docs-file-phenotype-description" + title="Documentation of the phenotype data file format."> + "Phenotypes Descriptions" documentation</a> section below for more + information on the expected format of the file provided here.</p>')}} + {{display_preview_table( + "tbl-preview-pheno-desc", "phenotype descriptions")}} + </div> + </fieldset> + + + <fieldset id="fldset-data-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-data-transposed" + name="phenotype-data-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-data-transposed" class="form-check-label"> + Data file transposed?</label> + </div> + + <div class="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>Drag and drop a CSV file that contains the phenotypes numerical data + here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + <p>Please see the + <a href="#docs-file-phenotype-data" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format for the file provided here.</p>')}} + {{display_preview_table("tbl-preview-pheno-data", "phenotype data")}} + </div> + </fieldset> + + + {%if population.Family in families_with_se_and_n%} + <fieldset id="fldset-se-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-se-transposed" + name="phenotype-se-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-se-transposed" class="form-check-label"> + Standard-Errors file transposed?</label> + </div> + <div class="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>Drag and drop a CSV file that contains the phenotypes standard-errors + data here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + + <p>Please see the + <a href="#docs-file-phenotype-se" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format of the file provided here.</p>')}} + + {{display_preview_table("tbl-preview-pheno-se", "standard errors")}} + </div> + </fieldset> + + + <fieldset id="fldset-n-file"> + <div class="form-group"> + <div class="form-check"> + <input id="chk-phenotype-n-transposed" + name="phenotype-n-transposed" + type="checkbox" + class="form-check-input" + style="border: solid #8EABF0" /> + <label for="chk-phenotype-n-transposed" class="form-check-label"> + Counts file transposed?</label> + </div> + <div class="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>Drag and drop a CSV file that contains the samples\' phenotypes counts + data here. You can click the "Browse" button (below and to the right) to + select the file from your computer.</p> + + <p>The CSV should be a matrix of <strong>samples × phenotypes</strong>, + i.e. The first column contains the samples identifiers while the first + row is the list of phenotypes identifiers occurring in the phenotypes + descriptions file.</p> + + <p>If the format is transposed (i.e <strong>phenotypes × samples</strong>) + select the checkbox above.</p> + + <p>Please see the + <a href="#docs-file-phenotype-se" + title="Documentation of the phenotype data file format."> + "Phenotypes Data" documentation</a> section below for more information + on the expected format of the file provided here.</p>')}} + + {{display_preview_table("tbl-preview-pheno-n", "number of samples/individuals")}} + </div> + </fieldset> +</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 — 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 – 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>samples(or individuals) × phenotypes</em>, e.g.</p> + <code> + # num-cases: 2549 + # num-phenos: 13 + id,pheno10001,pheno10002,pheno10003,pheno10004,53.099998,…<br /> + IND001,61.400002,49,62.5,55.099998,…<br /> + IND002,54.099998,50.099998,53.299999,55.099998,…<br /> + IND003,483,403,501,403,…<br /> + IND004,49.799999,45.5,62.900002,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 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"), "visually-hidden"); + } else { + remove_class(table.find(".data-row-template"), "visually-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[0].files.length > 0) { + readFirstNLines( + file_input[0].files[0], + 10, + [makePreviewUpdater(preview_table)]); + } + }); + + if(typeof(resumables) !== "undefined") { + resumables.forEach((resumable) => { + if(resumable.files.length > 0) { + readFirstNLines( + resumable.files[0].file, + 10, + [makePreviewUpdater(resumable.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, "visually-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, "visually-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, "visually-hidden"); + remove_class(cancel_button, "visually-hidden"); + add_class(browse_button, "visually-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, "visually-hidden"); + remove_class(retry_button, "visually-hidden"); + remove_class(browse_button, "visually-hidden"); + }); + }; + + + var startUpload = (browse_button, retry_button, cancel_button) => { + return (event) => { + remove_class(cancel_button, "visually-hidden"); + add_class(retry_button, "visually-hidden"); + add_class(browse_button, "visually-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"); + formdata.append("publication-id", $("#txt-publication-id").val()); + 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", "txt"]), + 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) => { + r = makeResumableObject(row[0], row[1], row[2], row[3]); + r.preview_table = $("#" + row[3]); + return r; + }).filter((val) => { + return Boolean(val); + }); + + $("#frm-add-phenotypes input[type=submit]").on("click", (event) => { + event.preventDefault(); + console.debug(); + if ($("#txt-publication-id").val() == "") { + alert("You MUST provide a publication for the phenotypes."); + return false; + } + // 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/sui-add-phenotypes-with-rqtl2-bundle.html b/uploader/templates/phenotypes/sui-add-phenotypes-with-rqtl2-bundle.html new file mode 100644 index 0000000..29a8dea --- /dev/null +++ b/uploader/templates/phenotypes/sui-add-phenotypes-with-rqtl2-bundle.html @@ -0,0 +1,189 @@ +{%extends "phenotypes/sui-add-phenotypes-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%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 /> + ⋮,<br /> + "phenocovar": "your_covariates_file.csv",<br /> + ⋮<br /> + } + </code> + </li> + <li>JSON: multiple files<br /> + <code>{<br /> + ⋮,<br /> + "phenocovar": [<br /> + "covariates_file_01.csv",<br /> + "covariates_file_01.csv",<br /> + ⋮<br /> + ],<br /> + ⋮<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%} diff --git a/uploader/templates/phenotypes/sui-base.html b/uploader/templates/phenotypes/sui-base.html new file mode 100644 index 0000000..d7d980f --- /dev/null +++ b/uploader/templates/phenotypes/sui-base.html @@ -0,0 +1,25 @@ +{%extends "populations/sui-base.html"%} +{%from "phenotypes/macro-display-pheno-dataset-card.html" import display_sui_pheno_dataset_card%} + +{%block breadcrumbs%} +{{super()}} +<li class="breadcrumb-item"> + <a href="{{url_for('species.populations.phenotypes.view_dataset', + species_id=species['SpeciesId'], + population_id=population['Id'], + dataset_id=dataset['Id'])}}"> + {{dataset["Name"]}} + </a> +</li> +{%endblock%} + +{%block contents%} +<div class="row"> + <h2 class="heading">{{dataset.FullName}} ({{dataset.Name}})</h2> +</div> +{%endblock%} + + +{%block sidebarcontents%} +{{display_sui_pheno_dataset_card(species, population, dataset)}} +{%endblock%} diff --git a/uploader/templates/phenotypes/sui-job-status.html b/uploader/templates/phenotypes/sui-job-status.html new file mode 100644 index 0000000..bca87d5 --- /dev/null +++ b/uploader/templates/phenotypes/sui-job-status.html @@ -0,0 +1,140 @@ +{%extends "phenotypes/sui-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%} + +{%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 contents%} + +{%if job%} +<div class="row"> + <h2 class="heading">{{dataset.FullName}} ({{dataset.Name}})</h2> + <h3 class="subheading">upload progress</h3> +</div> +<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> + +<h3 class="subheading">upload errors</h3> +<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 is not none and error.cellvalue | length > 25%} + {{error.cellvalue[0:24]}}… + {%else%} + {{error.cellvalue}} + {%endif%} + </td> + <td> + {%if error.message | length > 250 %} + {{error.message[0:249]}}… + {%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%} diff --git a/uploader/templates/phenotypes/sui-load-phenotypes-success.html b/uploader/templates/phenotypes/sui-load-phenotypes-success.html new file mode 100644 index 0000000..dff0682 --- /dev/null +++ b/uploader/templates/phenotypes/sui-load-phenotypes-success.html @@ -0,0 +1,26 @@ +{%extends "phenotypes/sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-table-pagination.html" import table_pagination%} + +{%block title%}Phenotypes{%endblock%} + +{%block pagetitle%}Phenotypes{%endblock%} + +{%block contents%} +{{super()}} + +<div class="row"> + <p>You have successfully loaded + <!-- maybe indicate the number of phenotypes here? -->your + new phenotypes into the database.</p> + <!-- TODO: Maybe notify user that they have sole access. --> + <!-- TODO: Maybe provide a link to go to GeneNetwork to view the data. --> + <p>View your data + <a href="{{search_page_uri}}" + target="_blank">on GeneNetwork2</a>. + You might need to login to GeneNetwork2 to view specific traits.</p> +</div> +{%endblock%} + + +{%block more_javascript%}{%endblock%} diff --git a/uploader/templates/phenotypes/sui-review-job-data.html b/uploader/templates/phenotypes/sui-review-job-data.html new file mode 100644 index 0000000..ea4183d --- /dev/null +++ b/uploader/templates/phenotypes/sui-review-job-data.html @@ -0,0 +1,121 @@ +{%extends "phenotypes/sui-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 class="text-info"><strong> + The data has <em>NOT</em> been added/saved yet. Review the details below + and click "Continue" to save the data.</strong></p> + <p>The “<strong>{{dataset.FullName}}</strong>” dataset from the + “<strong>{{population.FullName}}</strong>” population of the + species “<strong>{{species.SpeciesName}} ({{species.FullName}})</strong>” + will be updated as follows:</p> + + <ul> + {%if publication%} + <li>All {{summary.get("pheno", {}).get("total-data-rows", "0")}} phenotypes + are linked to the following publication: + <ul> + <li><strong>Publication Title:</strong> + {{publication.Title or "—"}}</li> + <li><strong>Author(s):</strong> + {{publication.Authors or "—"}}</li> + </ul> + </li> + {%endif%} + {%for ftype in ("phenocovar", "pheno", "phenose", "phenonum")%} + {%if summary.get(ftype, False)%} + <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> + {%endif%} + {%endfor%} + </ul> + + <form id="frm-review-phenotype-data" + method="POST" + action="{{url_for('species.populations.phenotypes.load_data_to_database', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}"> + <input type="hidden" name="data-qc-job-id" value="{{job.jobid}}" /> + <input type="submit" + value="continue" + class="btn btn-primary" /> + </form> +</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 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/view-dataset.html b/uploader/templates/phenotypes/view-dataset.html index 21563d6..c634a48 100644 --- a/uploader/templates/phenotypes/view-dataset.html +++ b/uploader/templates/phenotypes/view-dataset.html @@ -46,12 +46,50 @@ </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 class="col"> + <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> + </div> + + <div class="col"> + <form id="frm-recompute-phenotype-means" + method="POST" + action="{{url_for( + 'species.populations.phenotypes.recompute_means', + species_id=species['SpeciesId'], + population_id=population['Id'], + dataset_id=dataset['Id'])}}" + class="d-flex flex-row align-items-center flex-wrap" + style="display: inline;"> + <input type="submit" + title="Compute/Recompute the means for all phenotypes." + class="btn btn-info" + value="(rec/c)ompute means" + id="submit-frm-recompute-phenotype-means" /> + </form> + </div> + + <div class="col"> + <form id="frm-run-qtlreaper" + method="POST" + action="{{url_for( + 'species.populations.phenotypes.rerun_qtlreaper', + species_id=species['SpeciesId'], + population_id=population['Id'], + dataset_id=dataset['Id'])}}" + class="d-flex flex-row align-items-center flex-wrap" + style="display: inline;"> + <input type="submit" + title="Run/Rerun QTLReaper." + class="btn btn-info" + value="(re)run QTLReaper" + id="submit-frm-rerun-qtlreaper" /> + </form> + </div> </div> <div class="row"> @@ -133,100 +171,6 @@ { select: "multi+shift", layout: { - top2: { - buttons: [ - { - extend: "selectAll", - className: "btn btn-info", - titleAttr: "Click to select ALL records in the table." - }, - { - extend: "selectNone", - className: "btn btn-info", - titleAttr: "Click to deselect ANY selected record(s) in the table." - }, - { - text: "Bulk Edit (Download Data)", - className: "btn btn-info btn-bulk-edit", - titleAttr: "Click to download data for editing.", - action: (event, dt, node, config) => { - var phenoids = []; - var selected = dt.rows({selected: true, page: "all"}).data(); - for(var idx = 0; idx < selected.length; idx++) { - phenoids.push({ - phenotype_id: selected[idx].Id, - xref_id: selected[idx].xref_id - }); - } - if(phenoids.length == 0) { - alert("No record selected. Nothing to do!"); - return false; - } - - $(".btn-bulk-edit").prop("disabled", true); - $(".btn-bulk-edit").addClass("d-none"); - var spinner = $( - "<div id='bulk-edit-spinner' class='spinner-grow text-info'>"); - spinner_content = $( - "<span class='visually-hidden'>"); - spinner_content.html( - "Downloading data …"); - spinner.append(spinner_content) - $(".btn-bulk-edit").parent().append( - spinner); - - $.ajax( - (`/species/${species_id}/populations/` + - `${population_id}/phenotypes/datasets/` + - `${dataset_id}/edit-download`), - { - method: "POST", - data: JSON.stringify(phenoids), - xhrFields: { - responseType: "blob" - }, - success: (data, textStatus, jqXHR) => { - var link = document.createElement("a"); - uri = window.URL.createObjectURL(data); - link.href = uri; - link.download = `${dataset_name}_data.tsv`; - - document.body.appendChild(link); - link.click(); - window.URL.revokeObjectURL(uri); - link.remove(); - }, - error: (jQXHR, textStatus, errorThrown) => { - console.log("Experienced an error: ", textStatus); - console.log("The ERROR: ", errorThrown); - }, - complete: (jqXHR, textStatus) => { - $("#bulk-edit-spinner").remove(); - $(".btn-bulk-edit").removeClass( - "d-none"); - $(".btn-bulk-edit").prop( - "disabled", false); - }, - contentType: "application/json" - }); - } - }, - { - text: "Bulk Edit (Upload Data)", - className: "btn btn-info btn-bulk-edit", - titleAttr: "Click to upload edited data you got by clicking the `Bulk Edit (Download Data)` button.", - action: (event, dt, node, config) => { - window.location.assign( - `${window.location.protocol}//` + - `${window.location.host}` + - `/species/${species_id}` + - `/populations/${population_id}` + - `/phenotypes/datasets/${dataset_id}` + - `/edit-upload`) - } - } - ] - }, top1Start: { pageLength: { text: "Show _MENU_ of _TOTAL_" @@ -239,6 +183,27 @@ return `${pheno.InbredSetCode}_${pheno.xref_id}`; } }); + + + $("#submit-frm-rerun-qtlreaper").on( + "click", + function(event) { + // (Re)run the QTLReaper script for selected phenotypes. + event.preventDefault(); + var form = $("#frm-run-qtlreaper"); + form.find(".dynamically-added-element").remove(); + dtPhenotypesList.rows({selected: true}).nodes().each((node, index) => { + _cloned = $(node).find(".chk-row-select").clone(); + _cloned.removeAttr("id"); + _cloned.removeAttr("class"); + _cloned.attr("style", "display: none;"); + _cloned.attr("data-type", "dynamically-added-element"); + _cloned.attr("class", "dynamically-added-element checkbox"); + _cloned.prop("checked", true); + form.append(_cloned); + }); + form.submit(); + }); }); </script> {%endblock%} diff --git a/uploader/templates/phenotypes/view-phenotype.html b/uploader/templates/phenotypes/view-phenotype.html index 21ac501..75e3c1e 100644 --- a/uploader/templates/phenotypes/view-phenotype.html +++ b/uploader/templates/phenotypes/view-phenotype.html @@ -24,8 +24,10 @@ {{flash_all_messages()}} <div class="row"> - <div class="panel panel-default"> - <div class="panel-heading"><strong>Basic Phenotype Details</strong></div> + <div class="card"> + <div class="card-header"> + <h5 class="card-title">Basic Phenotype Details</h5> + </div> <table class="table"> <tbody> @@ -41,24 +43,46 @@ <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> +<div class="row" style="margin-top:5px;"> + <div class="card"> + <div class="card-header"> + <h5 class="card-title">Publication Details</h5> + </div> + + <div class="card-body"> + <table class="table"> + <tbody> + <tr> + {%for key in ("PubMed_ID", "Authors", "Title", "Journal"):%} + <tr> + <td><strong>{{key}}</strong></td> + <td>{{publication.get(key, "")}}</td> + </tr> + {%else%} + <tr> + <td colspan="2" class="text-muted"> + <span class="glyphicon glyphicon-exclamation-sign"></span> + No publication data found. + </td> + </tr> + {%endfor%} + </tr> + </tbody> + </table> + <div style="text-align: right;"> + <a href="{{url_for('publications.edit_publication', publication_id=publication.Id, next=next)}}" + class="btn btn-info">edit</a> + <a href="#" class="btn btn-danger not-implemented">change</a> + </div> + </div> + </div> +</div> + {%if "group:resource:edit-resource" in privileges or "group:resource:delete-resource" in privileges%} <div class="row"> diff --git a/uploader/templates/populations/create-population.html b/uploader/templates/populations/create-population.html index c0c4f45..007b6bf 100644 --- a/uploader/templates/populations/create-population.html +++ b/uploader/templates/populations/create-population.html @@ -154,24 +154,35 @@ {%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> + <label for="txt-population-family" class="form-label">Family</label> + <input type="text" + id="txt-population-family" + name="population_family" + class="form-control" + list="families-list" /> + <datalist id="families-list"> {%for family in families%} - <option value="{{family}}" - {%if error_values.population_family == family%} - selected="selected" - {%endif%}>{{family}}</option> + <option value="{{family}}">{{family}}</option> {%endfor%} - </select> + </datalist> <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> + This is <strong>optional</strong> metadata. It is used to group + populations into "families" for presentation in the menus. + {%if families | length > 0%} + Examples of currently existing families are: + <ul> + {%for family in families[0:7]%} + <li>{{family}}</li> + {%endfor%} + <li>etc.</li> + </ul> + {%endif%} + + You can + {%if families|length>0%} select from existing families, or {%endif%} + create a new family by typing in the input box above. You can also + leave the family blank.</p> </small> </div> diff --git a/uploader/templates/populations/list-populations.html b/uploader/templates/populations/list-populations.html index f780e94..a092e34 100644 --- a/uploader/templates/populations/list-populations.html +++ b/uploader/templates/populations/list-populations.html @@ -54,7 +54,7 @@ <th></th> <th>Name</th> <th>Full Name</th> - <th>Description</th> + <th>Information</th> </tr> </thead> @@ -71,7 +71,10 @@ </a> </td> <td>{{population.FullName}}</td> - <td>{{population.Description}}</td> + <td><a href="https://info.genenetwork.org/species/source.php?SpeciesName={{species.Name}}&InbredSetName={{population.Name}}" + title="Link to detailed information on this population." + class="btn btn-info" + target="_blank">info</a></td> </tr> {%else%} <tr> diff --git a/uploader/templates/populations/macro-display-population-card.html b/uploader/templates/populations/macro-display-population-card.html index 16b477f..6b5f1e0 100644 --- a/uploader/templates/populations/macro-display-population-card.html +++ b/uploader/templates/populations/macro-display-population-card.html @@ -1,4 +1,4 @@ -{%from "species/macro-display-species-card.html" import display_species_card%} +{%from "species/macro-display-species-card.html" import display_species_card,display_sui_species_card%} {%macro display_population_card(species, population)%} {{display_species_card(species)}} @@ -39,3 +39,40 @@ </div> </div> {%endmacro%} + + +{%macro display_sui_population_card(species, population)%} +{{display_sui_species_card(species)}} + +<div class="row"> + <table class="table"> + <caption>Current population</caption> + <tbody> + <tr> + <th>Name</th> + <td>{{population.Name}}</td> + </tr> + + <tr> + <th>Full Name</th> + <td>{{population.FullName}}</td> + </tr> + + <tr> + <th>Code</th> + <td>{{population.InbredSetCode}}</td> + </tr> + + <tr> + <th>Genetic Type</th> + <td>{{population.GeneticType}}</td> + </tr> + + <tr> + <th>Family</th> + <td>{{population.Family}}</td> + </tr> + </tbody> + </table> +</div> +{%endmacro%} diff --git a/uploader/templates/populations/sui-base.html b/uploader/templates/populations/sui-base.html new file mode 100644 index 0000000..0ca5c59 --- /dev/null +++ b/uploader/templates/populations/sui-base.html @@ -0,0 +1,12 @@ +{%extends "species/sui-base.html"%} + +{%block breadcrumbs%} +{{super()}} +<li class="breadcrumb-item"> + <a href="{{url_for('species.populations.view_population', + species_id=species['SpeciesId'], + population_id=population['Id'])}}"> + {{population["Name"]}} + </a> +</li> +{%endblock%} diff --git a/uploader/templates/populations/sui-view-population.html b/uploader/templates/populations/sui-view-population.html new file mode 100644 index 0000000..6244f4d --- /dev/null +++ b/uploader/templates/populations/sui-view-population.html @@ -0,0 +1,267 @@ +{%extends "populations/sui-base.html"%} +{%from "macro-step-indicator.html" import step_indicator%} +{%from "populations/macro-display-population-card.html" import display_sui_population_card%} + +{%block contents%} +<div class="row"> + <h2 class="heading">{{population.FullName}} ({{population.Name}})</h2> +</div> + +<div class="row"> + <ul class="nav nav-tabs" id="population-actions"> + <li class="nav-item presentation"> + <button class="nav-link" + id="samples-tab" + data-bs-toggle="tab" + data-bs-target="#samples-content" + type="button" + role="tab" + aria-controls="samples-content" + aria-selected="true">Samples</button></li> + <li class="nav-item presentation"> + <button class="nav-link active" + id="phenotypes-tab" + data-bs-toggle="tab" + data-bs-target="#phenotypes-content" + type="button" + role="tab" + aria-controls="phenotypes-content" + aria-selected="false">Phenotypes</button></li> + {%if view_under_construction%} + <li class="nav-item presentation"> + <button class="nav-link" + id="genotypes-tab" + data-bs-toggle="tab" + data-bs-target="#genotypes-content" + type="button" + role="tab" + aria-controls="genotypes-content" + aria-selected="false">Genotypes</button></li> + <li class="nav-item presentation"> + <button class="nav-link" + id="expression-data-tab" + data-bs-toggle="tab" + data-bs-target="#expression-data-content" + type="button" + role="tab" + aria-controls="expression-data-content" + aria-selected="false">Expression-Data</button></li> + {%endif%} + </ul> +</div> + +<div class="row"> + <div class="tab-content" id="populations-tabs-content"> + <div class="tab-pane fade" + id="samples-content" + role="tabpanel" + aria-labelledby="samples-content-tab"> + <p>Think of a <strong>"sample"</strong> as say a single case or individual + in the experiment. It could even be a single strain (where applicable). + </p> + <p>This is a convenience feature for when you want to upload phenotypes to + the system, but do not have the genotypes data ready yet.</p> + <a href="{{url_for('species.populations.samples.list_samples', + species_id=species.SpeciesId, + population_id=population.Id)}}" + title="View and upload samples for population '{{population['Name']}}'" + class="btn btn-primary">Manage Samples</a> + </div> + + <div class="tab-pane fade show active" + id="phenotypes-content" + role="tabpanel" + aria-labelledby="phenotypes-content-tab"> + + <div class="row" style="margin-top: 0.3em;"> + <div class="col"> + <a href="{{url_for('species.populations.phenotypes.add_phenotypes', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id)}}" + title="Upload phenotype data for population '{{population['Name']}}'" + class="btn btn-primary">Upload new Phenotypes</a> + </div> + <div class="col"> + <a href="#" + title="List all existing publications for this population." + class="btn btn-primary not-implemented">view publications</a> + <!-- Maybe, actually filter publications by population? --> + <!-- Provide other features for publications on loaded page. --> + </div> + </div> + + <div class="row" style="margin-top: 1em;"> + <h3> Phenotypes in Population "{{population.FullName}} ({{population.Name}})"</h3> + + <p>The table below lists the phenotypes that already exist for + population "<em>{{population.FullName}} ({{population.Name}})</em>" of + species "<em>{{species.FullName}} ({{species.Name}})</em>".</p> + + <div class="row phenotypes-list-actions"> + <div class="col"> + <form id="frm-recompute-phenotype-means" + method="POST" + action="{{url_for( + 'species.populations.phenotypes.recompute_means', + species_id=species['SpeciesId'], + population_id=population['Id'], + dataset_id=dataset['Id'])}}"> + <input id="submit-frm-recompute-phenotype-means" + class="btn btn-info" + type="submit" + title="Compute/Recompute the means for selected phenotypes (or all phenotypes if none selected)." + value="(Rec/C)ompute means" /> + </form> + </div> + <div class="col"> + <form id="frm-rerun-qtlreaper" + method="POST" + action="{{url_for( + 'species.populations.phenotypes.rerun_qtlreaper', + species_id=species['SpeciesId'], + population_id=population['Id'], + dataset_id=dataset['Id'])}}"> + <input id="submit-frm-rerun-qtlreaper" + class="btn btn-info" + type="submit" + title="Run/Rerun QTLReaper for selected phenotypes (or all phenotypes if none selected)." + value="(rer/r)un QTLReaper" /> + </form> + </div> + </div> + + <table id="tbl-phenotypes-list" class="table compact stripe cell-border"> + <thead> + <tr> + <th></th> + <th>Index</th> + <th>Record</th> + <th>Description</th> + </tr> + </thead> + + <tbody></tbody> + </table> + </div> + </div> + + <div class="tab-pane fade" + id="genotypes-content" + role="tabpanel" + aria-labelledby="genotypes-content-tab"> + <p>This allows you to upload the data that concerns your genotypes.</p> + <p>Any samples/individuals/cases/strains that do not already exist in the + system will be added. This does not delete any existing data.</p> + <a href="{{url_for('species.populations.genotypes.list_genotypes', + species_id=species.SpeciesId, + population_id=population.Id)}}" + title="Upload genotype information for the '{{population.FullName}}' population of the '{{species.FullName}}' species." + class="btn btn-primary">upload genotypes</a> + </div> + <div class="tab-pane fade" id="expression-data-content" role="tabpanel" aria-labelledby="expression-data-content-tab"> + <p>Upload expression data (mRNA data) for this population.</p> + <a href="#" title="" class="btn btn-primary">upload genotypes</a> + </div> + </div> +</div> +{%endblock%} + +{%block sidebarcontents%} +<div class="row"> + <p>Each tab presents a feature that's available at the population level. + Select the tab that allows you to continue with your task.</p> +</div> +{{display_sui_population_card(species, population)}} +{%endblock%} + + + + +{%block javascript%} +<script type="text/javascript" src="/static/js/urls.js"></script> + +<script type="text/javascript"> + $(function() { + /** JS to build list of phenotypes table. **/ + var species_id = {{species.SpeciesId}}; + var population_id = {{population.Id}}; + var dataset_id = {{dataset.Id}}; + var dataset_name = "{{dataset.Name}}"; + var data = {{phenotypes | tojson}}; + + var dtPhenotypesList = buildDataTable( + "#tbl-phenotypes-list", + data, + [ + { + data: function(pheno) { + return `<input type="checkbox" name="selected-phenotypes" ` + + `id="chk-selected-phenotypes-${pheno.InbredSetCode}_${pheno.xref_id}" ` + + `value="${pheno.InbredSetCode}_${pheno.xref_id}" ` + + `class="chk-row-select" />` + } + }, + {data: "sequence_number"}, + { + data: function(pheno, type, set, meta) { + var spcs_id = {{species.SpeciesId}}; + var pop_id = {{population.Id}}; + var dtst_id = {{dataset.Id}}; + var url = buildURLFromCurrentURL( + (`/species/${spcs_id}` + + `/populations/${pop_id}` + + `/phenotypes/datasets/${dtst_id}` + + `/phenotype/${pheno.xref_id}`)); + return `<a href="${url.toString()}" target="_blank">` + + `${pheno.InbredSetCode}_${pheno.xref_id}` + + `</a>`; + } + }, + { + data: function(pheno) { + return (pheno.Post_publication_description || + pheno.Original_description || + pheno.Pre_publication_description); + } + } + ], + { + select: "multi+shift", + layout: { + top1Start: { + pageLength: { + text: "Show _MENU_ of _TOTAL_" + } + }, + topStart: "info", + top1End: null + }, + rowId: function(pheno) { + return `${pheno.InbredSetCode}_${pheno.xref_id}`; + } + }); + + + $("#submit-frm-rerun-qtlreaper").on( + "click", + function(event) { + // (Re)run the QTLReaper script for selected phenotypes. + event.preventDefault(); + var form = $("#frm-rerun-qtlreaper"); + form.find(".dynamically-added-element").remove(); + dtPhenotypesList.rows({selected: true}).nodes().each((node, index) => { + _cloned = $(node).find(".chk-row-select").clone(); + _cloned.removeAttr("id"); + _cloned.removeAttr("class"); + _cloned.attr("style", "display: none;"); + _cloned.attr("data-type", "dynamically-added-element"); + _cloned.attr("class", "dynamically-added-element checkbox"); + _cloned.prop("checked", true); + form.append(_cloned); + }); + form.submit(); + }); + }); +</script> +{%endblock%} diff --git a/uploader/templates/populations/view-population.html b/uploader/templates/populations/view-population.html index b23caeb..3b9661b 100644 --- a/uploader/templates/populations/view-population.html +++ b/uploader/templates/populations/view-population.html @@ -42,8 +42,10 @@ <dt>Family</dt> <dd>{{population.Family}}</dd> - <dt>Description</dt> - <dd><pre>{{population.Description or "-"}}</pre></dd> + <dt>Information</dt> + <dd><a href="https://info.genenetwork.org/species/source.php?SpeciesName={{species.Name}}&InbredSetName={{population.Name}}" + title="Link to detailed information on this population." + target="_blank">Population Information</a></dd> </dl> </div> diff --git a/uploader/templates/publications/delete-publication-success.html b/uploader/templates/publications/delete-publication-success.html new file mode 100644 index 0000000..53a44ec --- /dev/null +++ b/uploader/templates/publications/delete-publication-success.html @@ -0,0 +1,18 @@ +{%extends "publications/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}View Publication{%endblock%} + +{%block pagetitle%}View Publication{%endblock%} + + +{%block contents%} +{{flash_all_messages()}} +{%endblock%} + + +{%block javascript%} +<script type="text/javascript"> + $(function() {}); +</script> +{%endblock%} diff --git a/uploader/templates/publications/delete-publication.html b/uploader/templates/publications/delete-publication.html new file mode 100644 index 0000000..0ac93ec --- /dev/null +++ b/uploader/templates/publications/delete-publication.html @@ -0,0 +1,88 @@ +{%extends "publications/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}View Publication{%endblock%} + +{%block pagetitle%}View Publication{%endblock%} + + +{%block contents%} +{{flash_all_messages()}} +<div class="row"> + <p>You are about to delete the publication with the following details:</p> +</div> + +<div class="row"> + <table class="table"> + <tr> + <th>Linked Phenotypes</th> + <td>{{linked_phenotypes | count}}</td> + </tr> + <tr> + <th>PubMed</th> + <td> + {%if publication.PubMed_ID%} + <a href="https://pubmed.ncbi.nlm.nih.gov/{{publication.PubMed_ID}}/" + target="_blank">{{publication.PubMed_ID}}</a> + {%else%} + — + {%endif%} + </td> + </tr> + <tr> + <th>Title</th> + <td>{{publication.Title or "—"}}</td> + </tr> + <tr> + <th>Authors</th> + <td>{{publication.Authors or "—"}}</td> + </tr> + <tr> + <th>Journal</th> + <td>{{publication.Journal or "—"}}</td> + </tr> + <tr> + <th>Published</th> + <td>{{publication.Month or ""}} {{publication.Year or "—"}}</td> + </tr> + <tr> + <th>Volume</th> + <td>{{publication.Volume or "—"}}</td> + </tr> + <tr> + <th>Pages</th> + <td>{{publication.Pages or "—"}}</td> + </tr> + <tr> + <th>Abstract</th> + <td> + {%for line in (publication.Abstract or "—").replace("\r\n", "<br />").replace("\n", "<br />").split("<br />")%} + <p>{{line}}</p> + {%endfor%} + </td> + </tr> + </table> +</div> + +<div class="row"> + <p>If you are sure that is what you want, click the button below to delete the + publication</p> + <p class="form-text text-small"> + <small>You will not be able to recover the data if you click + delete below.</small></p> + + <form action="{{url_for('publications.delete_publication', publication_id=publication_id)}}" + method="POST"> + <div class="form-group"> + <input type="submit" value="delete" class="btn btn-danger" /> + </div> + </form> +</div> +{%endblock%} + + +{%block javascript%} +<script type="text/javascript"> + $(function() {}); +</script> +{%endblock%} diff --git a/uploader/templates/publications/edit-publication.html b/uploader/templates/publications/edit-publication.html new file mode 100644 index 0000000..97fa134 --- /dev/null +++ b/uploader/templates/publications/edit-publication.html @@ -0,0 +1,196 @@ +{%extends "publications/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} + +{%block title%}View Publication{%endblock%} + +{%block pagetitle%}View Publication{%endblock%} + + +{%block contents%} +{{flash_all_messages()}} + +<div class="row"> + <form id="frm-create-publication" + method="POST" + action="{{url_for('publications.edit_publication', + publication_id=publication_id, + next=request.args.get('next', ''))}}" + class="form-horizontal"> + + <div class="row mb-3"> + <label for="txt-pubmed-id" class="col-sm-2 col-form-label"> + PubMed ID</label> + <div class="col-sm-10"> + <div class="input-group"> + <input type="text" + id="txt-pubmed-id" + name="pubmed-id" + value="{{publication.PubMed_ID or ''}}" + class="form-control" /> + <div class="input-group-text"> + <button class="btn btn-outline-primary" + id="btn-search-pubmed-id">search</button> + </div> + </div> + <span id="search-pubmed-id-error" + class="form-text text-muted text-danger visually-hidden"> + </span> + <span class="form-text text-muted">This is the publication's ID on + <a href="https://pubmed.ncbi.nlm.nih.gov/" + title="Link to NCBI's PubMed service">NCBI's Pubmed Service</a> + </span> + </div> + </div> + + <div class="row mb-3"> + <label for="txt-publication-title" class="col-sm-2 col-form-label"> + Title</label> + <div class="col-sm-10"> + <input type="text" + id="txt-publication-title" + name="publication-title" + value="{{publication.Title}}" + class="form-control" /> + <span class="form-text text-muted">Provide the publication's title here.</span> + </div> + </div> + + <div class="row mb-3"> + <label for="txt-publication-authors" class="col-sm-2 col-form-label"> + Authors</label> + <div class="col-sm-10"> + <input type="text" + id="txt-publication-authors" + name="publication-authors" + value="{{publication.Authors}}" + required="required" + class="form-control" /> + <span class="form-text text-muted"> + A publication <strong>MUST</strong> have an author. You <em>must</em> + provide a value for the authors field. + </span> + </div> + </div> + + <div class="row mb-3"> + <label for="txt-publication-journal" class="col-sm-2 col-form-label"> + Journal</label> + <div class="col-sm-10"> + <input type="text" + id="txt-publication-journal" + name="publication-journal" + value="{{publication.Journal}}" + class="form-control" /> + <span class="form-text text-muted">Provide the name journal where the + publication was done, here.</span> + </div> + </div> + + <div class="row mb-3"> + <label for="select-publication-month" + class="col-sm-2 col-form-label"> + Month</label> + <div class="col-sm-4"> + <select class="form-control" + id="select-publication-month" + name="publication-month"> + <option value="">Select a month</option> + {%for month in ("january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"):%} + <option value="{{month}}" + {%if publication.Month | lower == month %} + selected="selected" + {%endif%}> + {{month | title}} + </option> + {%endfor%} + </select> + <span class="form-text text-muted">Month of publication</span> + </div> + + <label for="txt-publication-year" + class="col-sm-2 col-form-label"> + Year</label> + <div class="col-sm-4"> + <input type="number" + id="txt-publication-year" + name="publication-year" + value="{{publication.Year}}" + class="form-control" + min="1960" /> + <span class="form-text text-muted">Year of publication</span> + </div> + </div> + + <div class="row mb-3"> + <label for="txt-publication-volume" + class="col-sm-2 col-form-label"> + Volume</label> + <div class="col-sm-4"> + <input type="text" + id="txt-publication-volume" + name="publication-volume" + value="{{publication.Volume}}" + class="form-control"> + <span class="form-text text-muted">Journal volume</span> + </div> + + <label for="txt-publication-pages" + class="col-sm-2 col-form-label"> + Pages</label> + <div class="col-sm-4"> + <input type="text" + id="txt-publication-pages" + name="publication-pages" + value="{{publication.Pages}}" + class="form-control" /> + <span class="form-text text-muted">Journal pages for the publication</span> + </div> + </div> + + <div class="row mb-3"> + <label for="txt-abstract" class="col-sm-2 col-form-label">Abstract</label> + <div class="col-sm-10"> + <textarea id="txt-publication-abstract" + name="publication-abstract" + class="form-control" + rows="7">{{publication.Abstract or ""}}</textarea> + </div> + </div> + + <div class="row mb-3"> + <div class="col-sm-2"></div> + <div class="col-sm-8"> + <input type="submit" class="btn btn-primary" value="Save" /> + <input type="reset" class="btn btn-danger" /> + </div> + </div> + +</form> +</div> + +{%endblock%} + + +{%block javascript%} +<script type="text/javascript" src="/static/js/pubmed.js"></script> +<script type="text/javascript"> + $(function() { + $("#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> +{%endblock%} diff --git a/uploader/templates/publications/index.html b/uploader/templates/publications/index.html index f846d54..369812b 100644 --- a/uploader/templates/publications/index.html +++ b/uploader/templates/publications/index.html @@ -41,6 +41,7 @@ [ {data: "index"}, { + searchable: true, data: (pub) => { if(pub.PubMed_ID) { return `<a href="https://pubmed.ncbi.nlm.nih.gov/` + @@ -52,6 +53,7 @@ } }, { + searchable: true, data: (pub) => { var title = "⸻"; if(pub.Title) { @@ -64,6 +66,7 @@ } }, { + searchable: true, data: (pub) => { authors = pub.Authors.split(",").map( (item) => {return item.trim();}); @@ -75,16 +78,21 @@ } ], { + serverSide: true, ajax: { url: "/publications/list", dataSrc: "publications" }, scrollY: 700, - paging: false, + scroller: true, + scrollCollapse: true, + paging: true, deferRender: true, layout: { topStart: "info", - topEnd: "search" + topEnd: "search", + bottomStart: "pageLength", + bottomEnd: false } }); }); diff --git a/uploader/templates/publications/view-publication.html b/uploader/templates/publications/view-publication.html index 388547a..0bd7bc5 100644 --- a/uploader/templates/publications/view-publication.html +++ b/uploader/templates/publications/view-publication.html @@ -12,6 +12,10 @@ <div class="row"> <table class="table"> <tr> + <th>Linked Phenotypes</th> + <td>{{linked_phenotypes | count}}</td> + </tr> + <tr> <th>PubMed</th> <td> {%if publication.PubMed_ID%} @@ -58,15 +62,15 @@ </div> <div class="row"> - <form id="frm-edit-delete-publication" method="POST" action="#"> - <input type="hidden" name="publication_id" value="{{publication.Id}}" /> - <div class="form-group"> - <input type="submit" value="edit" class="btn btn-primary not-implemented" /> - {%if linked_phenotypes | length == 0%} - <input type="submit" value="delete" class="btn btn-danger not-implemented" /> - {%endif%} - </div> - </form> + <div> + <a href="{{url_for('publications.edit_publication', publication_id=publication.Id)}}" + title="Edit details for this publication." + class="btn btn-primary">Edit</a> + {%if linked_phenotypes | length == 0%} + <a href="{{url_for('publications.delete_publication', publication_id=publication.Id)}}" + class="btn btn-danger">delete</a> + {%endif%} + </div> </div> {%endblock%} diff --git a/uploader/templates/samples/list-samples.html b/uploader/templates/samples/list-samples.html index 185e784..aed27c3 100644 --- a/uploader/templates/samples/list-samples.html +++ b/uploader/templates/samples/list-samples.html @@ -29,6 +29,19 @@ </p> </div> +<div class="row"> + <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> + {%if samples | length > 0%} <div class="row"> <p> @@ -96,32 +109,17 @@ <p> <a href="#" - title="Add samples for population '{{population.FullName}}' from species + title="Delete samples from population '{{population.FullName}}' from species '{{species.FullName}}'." - class="btn btn-danger"> + class="btn btn-danger not-implemented"> 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> + <p>There are no samples entered for this population. Click the "Add Samples" + button above, to add some new samples.</p> </div> {%endif%} diff --git a/uploader/templates/samples/sui-base.html b/uploader/templates/samples/sui-base.html new file mode 100644 index 0000000..ee08e2e --- /dev/null +++ b/uploader/templates/samples/sui-base.html @@ -0,0 +1,19 @@ +{%extends "populations/sui-base.html"%} +{%from "populations/macro-display-population-card.html" import display_sui_population_card%} + +{%block breadcrumbs%} +{{super()}} +<li class="breadcrumb-item">Manage Samples</li> +{%endblock%} + +{%block contents%} +<div class="row"> + <h2 class="heading">{{population.FullName}} ({{population.Name}})</h2> +</div> +{%endblock%} + + + +{%block sidebarcontents%} +{{display_sui_population_card(species, population)}} +{%endblock%} diff --git a/uploader/templates/samples/sui-list-samples.html b/uploader/templates/samples/sui-list-samples.html new file mode 100644 index 0000000..e9ed71a --- /dev/null +++ b/uploader/templates/samples/sui-list-samples.html @@ -0,0 +1,98 @@ +{%extends "samples/sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "populations/macro-select-population.html" import select_population_form%} + +{%block title%}Samples — List Samples{%endblock%} + +{%block contents%} +{{super()}} + +<div class="row"> + <h3 class="subheading">manage samples</h3> + {{flash_all_messages()}} +</div> + +<div class="row"> + <div class="col"> + <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 new samples</a> + </div> +</div> + +{%if samples | length > 0%} +<div class="row"> + <p> + Population "{{population.FullName}} ({{population.Name}})" already has + <strong>{{total_samples}}</strong> samples/individuals entered. You can + explore the list of samples 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}} — {{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> +</div> +{%else%} +<div class="row"> + <p>There are no samples entered for this population. Click the "Add Samples" + button above, to add some new samples.</p> +</div> +{%endif%} + +{%endblock%} diff --git a/uploader/templates/samples/upload-samples.html b/uploader/templates/samples/upload-samples.html index 25d3290..6422094 100644 --- a/uploader/templates/samples/upload-samples.html +++ b/uploader/templates/samples/upload-samples.html @@ -66,7 +66,7 @@ <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" + accept="text/csv, text/tab-separated-values, text/plain" class="form-control" /> </div> diff --git a/uploader/templates/species/macro-display-species-card.html b/uploader/templates/species/macro-display-species-card.html index 166c7b9..30c564f 100644 --- a/uploader/templates/species/macro-display-species-card.html +++ b/uploader/templates/species/macro-display-species-card.html @@ -20,3 +20,32 @@ </div> </div> {%endmacro%} + + +{%macro display_sui_species_card(species)%} +<div class="row"> + <table class="table"> + <caption>Current Species</caption> + <tbody> + <tr> + <th>Name</th> + <td>{{species["Name"] | title}}</td> + </tr> + <tr> + <th>Scientific</th> + <td>{{species["FullName"]}}</td> + </tr> + {%if species["TaxonomyId"]%} + <tr> + <th>Taxonomy ID</th> + <td> + <a href="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={{species.TaxonomyId}}" + title="NCBI's Taxonomy Browser page for {{species.Name}}"> + {{species.TaxonomyId}}</a> + </td> + </tr> + </tbody> + {%endif%} + </table> +</div> +{%endmacro%} diff --git a/uploader/templates/species/sui-base.html b/uploader/templates/species/sui-base.html new file mode 100644 index 0000000..f7b4fef --- /dev/null +++ b/uploader/templates/species/sui-base.html @@ -0,0 +1,10 @@ +{%extends "sui-base.html"%} + +{%block breadcrumbs%} +{{super()}} +<li class="breadcrumb-item"> + <a href="{{url_for('species.view_species', species_id=species['SpeciesId'])}}"> + {{species["Name"]|title}} + </a> +</li> +{%endblock%} diff --git a/uploader/templates/species/sui-view-species.html b/uploader/templates/species/sui-view-species.html new file mode 100644 index 0000000..4b6402e --- /dev/null +++ b/uploader/templates/species/sui-view-species.html @@ -0,0 +1,127 @@ +{%extends "species/sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-forms.html" import add_http_feature_flags%} +{%from "macro-step-indicator.html" import step_indicator%} +{%from "species/macro-display-species-card.html" import display_sui_species_card%} + +{%block title%}View Species{%endblock%} + +{%macro add_form_buttons()%} +<div class="row form-buttons"> + <div class="col"> + <input type="submit" + value="use selected population" + class="btn btn-primary" /> + </div> + + <div class="col"> + <a href="url_for('species.population.create_population', + species_id=species.SpeciesId, + return_to='species.view_species')" + title="Create a new population for species '{{species.Name}}'." + class="btn btn-outline-info"> + Create a new population + </a> + </div> +</div> +{%endmacro%} + + +{%block contents%} +<div class="row"> + <h2 class="heading">{{species.FullName}} ({{species.Name}})</h2> +</div> + +<div class "row"> + <ul class="nav nav-tabs" id="species-actions"> + <li class="nav-item presentation"> + <button class="nav-link active" + id="populations-tab" + data-bs-toggle="tab" + data-bs-target="#populations-content" + type="button" + role="tab" + aria-controls="populations-content" + aria-selected="true">Populations</button> + </li> + <li class="nav-item presentation"> + <button class="nav-link" + id="sequencing-platforms-tab" + data-bs-toggle="tab" + data-bs-target="#sequencing-platforms-content" + type="button" + role="tab" + aria-controls="sequencing-platforms-content" + aria-selected="true">Sequencing Platforms</button> + </li> + </ul> +</div> + +<div class="row"> + <div class="tab-content" id="species-tabs-content"> + <div class="tab-pane fade show active" + id="populations-content" + role="tabpanel" + aria-labelledby="populations-content-tab"> + <p>Data belonging to a particular species is further divided into one or more + populations for easier handling. Please select the population you want to work + with.</p> + + <form method="GET" + action="{{url_for('species.view_species', species_id=species.SpeciesId)}}" + class="form-horizontal"> + {{add_http_feature_flags()}} + {{add_form_buttons()}} + + {%if populations | length != 0%} + <div style="margin-top:0.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']}}).</p> + {%endif%} + + {{add_form_buttons()}} + + </form> + </div> + <div class="tab-pane fade" + id="sequencing-platforms-content" + role="tabpanel" + aria-labelledby="sequencing-platforms-content-tab"> + <p>Upload and manage the sequencing platforms for species + '{{species.Name | title}} ({{species.FullName}})' + <a href="{{url_for('species.platforms.list_platforms', + species_id=species.SpeciesId)}}" + title="Manage sequencing platforms for {{species.Name}}">here</a>. + </p> + </div> + </div> +</div> +{%endblock%} + +{%block sidebarcontents%} +<div class="row"> + <p>You can manage species' populations and sequencing platforms here. Select + the tab for the feature you wish to continue working on.</p> +</div> +{{display_sui_species_card(species)}} +{%endblock%} + + +{%block javascript%} +<script type="text/javascript" src="/static/js/populations.js"></script> +{%endblock%} diff --git a/uploader/templates/sui-base.html b/uploader/templates/sui-base.html new file mode 100644 index 0000000..719a646 --- /dev/null +++ b/uploader/templates/sui-base.html @@ -0,0 +1,103 @@ +<!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.datatables', + filename='css/dataTables.bootstrap5.min.css')}}" /> + <link rel="stylesheet" type="text/css" href="/static/css/layout-common.css" /> + <link rel="stylesheet" type="text/css" href="/static/css/layout-large.css" /> + <link rel="stylesheet" type="text/css" href="/static/css/layout-medium.css" /> + <link rel="stylesheet" type="text/css" href="/static/css/layout-small.css" /> + <link rel="stylesheet" type="text/css" href="/static/css/theme.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> + {{user_email()}} Sign Out</a> + {%else%} + <a href="{{authserver_authorise_uri()}}" + title="Log in to the system">Sign In</a> + {%endif%} + </li> + </ul> + </nav> + </header> + + + <main id="main" class="main"> + <nav id="breadcrumbs" aria-label="breadcrumb"> + <ol class="breadcrumb"> + {%block breadcrumbs%} + <li class="breadcrumb-item"> + <a href="{{url_for('base.index')}}">Home</a></li> + {%endblock%} + </ol> + </nav> + + <div id="main-content"> + {%block contents%}{%endblock%} + </div> + + <div id="sidebar-content"> + {%block sidebarcontents%}{%endblock%} + </div> + </main> + + + + <script type="text/javascript" src="/static/js/debug.js"></script> + <!-- + Core dependencies + --> + <script src="{{url_for('base.jquery', + filename='jquery.min.js')}}"></script> + <script src="{{url_for('base.bootstrap', + filename='js/bootstrap.min.js')}}"></script> + + <!-- + DataTables dependencies + --> + <script type="text/javascript" + src="{{url_for('base.datatables', + filename='js/dataTables.min.js')}}"></script> + <script type="text/javascript" + src="{{url_for('base.datatables_extensions', + filename='scroller/js/dataTables.scroller.min.js')}}"></script> + <script type="text/javascript" + src="{{url_for('base.datatables_extensions', + filename='buttons/js/dataTables.buttons.min.js')}}"></script> + <script type="text/javascript" + src="{{url_for('base.datatables_extensions', + filename='select/js/dataTables.select.min.js')}}"></script> + + <!-- + local dependencies + --> + <script type="text/javascript" src="/static/js/utils.js"></script> + <script type="text/javascript" src="/static/js/datatables.js"></script> + {%block javascript%}{%endblock%} + </body> +</html> diff --git a/uploader/templates/sui-index.html b/uploader/templates/sui-index.html new file mode 100644 index 0000000..888823f --- /dev/null +++ b/uploader/templates/sui-index.html @@ -0,0 +1,123 @@ +{%extends "sui-base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "macro-forms.html" import add_http_feature_flags%} +{%from "macro-step-indicator.html" import step_indicator%} + +{%block title%}Home{%endblock%} + +{%block pagetitle%}Home{%endblock%} + +{%block extra_breadcrumbs%}{%endblock%} + +{%block contents%} + +{%macro add_form_buttons()%} +<div class="row form-buttons"> + <div class="col"> + <input type="submit" + class="btn btn-primary" + value="use selected species" /> + </div> + <div class="col"> + <a href="{{url_for('species.create_species', return_to='base.index')}}" + class="btn btn-outline-primary" + title="Create a new species.">Create a new Species</a> + </div> +</div> +{%endmacro%} + +<div class="row">{{flash_all_messages()}}</div> + +{%if user_logged_in()%} + +<div class="row"> + <div class="row"> + <h2 class="heading">Species</h2> + + <p>Select the species you want to work with.</p> + </div> +</div> + +<div class="row"> + <form method="GET" action="{{url_for('base.index')}}" class="form-horizontal"> + {{add_http_feature_flags()}} + + {{add_form_buttons()}} + + {%if species | length != 0%} + <div style="margin-top:1em;"> + <table id="tbl-select-species" class="table compact stripe" + data-species-list='{{species | tojson}}'> + <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%} + + {{add_form_buttons()}} + + </form> +</div> + +{%else%} + +<div class="row"> + <p>The Genenetwork Uploader (<em>gn-uploader</em>) enables upload of new data + into the Genenetwork System. It provides Quality Control over data, and + guidance in case you data does not meet the standards for acceptance.</p> + <p> + <a href="{{authserver_authorise_uri()}}" + title="Sign in to the system" + class="btn btn-primary">Sign in</a> + to get started.</p> +</div> +{%endif%} + +{%endblock%} + + + +{%block sidebarcontents%} +<div class="row"> + <p>The data in Genenetwork is related to one species or another. Use the form + provided to select from existing species, or click on the + "Create a New Species" button if you cannot find the species you want to + work with.</p> +</div> +<div class="row"> + <form id="frm-quick-navigation"> + <legend>Quick Navigation</legend> + <div class="form-group"> + <label for="fqn-species-id">Species</label> + <select name="species_id"> + <option value="">Select species</option> + </select> + </div> + </form> +</div> +{%endblock%} + + +{%block javascript%} +<script type="text/javascript" src="/static/js/species.js"></script> +{%endblock%} |
