aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--uploader/__init__.py2
-rw-r--r--uploader/base_routes.py5
-rw-r--r--uploader/datautils.py17
-rw-r--r--uploader/genotypes/__init__.py1
-rw-r--r--uploader/genotypes/models.py41
-rw-r--r--uploader/genotypes/views.py127
-rw-r--r--uploader/oauth2/client.py2
-rw-r--r--uploader/population/models.py57
-rw-r--r--uploader/population/views.py57
-rw-r--r--uploader/samples/views.py18
-rw-r--r--uploader/session.py2
-rw-r--r--uploader/species/models.py28
-rw-r--r--uploader/species/views.py5
-rw-r--r--uploader/static/css/styles.css7
-rw-r--r--uploader/templates/base.html6
-rw-r--r--uploader/templates/genotypes/base.html12
-rw-r--r--uploader/templates/genotypes/index.html28
-rw-r--r--uploader/templates/genotypes/list-genotypes.html119
-rw-r--r--uploader/templates/genotypes/list-markers.html102
-rw-r--r--uploader/templates/genotypes/select-population.html31
-rw-r--r--uploader/templates/index.html80
-rw-r--r--uploader/templates/login.html2
-rw-r--r--uploader/templates/populations/create-population.html32
-rw-r--r--uploader/templates/populations/index.html8
-rw-r--r--uploader/templates/populations/list-populations.html2
-rw-r--r--uploader/templates/populations/view-population.html9
-rw-r--r--uploader/templates/samples/index.html6
-rw-r--r--uploader/templates/samples/list-samples.html91
-rw-r--r--uploader/templates/samples/select-population.html17
-rw-r--r--uploader/templates/samples/upload-samples.html7
-rw-r--r--uploader/templates/species/list-species.html30
-rw-r--r--uploader/templates/species/macro-select-species.html2
-rw-r--r--uploader/templates/species/view-species.html5
33 files changed, 821 insertions, 137 deletions
diff --git a/uploader/__init__.py b/uploader/__init__.py
index 347f170..1af159b 100644
--- a/uploader/__init__.py
+++ b/uploader/__init__.py
@@ -9,6 +9,7 @@ from flask_session import Session
from uploader.oauth2.client import user_logged_in, authserver_authorise_uri
+from . import session
from .base_routes import base
from .species import speciesbp
from .dbinsert import dbinsertbp
@@ -76,6 +77,7 @@ def create_app():
app.add_template_global(lambda: app.config["GN2_SERVER_URL"],
name="gn2server_uri")
app.add_template_global(user_logged_in)
+ app.add_template_global(lambda : session.user_details()["email"], name="user_email")
Session(app)
diff --git a/uploader/base_routes.py b/uploader/base_routes.py
index 88247b2..a20b7ff 100644
--- a/uploader/base_routes.py
+++ b/uploader/base_routes.py
@@ -1,5 +1,6 @@
"""Basic routes required for all pages"""
import os
+from urllib.parse import urljoin
from flask import (
Blueprint,
@@ -23,7 +24,9 @@ def favicon():
@base.route("/", methods=["GET"])
def index():
"""Load the landing page"""
- return render_template("index.html" if user_logged_in() else "login.html")
+ return render_template("index.html" if user_logged_in() else "login.html",
+ gn2server_intro=urljoin(app.config["GN2_SERVER_URL"],
+ "/intro"))
def appenv():
"""Get app's guix environment path."""
diff --git a/uploader/datautils.py b/uploader/datautils.py
index b95a9e0..2ee079d 100644
--- a/uploader/datautils.py
+++ b/uploader/datautils.py
@@ -1,7 +1,14 @@
"""Generic data utilities: Rename module."""
import math
+from typing import Sequence
from functools import reduce
+def enumerate_sequence(seq: Sequence[dict], start:int = 1) -> Sequence[dict]:
+ """Enumerate sequence beginning at 1"""
+ return tuple({**item, "sequence_number": seqno}
+ for seqno, item in enumerate(seq, start=start))
+
+
def order_by_family(items: tuple[dict, ...],
family_key: str = "Family",
order_key: str = "FamilyOrderId") -> list:
@@ -19,3 +26,13 @@ def order_by_family(items: tuple[dict, ...],
return sorted(tuple(reduce(__order__, items, {}).items()),
key=lambda item: item[0][0])
+
+
+def safe_int(val: str) -> int:
+ """
+ Convert val into an integer: if val cannot be converted, return a zero.
+ """
+ try:
+ return int(val)
+ except ValueError:
+ return 0
diff --git a/uploader/genotypes/__init__.py b/uploader/genotypes/__init__.py
new file mode 100644
index 0000000..d0025d6
--- /dev/null
+++ b/uploader/genotypes/__init__.py
@@ -0,0 +1 @@
+"""The Genotypes module."""
diff --git a/uploader/genotypes/models.py b/uploader/genotypes/models.py
new file mode 100644
index 0000000..53c5fb8
--- /dev/null
+++ b/uploader/genotypes/models.py
@@ -0,0 +1,41 @@
+"""Functions for handling genotypes."""
+from typing import Optional
+
+import MySQLdb as mdb
+from MySQLdb.cursors import DictCursor
+
+from uploader.db_utils import debug_query
+
+def genocode_by_population(
+ conn: mdb.Connection, population_id: int) -> tuple[dict, ...]:
+ """Get the allele/genotype codes."""
+ with conn.cursor(cursorclass=DictCursor) as cursor:
+ cursor.execute("SELECT * FROM GenoCode WHERE InbredSetId=%s",
+ (population_id,))
+ return tuple(dict(item) for item in cursor.fetchall())
+
+
+def genotype_markers_count(conn: mdb.Connection, species_id: int) -> int:
+ """Find the total count of the genotype markers for a species."""
+ with conn.cursor(cursorclass=DictCursor) as cursor:
+ cursor.execute(
+ "SELECT COUNT(Name) AS markers_count FROM Geno WHERE SpeciesId=%s",
+ (species_id,))
+ return int(cursor.fetchone()["markers_count"])
+
+
+def genotype_markers(
+ conn: mdb.Connection,
+ species_id: int,
+ offset: int = 0,
+ limit: Optional[int] = None
+) -> tuple[dict, ...]:
+ """Retrieve markers from the database."""
+ _query = "SELECT * FROM Geno WHERE SpeciesId=%s"
+ if bool(limit) and limit > 0:
+ _query = _query + f" LIMIT {limit} OFFSET {offset}"
+
+ with conn.cursor(cursorclass=DictCursor) as cursor:
+ cursor.execute(_query, (species_id,))
+ debug_query(cursor)
+ return tuple(dict(row) for row in cursor.fetchall())
diff --git a/uploader/genotypes/views.py b/uploader/genotypes/views.py
new file mode 100644
index 0000000..2ff9965
--- /dev/null
+++ b/uploader/genotypes/views.py
@@ -0,0 +1,127 @@
+"""Views for the genotypes."""
+from flask import (flash,
+ request,
+ url_for,
+ redirect,
+ Blueprint,
+ render_template,
+ current_app as app)
+
+from uploader.authorisation import require_login
+from uploader.db_utils import database_connection
+from uploader.species.models import all_species, species_by_id
+from uploader.datautils import safe_int, order_by_family, enumerate_sequence
+from uploader.population.models import (populations_by_species,
+ population_by_species_and_id)
+
+from .models import (genotype_markers,
+ genotype_markers_count,
+ genocode_by_population)
+
+genotypesbp = Blueprint("genotypes", __name__)
+
+@genotypesbp.route("populations/genotypes", methods=["GET"])
+@require_login
+def index():
+ """Direct entry-point for genotypes."""
+ with database_connection(app.config["SQL_URI"]) as conn:
+ if not bool(request.args.get("species_id")):
+ return render_template("genotypes/index.html",
+ species=order_by_family(all_species(conn)),
+ activelink="genotypes")
+ species = species_by_id(conn, request.args.get("species_id"))
+ if not bool(species):
+ flash(f"Could not find species with ID '{request.args.get('species_id')}'!",
+ "alert-danger")
+ return redirect(url_for("species.populations.genotypes.index"))
+ return redirect(url_for("species.populations.genotypes.select_population",
+ species_id=species["SpeciesId"]))
+
+
+@genotypesbp.route("/<int:species_id>/populations/genotypes/select-population",
+ methods=["GET"])
+@require_login
+def select_population(species_id: int):
+ """Select the population under which the genotypes go."""
+ with database_connection(app.config["SQL_URI"]) as conn:
+ species = species_by_id(conn, species_id)
+ if not bool(species):
+ flash("Invalid species provided!", "alert-danger")
+ return redirect(url_for("species.populations.genotypes.index"))
+
+ if not bool(request.args.get("population_id")):
+ return render_template("genotypes/select-population.html",
+ species=species,
+ populations=order_by_family(
+ populations_by_species(conn, species_id),
+ order_key="FamilyOrder"),
+ activelink="genotypes")
+
+ population = population_by_species_and_id(
+ conn, species_id, request.args.get("population_id"))
+ if not bool(population):
+ flash("Invalid population selected!", "alert-danger")
+ return redirect(url_for(
+ "species.populations.genotypes.select_population",
+ species_id=species_id))
+
+ return redirect(url_for("species.populations.genotypes.list_genotypes",
+ species_id=species_id,
+ population_id=population["Id"]))
+
+
+@genotypesbp.route(
+ "/<int:species_id>/populations/<int:population_id>/genotypes",
+ methods=["GET"])
+@require_login
+def list_genotypes(species_id: int, population_id: int):
+ """List genotype details for species and population."""
+ with database_connection(app.config["SQL_URI"]) as conn:
+ species = species_by_id(conn, species_id)
+ if not bool(species):
+ flash("Invalid species provided!", "alert-danger")
+ return redirect(url_for("species.populations.genotypes.index"))
+
+ population = population_by_species_and_id(
+ conn, species_id, population_id)
+ if not bool(population):
+ flash("Invalid population selected!", "alert-danger")
+ return redirect(url_for(
+ "species.populations.genotypes.select_population",
+ species_id=species_id))
+
+ return render_template("genotypes/list-genotypes.html",
+ species=species,
+ population=population,
+ genocode=genocode_by_population(
+ conn, population_id),
+ total_markers=genotype_markers_count(
+ conn, species_id),
+ activelink="list-genotypes")
+
+
+@genotypesbp.route("/<int:species_id>/genotypes/list-markers", methods=["GET"])
+@require_login
+def list_markers(species_id: int):
+ """List a species' genetic markers."""
+ with database_connection(app.config["SQL_URI"]) as conn:
+ species = species_by_id(conn, species_id)
+ if not bool(species):
+ flash("Invalid species provided!", "alert-danger")
+ return redirect(url_for("species.populations.genotypes.index"))
+
+ start_from = safe_int(request.args.get("start_from") or 0)
+ if start_from < 0:
+ start_from = 0
+ count = safe_int(request.args.get("count") or 20)
+ markers = enumerate_sequence(
+ genotype_markers(conn, species_id, offset=start_from, limit=count),
+ start=start_from+1)
+ return render_template("genotypes/list-markers.html",
+ species=species,
+ total_markers=genotype_markers_count(
+ conn, species_id),
+ start_from=start_from,
+ count=count,
+ markers=markers,
+ activelink="list-markers")
diff --git a/uploader/oauth2/client.py b/uploader/oauth2/client.py
index e119cc3..70a32ff 100644
--- a/uploader/oauth2/client.py
+++ b/uploader/oauth2/client.py
@@ -61,7 +61,7 @@ def __update_auth_server_jwks__(jwks) -> KeySet:
def auth_server_jwks() -> KeySet:
"""Fetch the auth-server JSON Web Keys information."""
- _jwks = session.session_info().get("auth_server_jwks")
+ _jwks = session.session_info().get("auth_server_jwks") or {}
if bool(_jwks):
return __update_auth_server_jwks__({
"last-updated": _jwks["last-updated"],
diff --git a/uploader/population/models.py b/uploader/population/models.py
index 782bc9f..c6c77ae 100644
--- a/uploader/population/models.py
+++ b/uploader/population/models.py
@@ -44,33 +44,32 @@ def population_genetic_types(conn) -> tuple:
return tuple(row["GeneticType"] for row in cursor.fetchall())
-def save_population(conn: mdb.Connection, population_details: dict) -> dict:
+def save_population(cursor: mdb.cursors.Cursor, population_details: dict) -> dict:
"""Save the population details to the db."""
- with conn.cursor(cursorclass=DictCursor) as cursor:
- #TODO: Handle FamilyOrder here
- cursor.execute(
- "INSERT INTO InbredSet("
- "InbredSetId, InbredSetName, Name, SpeciesId, FullName, "
- "public, MappingMethodId, GeneticType, Family, MenuOrderId, "
- "InbredSetCode, Description"
- ") "
- "VALUES ("
- "%(InbredSetId)s, %(InbredSetName)s, %(Name)s, %(SpeciesId)s, "
- "%(FullName)s, %(public)s, %(MappingMethodId)s, %(GeneticType)s, "
- "%(Family)s, %(MenuOrderId)s, %(InbredSetCode)s, %(Description)s"
- ")",
- {
- "MenuOrderId": 0,
- "InbredSetId": 0,
- "public": 2,
- **population_details
- })
- new_id = cursor.lastrowid
- cursor.execute("UPDATE InbredSet SET InbredSetId=%s WHERE Id=%s",
- (new_id, new_id))
- return {
- **population_details,
- "Id": new_id,
- "InbredSetId": new_id,
- "population_id": new_id
- }
+ #TODO: Handle FamilyOrder here
+ cursor.execute(
+ "INSERT INTO InbredSet("
+ "InbredSetId, InbredSetName, Name, SpeciesId, FullName, "
+ "public, MappingMethodId, GeneticType, Family, MenuOrderId, "
+ "InbredSetCode, Description"
+ ") "
+ "VALUES ("
+ "%(InbredSetId)s, %(InbredSetName)s, %(Name)s, %(SpeciesId)s, "
+ "%(FullName)s, %(public)s, %(MappingMethodId)s, %(GeneticType)s, "
+ "%(Family)s, %(MenuOrderId)s, %(InbredSetCode)s, %(Description)s"
+ ")",
+ {
+ "MenuOrderId": 0,
+ "InbredSetId": 0,
+ "public": 2,
+ **population_details
+ })
+ new_id = cursor.lastrowid
+ cursor.execute("UPDATE InbredSet SET InbredSetId=%s WHERE Id=%s",
+ (new_id, new_id))
+ return {
+ **population_details,
+ "Id": new_id,
+ "InbredSetId": new_id,
+ "population_id": new_id
+ }
diff --git a/uploader/population/views.py b/uploader/population/views.py
index 003787a..39a5762 100644
--- a/uploader/population/views.py
+++ b/uploader/population/views.py
@@ -2,7 +2,10 @@
import re
import json
import base64
+import traceback
+from requests.models import Response
+from MySQLdb.cursors import DictCursor
from flask import (flash,
request,
url_for,
@@ -10,10 +13,13 @@ from flask import (flash,
Blueprint,
current_app as app)
+from uploader.samples.views import samplesbp
+from uploader.oauth2.client import oauth2_post
from uploader.ui import make_template_renderer
from uploader.authorisation import require_login
+from uploader.genotypes.views import genotypesbp
from uploader.db_utils import database_connection
-from uploader.samples.views import samplesbp
+from uploader.datautils import enumerate_sequence
from uploader.species.models import (all_species,
species_by_id,
order_species_by_family)
@@ -27,6 +33,7 @@ from .models import (save_population,
__active_link__ = "populations"
popbp = Blueprint("populations", __name__)
popbp.register_blueprint(samplesbp, url_prefix="/")
+popbp.register_blueprint(genotypesbp, url_prefix="/")
render_template = make_template_renderer("populations")
@@ -58,7 +65,8 @@ def list_species_populations(species_id: int):
return render_template(
"populations/list-populations.html",
species=species,
- populations=populations_by_species(conn, species_id),
+ populations=enumerate_sequence(populations_by_species(
+ conn, species_id)),
activelink="list-populations")
@@ -89,7 +97,8 @@ def valid_population_name(population_name: str) -> bool:
@require_login
def create_population(species_id: int):
"""Create a new population."""
- with database_connection(app.config["SQL_URI"]) as conn:
+ with (database_connection(app.config["SQL_URI"]) as conn,
+ conn.cursor(cursorclass=DictCursor) as cursor):
species = species_by_id(conn, species_id)
if request.method == "GET":
@@ -100,7 +109,7 @@ def create_population(species_id: int):
).decode("utf8")
error_values = json.loads(base64.b64decode(
- error_values.encode("utf8")).decode("utf8"))
+ error_values.encode("utf8")).decode("utf8"))# type: ignore[union-attr]
return render_template(
"populations/create-population.html",
species=species,
@@ -119,7 +128,7 @@ def create_population(species_id: int):
flash("You must select a species.", "alert-danger")
return redirect(url_for("species.populations.index"))
- errors = tuple()
+ errors: tuple[tuple[str, str], ...] = tuple()
population_name = (request.form.get(
"population_name") or "").strip()
@@ -149,7 +158,7 @@ def create_population(species_id: int):
species_id=species["SpeciesId"],
error_values=values))
- new_population = save_population(conn, {
+ new_population = save_population(cursor, {
"SpeciesId": species["SpeciesId"],
"Name": population_name,
"InbredSetName": population_fullname,
@@ -161,9 +170,39 @@ def create_population(species_id: int):
"GeneticType": request.form.get("population_genetic_type") or None
})
- return redirect(url_for("species.populations.view_population",
- species_id=species["SpeciesId"],
- population_id=new_population["InbredSetId"]))
+ def __handle_error__(error):
+ error_format = (
+ "\n\nThere was an error creating the population:\n\t%s\n\n")
+ if issubclass(type(error), Exception):
+ app.logger.debug(error_format, traceback.format_exc())
+ raise error
+ if issubclass(type(error), Response):
+ try:
+ _data = error.json()
+ except Exception as _exc:
+ raise Exception(error.content) from _exc
+ raise Exception(_data)
+
+ app.logger.debug(error_format, error)
+ raise Exception(error)
+
+ def __flash_success__(_success):
+ flash("Successfully created resource.", "alert-success")
+ return redirect(url_for(
+ "species.populations.view_population",
+ species_id=species["SpeciesId"],
+ population_id=new_population["InbredSetId"]))
+
+ app.logger.debug("We begin setting up the privileges here…")
+ return oauth2_post(
+ "auth/resource/populations/create",
+ json={
+ **dict(request.form),
+ "species_id": species_id,
+ "population_id": new_population["Id"],
+ "public": "on"
+ }
+ ).either(__handle_error__, __flash_success__)
@popbp.route("/<int:species_id>/populations/<int:population_id>",
diff --git a/uploader/samples/views.py b/uploader/samples/views.py
index 6e3dc4b..fd3c601 100644
--- a/uploader/samples/views.py
+++ b/uploader/samples/views.py
@@ -19,9 +19,9 @@ from flask import (
from uploader import jobs
from uploader.files import save_file
-from uploader.datautils import order_by_family
from uploader.authorisation import require_login
from uploader.input_validation import is_integer_input
+from uploader.datautils import order_by_family, enumerate_sequence
from uploader.db_utils import (
with_db_connection,
database_connection,
@@ -39,6 +39,7 @@ from .models import samples_by_species_and_population
samplesbp = Blueprint("samples", __name__)
@samplesbp.route("/samples", methods=["GET"])
+@require_login
def index():
"""Direct entry-point for uploading/handling the samples."""
with database_connection(app.config["SQL_URI"]) as conn:
@@ -56,6 +57,7 @@ def index():
@samplesbp.route("<int:species_id>/samples/select-population", methods=["GET"])
+@require_login
def select_population(species_id: int):
"""Select the population to use for the samples."""
with database_connection(app.config["SQL_URI"]) as conn:
@@ -86,6 +88,7 @@ def select_population(species_id: int):
population_id=population["Id"]))
@samplesbp.route("<int:species_id>/populations/<int:population_id>/samples")
+@require_login
def list_samples(species_id: int, population_id: int):
"""
List the samples in a particular population and give the ability to upload
@@ -104,13 +107,13 @@ def list_samples(species_id: int, population_id: int):
"species.populations.samples.select_population",
species_id=species_id))
- all_samples = samples_by_species_and_population(
- conn, species_id, population_id)
+ all_samples = enumerate_sequence(samples_by_species_and_population(
+ conn, species_id, population_id))
total_samples = len(all_samples)
offset = int(request.args.get("from") or 0)
if offset < 0:
offset = 0
- count = int(request.args.get("count") or 10)
+ count = int(request.args.get("count") or 20)
return render_template("samples/list-samples.html",
species=species,
population=population,
@@ -197,6 +200,11 @@ def upload_samples(species_id: int, population_id: int):#pylint: disable=[too-ma
redisuri = app.config["REDIS_URL"]
with Redis.from_url(redisuri, decode_responses=True) as rconn:
+ #TODO: Add a QC step here — what do we check?
+ # 1. Does any sample in the uploaded file exist within the database?
+ # If yes, what is/are its/their species and population?
+ # 2. If yes 1. above, provide error with notes on which species and
+ # populations already own the samples.
the_job = jobs.launch_job(
jobs.initialise_job(
rconn,
@@ -224,6 +232,7 @@ def upload_samples(species_id: int, population_id: int):#pylint: disable=[too-ma
@samplesbp.route("<int:species_id>/populations/<int:population_id>/"
"upload-samples/status/<uuid:job_id>",
methods=["GET"])
+@require_login
def upload_status(species_id: int, population_id: int, job_id: uuid.UUID):
"""Check on the status of a samples upload job."""
with database_connection(app.config["SQL_URI"]) as conn:
@@ -273,6 +282,7 @@ def upload_status(species_id: int, population_id: int, job_id: uuid.UUID):
population=population), 400
@samplesbp.route("/upload/failure/<uuid:job_id>", methods=["GET"])
+@require_login
def upload_failure(job_id: uuid.UUID):
"""Display the errors of the samples upload failure."""
job = with_redis_connection(lambda rconn: jobs.job(
diff --git a/uploader/session.py b/uploader/session.py
index 399f28c..b538187 100644
--- a/uploader/session.py
+++ b/uploader/session.py
@@ -96,7 +96,7 @@ def user_token() -> Either:
def set_auth_server_jwks(keyset: KeySet) -> KeySet:
"""Update the JSON Web Keys in the session."""
save_session_info({
- **session_info(),
+ **session_info(),# type: ignore[misc]
"auth_server_jwks": {
"last-updated": datetime.now().timestamp(),
"jwks": keyset.as_dict()
diff --git a/uploader/species/models.py b/uploader/species/models.py
index 1abed6d..51f941c 100644
--- a/uploader/species/models.py
+++ b/uploader/species/models.py
@@ -28,7 +28,7 @@ def order_species_by_family(species: tuple[dict, ...]) -> list:
**ordered,
_key: ordered.get(_key, tuple()) + (current,)
}
- ordered = reduce(__order__, species, {})
+ ordered = reduce(__order__, species, {})# type: ignore[var-annotated]
return sorted(tuple(ordered.items()), key=lambda item: item[0][0])
@@ -47,7 +47,7 @@ def save_species(conn: mdb.Connection,
common_name: str,
scientific_name: str,
family: str,
- taxon_id: Optional[str] = None) -> int:
+ taxon_id: Optional[str] = None) -> dict:
"""
Save a new species to the database.
@@ -58,14 +58,14 @@ def save_species(conn: mdb.Connection,
common_name: The species' common name.
scientific_name; The species' scientific name.
"""
- genus, species = scientific_name.split(" ")
+ genus, species_name = scientific_name.split(" ")
families = species_families(conn)
with conn.cursor() as cursor:
cursor.execute("SELECT MAX(OrderId) FROM Species")
species = {
"common_name": common_name,
"common_name_lower": common_name.lower(),
- "menu_name": f"{common_name} ({genus[0]}. {species.lower()})",
+ "menu_name": f"{common_name} ({genus[0]}. {species_name.lower()})",
"scientific_name": scientific_name,
"family": family,
"family_order": families[family],
@@ -91,13 +91,15 @@ def save_species(conn: mdb.Connection,
}
-def update_species(conn: mdb.Connection,
- species_id: int,
- common_name: str,
- scientific_name: str,
- family: str,
- family_order: int,
- species_order: int):
+def update_species(# pylint: disable=[too-many-arguments]
+ conn: mdb.Connection,
+ species_id: int,
+ common_name: str,
+ scientific_name: str,
+ family: str,
+ family_order: int,
+ species_order: int
+):
"""Update a species' details.
Parameters
@@ -114,12 +116,12 @@ def update_species(conn: mdb.Connection,
species_order: The ordering of this species in relation to others
"""
with conn.cursor(cursorclass=DictCursor) as cursor:
- genus, species = scientific_name.split(" ")
+ genus, species_name = scientific_name.split(" ")
species = {
"species_id": species_id,
"common_name": common_name,
"common_name_lower": common_name.lower(),
- "menu_name": f"{common_name} ({genus[0]}. {species.lower()})",
+ "menu_name": f"{common_name} ({genus[0]}. {species_name.lower()})",
"scientific_name": scientific_name,
"family": family,
"family_order": family_order,
diff --git a/uploader/species/views.py b/uploader/species/views.py
index 55b0dd3..f478505 100644
--- a/uploader/species/views.py
+++ b/uploader/species/views.py
@@ -8,11 +8,11 @@ from flask import (flash,
current_app as app)
from uploader.population import popbp
-from uploader.datautils import order_by_family
from uploader.ui import make_template_renderer
from uploader.db_utils import database_connection
from uploader.oauth2.client import oauth2_get, oauth2_post
from uploader.authorisation import require_login, require_token
+from uploader.datautils import order_by_family, enumerate_sequence
from .models import (all_species,
save_species,
@@ -27,11 +27,12 @@ render_template = make_template_renderer("species")
@speciesbp.route("/", methods=["GET"])
+@require_login
def list_species():
"""List and display all the species in the database."""
with database_connection(app.config["SQL_URI"]) as conn:
return render_template("species/list-species.html",
- allspecies=all_species(conn))
+ allspecies=enumerate_sequence(all_species(conn)))
@speciesbp.route("/<int:species_id>", methods=["GET"])
@require_login
diff --git a/uploader/static/css/styles.css b/uploader/static/css/styles.css
index 834c563..4565aba 100644
--- a/uploader/static/css/styles.css
+++ b/uploader/static/css/styles.css
@@ -26,7 +26,7 @@ body {
#header .header {
font-size: 2em;
display: inline-block;
- text-align: center;
+ text-align: start;
}
#header .header-nav {
@@ -71,8 +71,9 @@ body {
}
.pagetitle h1 {
- text-align: center;
+ text-align: start;
text-transform: capitalize;
+ padding-left: 0.25em;
}
.pagetitle .breadcrumb {
@@ -101,7 +102,7 @@ dd {
padding-bottom: 1em;
}
-input[type="submit"] {
+input[type="submit"], .btn {
text-transform: capitalize;
}
diff --git a/uploader/templates/base.html b/uploader/templates/base.html
index d68c6c0..886f503 100644
--- a/uploader/templates/base.html
+++ b/uploader/templates/base.html
@@ -31,7 +31,7 @@
<li class="btn">
{%if user_logged_in()%}
<a href="{{url_for('oauth2.logout')}}"
- title="Log out of the system">Log Out</a>
+ title="Log out of the system">{{user_email()}} &mdash; Log Out</a>
{%else%}
<a href="{{authserver_authorise_uri()}}"
title="Log in to the system">Log In</a>
@@ -46,12 +46,12 @@
<li><a href="/" >Home</a></li>
<li><a href="{{url_for('species.list_species')}}"
title="View and manage species information.">Species</a></li>
- <li><a href="#"
- title="Upload Genotype data.">Genotype Data</a></li>
<li><a href="{{url_for('species.populations.index')}}"
title="View and manage species populations.">Populations</a></li>
<li><a href="{{url_for('species.populations.samples.index')}}"
title="Upload population samples.">Samples</a></li>
+ <li><a href="{{url_for('species.populations.genotypes.index')}}"
+ title="Upload Genotype data.">Genotype Data</a></li>
<li><a href="{{url_for('expression-data.index.index')}}"
title="Upload expression data.">Expression Data</a></li>
<li><a href="#"
diff --git a/uploader/templates/genotypes/base.html b/uploader/templates/genotypes/base.html
new file mode 100644
index 0000000..1b274bf
--- /dev/null
+++ b/uploader/templates/genotypes/base.html
@@ -0,0 +1,12 @@
+{%extends "populations/base.html"%}
+
+{%block lvl3_breadcrumbs%}
+<li {%if activelink=="genotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.index')}}">Genotypes</a>
+</li>
+{%block lvl4_breadcrumbs%}{%endblock%}
+{%endblock%}
diff --git a/uploader/templates/genotypes/index.html b/uploader/templates/genotypes/index.html
new file mode 100644
index 0000000..e749f5a
--- /dev/null
+++ b/uploader/templates/genotypes/index.html
@@ -0,0 +1,28 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-select-species.html" import select_species_form%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ This section allows you to upload genotype information for your experiments,
+ in the case that you have not previously done so.
+ </p>
+ <p>
+ We'll need to link the genotypes to the species and population, so do please
+ go ahead and select those in the next two steps.
+ </p>
+</div>
+
+<div class="row">
+ {{select_species_form(url_for("species.populations.genotypes.index"),
+ species)}}
+</div>
+{%endblock%}
diff --git a/uploader/templates/genotypes/list-genotypes.html b/uploader/templates/genotypes/list-genotypes.html
new file mode 100644
index 0000000..31b9eeb
--- /dev/null
+++ b/uploader/templates/genotypes/list-genotypes.html
@@ -0,0 +1,119 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "populations/macro-display-population-card.html" import display_population_card%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-genotypes"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.list_genotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}">List genotypes</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <h2>Genetic Markers</h2>
+ <p>There are a total of {{total_markers}} currently registered genetic markers
+ for the "{{species.FullName}}" species. You can click
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId)}}"
+ title="View genetic markers for species '{{species.FullName}}">
+ this link to view the genetic markers
+ </a>.
+ </p>
+</div>
+
+<div class="row">
+ <h2>Genotype Encoding</h2>
+ <p>
+ The genotype encoding used for the "{{population.FullName}}" population from
+ the "{{species.FullName}}" species is as shown in the table below.
+ </p>
+ <table class="table">
+
+ <thead>
+ <tr>
+ <th>Allele Type</th>
+ <th>Allele Symbol</th>
+ <th>Allele Value</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for row in genocode%}
+ <tr>
+ <td>{{row.AlleleType}}</td>
+ <td>{{row.AlleleSymbol}}</td>
+ <td>{{row.DatabaseValue if row.DatabaseValue is not none else "NULL"}}</td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="7" class="text-info">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ There is no explicit genotype encoding defined for this population.
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+
+ {%if genocode | length < 1%}
+ <a href="#add-genotype-encoding"
+ title="Add a genotype encoding system for this population"
+ class="btn btn-primary">
+ add genotype encoding
+ </a>
+ {%endif%}
+</div>
+
+<div class="row text-danger">
+ <h3>Some Important Concepts to Consider/Remember</h3>
+ <ul>
+ <li>Reference vs. Non-reference alleles</li>
+ <li>In <em>GenoCode</em> table, items are ordered by <strong>InbredSet</strong></li>
+ </ul>
+ <h3>Possible references</h3>
+ <ul>
+ <li>https://mr-dictionary.mrcieu.ac.uk/term/genotype/</li>
+ <li>https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7363099/</li>
+ </ul>
+</div>
+
+<div class="row">
+ <h2>Genotype Datasets</h2>
+
+ <p>The genotype data is organised under various genotype datasets. You can
+ click on the link for the relevant dataset to view a little more information
+ about it.</p>
+ <p>You can also create a new genotype dataset by clicking the button below.
+ <br />
+ <a href="#create-new-genotype-dataset"
+ title="Create a new genotype dataset for the '{{population.FullName}}' population for the '{{species.FullName}}' species."
+ class="btn btn-primary">
+ create new genotype dataset</a></p>
+
+ {%if datasets | length > 0%}
+ <table class="table">
+ </table>
+ {%else%}
+ <p class="text-warning">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ There are no genotype datasets to display, yet!
+ </p>
+ {%endif%}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_population_card(species, population)}}
+{%endblock%}
diff --git a/uploader/templates/genotypes/list-markers.html b/uploader/templates/genotypes/list-markers.html
new file mode 100644
index 0000000..9198b44
--- /dev/null
+++ b/uploader/templates/genotypes/list-markers.html
@@ -0,0 +1,102 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+
+{%block title%}Genotypes: List Markers{%endblock%}
+
+{%block pagetitle%}Genotypes: List Markers{%endblock%}
+
+{%block lvl4_breadcrumbs%}
+<li {%if activelink=="list-markers"%}
+ class="breadcrumb-item active"
+ {%else%}
+ class="breadcrumb-item"
+ {%endif%}>
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId)}}">List markers</a>
+</li>
+{%endblock%}
+
+{%block contents%}
+{{flash_all_messages()}}
+
+{%if markers | length > 0%}
+<div class="row">
+ <p>
+ There are a total of {{total_markers}} genotype markers for this species.
+ </p>
+ <div class="row">
+ <div class="col-md-2" style="text-align: start;">
+ {%if start_from > 0%}
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ start_from=start_from-count,
+ count=count)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
+ <div class="col-md-8" style="text-align: center;">
+ Displaying markers {{start_from+1}} to {{start_from+count if start_from+count < total_markers else total_markers}} of
+ {{total_markers}}
+ </div>
+ <div class="col-md-2" style="text-align: end;">
+ {%if start_from + count < total_markers%}
+ <a href="{{url_for('species.populations.genotypes.list_markers',
+ species_id=species.SpeciesId,
+ start_from=start_from+count,
+ count=count)}}">
+ Next
+ <span class="glyphicon glyphicon-forward"></span>
+ </a>
+ {%endif%}
+ </div>
+ </div>
+ <table class="table">
+ <thead>
+ <tr>
+ <th title="">#</th>
+ <th title="">Marker Name</th>
+ <th title="Chromosome">Chr</th>
+ <th title="Physical location of the marker in megabasepairs">
+ Location (Mb)</th>
+ <th title="">Source</th>
+ <th title="">Source2</th>
+ </thead>
+
+ <tbody>
+ {%for marker in markers%}
+ <tr>
+ <td>{{marker.sequence_number}}</td>
+ <td>{{marker.Marker_Name}}</td>
+ <td>{{marker.Chr}}</td>
+ <td>{{marker.Mb}}</td>
+ <td>{{marker.Source}}</td>
+ <td>{{marker.Source2}}</td>
+ </tr>
+ {%endfor%}
+ </tbody>
+ </table>
+</div>
+{%else%}
+<div class="row">
+ <p class="text-warning">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ This species does not currently have any genetic markers uploaded, therefore,
+ there is nothing to display here.
+ </p>
+ <p>
+ <a href="#add-genetic-markers-for-species-{{species.SpeciesId}}"
+ title="Add genetic markers for this species"
+ class="btn btn-primary">
+ add genetic markers
+ </a>
+ </p>
+</div>
+{%endif%}
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/genotypes/select-population.html b/uploader/templates/genotypes/select-population.html
new file mode 100644
index 0000000..7c81943
--- /dev/null
+++ b/uploader/templates/genotypes/select-population.html
@@ -0,0 +1,31 @@
+{%extends "genotypes/base.html"%}
+{%from "flash_messages.html" import flash_all_messages%}
+{%from "species/macro-display-species-card.html" import display_species_card%}
+{%from "populations/macro-select-population.html" import select_population_form%}
+
+{%block title%}Genotypes{%endblock%}
+
+{%block pagetitle%}Genotypes{%endblock%}
+
+
+{%block contents%}
+{{flash_all_messages()}}
+
+<div class="row">
+ <p>
+ You have indicated that you intend to upload the genotypes for species
+ '{{species.FullName}}'. We now just require the population for your
+ experiment/study, and you should be good to go.
+ </p>
+</div>
+
+<div class="row">
+ {{select_population_form(url_for("species.populations.genotypes.select_population",
+ species_id=species.SpeciesId),
+ populations)}}
+</div>
+{%endblock%}
+
+{%block sidebarcontents%}
+{{display_species_card(species)}}
+{%endblock%}
diff --git a/uploader/templates/index.html b/uploader/templates/index.html
index e3f5af4..0fa4a1b 100644
--- a/uploader/templates/index.html
+++ b/uploader/templates/index.html
@@ -18,9 +18,87 @@
<div class="explainer">
<p>Welcome to the <strong>GeneNetwork Data Quality Control and Upload System</strong>. This system is provided to help in uploading your data onto GeneNetwork where you can do analysis on it.</p>
- <p>Click on the menu items on the left to select the kind of data you want to upload.</p>
+ <p>The sections below provide an overview of what features the menu items on
+ the left provide to you. Please peruse the information to get a good
+ big-picture understanding of what the system provides you and how to get
+ the most out of it.</p>
{%block extrapageinfo%}{%endblock%}
+
+ <h2>Species</h2>
+
+ <p>The GeneNetwork service provides datasets and tools for doing genetic
+ studies &mdash; from
+ <a href="{{gn2server_intro}}"
+ target="_blank"
+ title="GeneNetwork introduction — opens in a new tab.">
+ its introduction</a>:
+
+ <blockquote class="blockquote">
+ <p>GeneNetwork is a group of linked data sets and tools used to study
+ complex networks of genes, molecules, and higher order gene function
+ and phenotypes. &hellip;</p>
+ </blockquote>
+ </p>
+
+ <p>With this in mind, it follows that the data in the system is centered
+ aroud a variety of species. The <strong>species section</strong> will
+ list the currently available species in the system, and give you the
+ ability to add new ones, if the one you want to work on does not currently
+ exist on GeneNetwork</p>
+
+ <h2>Populations</h2>
+
+ <p>Your studies will probably focus on a particular subset of the entire
+ species you are interested in &ndash your population.</p>
+ <p>Populations are a way to organise the species data so as to link data to
+ specific know populations for a particular species, e.g. The BXD
+ population of mice (Mus musculus)</p>
+ <p>In older GeneNetwork documentation, you might run into the term
+ <em>InbredSet</em>. Should you run into it, it is a term that we've
+ deprecated that essentially just means the population.</p>
+
+ <h2>Samples</h2>
+
+ <p>These are the samples or individuals (sometimes cases) that were involved
+ in the experiment, and from whom the data was derived.</p>
+
+ <h2>Genotype Data</h2>
+
+ <p>This section will allow you to view and upload the genetic markers for
+ your species, and the genotype encodings used for your particular
+ population.</p>
+ <p>While, technically, genetic markers relate to the species in general, and
+ not to a particular population, the data (allele information) itself
+ relates to the particular population it was generated from &ndash;
+ specifically, to the actual individuals used in the experiment.</p>
+ <p>This is the reason why the genotype data information comes under the
+ population, and will check for the prior existence of the related
+ samples/individuals before attempting an upload of your data.</p>
+
+ <h2>Expression Data</h2>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+
+ <h2>Phenotype Data</h2>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+
+ <h2>Individual Data</h2>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
+
+ <h2>RNA-Seq Data</h2>
+
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ <strong>TODO</strong>: Document this &hellip;</p>
</div>
</div>
diff --git a/uploader/templates/login.html b/uploader/templates/login.html
index bbca42f..1f71416 100644
--- a/uploader/templates/login.html
+++ b/uploader/templates/login.html
@@ -5,7 +5,7 @@
{%block pagetitle%}log in{%endblock%}
{%block extrapageinfo%}
-<p>
+<p class="text-dark text-primary">
You <strong>do need to be logged in</strong> to upload data onto this system.
Please do that by clicking the "Log In" button at the top of the page.</p>
{%endblock%}
diff --git a/uploader/templates/populations/create-population.html b/uploader/templates/populations/create-population.html
index b57afba..b05ce37 100644
--- a/uploader/templates/populations/create-population.html
+++ b/uploader/templates/populations/create-population.html
@@ -107,7 +107,10 @@
value="{{error_values.population_code or ''}}"
class="form-control" />
<small class="form-text text-muted">
- … document what this field is for …
+ <p class="text-danger">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ What is this field is for? Confirm with Arthur and the rest.
+ </p>
</small>
</div>
@@ -159,7 +162,10 @@
{%endfor%}
</select>
<small class="form-text text-muted">
- <p>… provide some documentation on what this field does …</p>
+ <p>
+ This is a rough grouping of the populations in GeneNetwork into lists
+ of common types of populations.
+ </p>
</small>
</div>
@@ -207,6 +213,28 @@
{%endif%}>{{gtype}}</option>
{%endfor%}
</select>
+ <small class="form-text text-muted text-danger">
+ <p>
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ This might be a poorly named field.
+ </p>
+ <p>
+ It probably has more to do with the mating crosses/crossings used to
+ produce the individuals in the population. I am no biologist, however,
+ and I'm leaving this here to remind myself to confirm this.
+ </p>
+ <p>
+ I still don't know what riset is.<br />
+ … probably something to do with Recombinant Inbred Strains
+ </p>
+ <p>
+ Possible resources for this:
+ <ul>
+ <li>https://www.informatics.jax.org/silver/chapters/3-2.shtml</li>
+ <li>https://www.informatics.jax.org/silver/chapters/9-2.shtml</li>
+ </ul>
+ </p>
+ </small>
</div>
<div class="form-group">
diff --git a/uploader/templates/populations/index.html b/uploader/templates/populations/index.html
index 3314516..4354e02 100644
--- a/uploader/templates/populations/index.html
+++ b/uploader/templates/populations/index.html
@@ -11,8 +11,14 @@
{{flash_all_messages()}}
<div class="row">
- To continue, you need to select the species:
+ <p>
+ Your experiment data will relate to a particular population from a
+ particular species. Let us know what species it is you want to work with
+ below.
+ </p>
+</div>
+<div class="row">
{{select_species_form(url_for("species.populations.index"), species)}}
</div>
{%endblock%}
diff --git a/uploader/templates/populations/list-populations.html b/uploader/templates/populations/list-populations.html
index c83c18c..7c7145f 100644
--- a/uploader/templates/populations/list-populations.html
+++ b/uploader/templates/populations/list-populations.html
@@ -51,6 +51,7 @@
<caption>Populations for {{species.FullName}}</caption>
<thead>
<tr>
+ <th>#</th>
<th>Name</th>
<th>Full Name</th>
<th>Description</th>
@@ -60,6 +61,7 @@
<tbody>
{%for population in populations%}
<tr>
+ <td>{{population["sequence_number"]}}</td>
<td>
<a href="{{url_for('species.populations.view_population',
species_id=species.SpeciesId,
diff --git a/uploader/templates/populations/view-population.html b/uploader/templates/populations/view-population.html
index 31db54f..1e2964e 100644
--- a/uploader/templates/populations/view-population.html
+++ b/uploader/templates/populations/view-population.html
@@ -23,6 +23,9 @@
{%block contents%}
<div class="row">
<h2>Population Details</h2>
+
+ {{flash_all_messages()}}
+
<dl>
<dt>Name</dt>
<dd>{{population.Name}}</dd>
@@ -59,6 +62,12 @@
<nav class="nav">
<ul>
<li>
+ <a href="{{url_for('species.populations.genotypes.list_genotypes',
+ species_id=species.SpeciesId,
+ population_id=population.Id)}}"
+ title="Upload genotypes for {{species.FullName}}">Upload Genotypes</a>
+ </li>
+ <li>
<a href="{{url_for('species.populations.samples.list_samples',
species_id=species.SpeciesId,
population_id=population.Id)}}"
diff --git a/uploader/templates/samples/index.html b/uploader/templates/samples/index.html
index 7c88c01..ee4a63e 100644
--- a/uploader/templates/samples/index.html
+++ b/uploader/templates/samples/index.html
@@ -11,10 +11,8 @@
{{flash_all_messages()}}
<div class="row">
- <p>Here, you can upload the samples/individuals that were used in your
- experiments.</p>
- <p>Since the samples are linked to specific species and populations, we will
- need to first select them in the next few steps.</p>
+ <p>GeneNetwork has a selection of different species of organisms to choose from. Within those species, there are the populations of interest for a variety of experiments, from which you, the researcher, picked your samples (or individuals or cases) from. Here you can provide some basic details about your samples.</p>
+ <p>To start off, we will need to know what species and population your samples belong to. Please provide that information in the next sections.</p>
{{select_species_form(url_for("species.populations.samples.index"), species)}}
</div>
diff --git a/uploader/templates/samples/list-samples.html b/uploader/templates/samples/list-samples.html
index 8f1bf16..13e5cec 100644
--- a/uploader/templates/samples/list-samples.html
+++ b/uploader/templates/samples/list-samples.html
@@ -24,45 +24,56 @@
<div class="row">
<p>
- Samples for population "{{population.FullName}}" from the
+ You selected the population "{{population.FullName}}" from the
"{{species.FullName}}" species.
</p>
+</div>
+
+{%if samples | length > 0%}
+<div class="row">
+ <p>
+ This population already has <strong>{{total_samples}}</strong>
+ samples/individuals entered. You can explore the list of samples in this
+ population in the table below.
+ </p>
+</div>
+
+<div class="row">
+ <div class="col-md-2">
+ {%if offset > 0:%}
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ from=offset-count,
+ count=count)}}">
+ <span class="glyphicon glyphicon-backward"></span>
+ Previous
+ </a>
+ {%endif%}
+ </div>
- {%if samples | length > 0%}
- <div class="row">
- <div class="col-md-2">
- {%if offset > 0:%}
- <a href="{{url_for('species.populations.samples.list_samples',
- species_id=species.SpeciesId,
- population_id=population.Id,
- from=offset-count,
- count=count)}}">
- <span class="glyphicon glyphicon-backward"></span>
- Previous
- </a>
- {%endif%}
- </div>
-
- <div class="col-md-8" style="text-align: center;">
- Samples {{offset}} &mdash; {{offset+(count if offset + count < total_samples else total_samples - offset)}} / {{total_samples}}
- </div>
-
- <div class="col-md-2">
- {%if offset + count < total_samples:%}
- <a href="{{url_for('species.populations.samples.list_samples',
- species_id=species.SpeciesId,
- population_id=population.Id,
- from=offset+count,
- count=count)}}">
- Next
- <span class="glyphicon glyphicon-forward"></span>
- </a>
- {%endif%}
- </div>
+ <div class="col-md-8" style="text-align: center;">
+ Samples {{offset}} &mdash; {{offset+(count if offset + count < total_samples else total_samples - offset)}} / {{total_samples}}
+ </div>
+
+ <div class="col-md-2">
+ {%if offset + count < total_samples:%}
+ <a href="{{url_for('species.populations.samples.list_samples',
+ species_id=species.SpeciesId,
+ population_id=population.Id,
+ from=offset+count,
+ count=count)}}">
+ Next
+ <span class="glyphicon glyphicon-forward"></span>
+ </a>
+ {%endif%}
</div>
+</div>
+<div class="row">
<table class="table">
<thead>
<tr>
+ <th>#</th>
<th>Name</th>
<th>Auxilliary Name</th>
<th>Symbol</th>
@@ -73,6 +84,7 @@
<tbody>
{%for sample in samples%}
<tr>
+ <td>{{sample.sequence_number}}</td>
<td>{{sample.Name}}</td>
<td>{{sample.Name2}}</td>
<td>{{sample.Symbol or "-"}}</td>
@@ -90,10 +102,14 @@
delete all samples
</a>
</p>
- {%else%}
- <p class="text-danger">
- <span class="glyphicon glyphicon-exclamation-sign"></span>
- There are no samples for this population at this time.
+</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>
@@ -106,8 +122,9 @@
add samples
</a>
</p>
- {%endif%}
</div>
+{%endif%}
+
{%endblock%}
{%block sidebarcontents%}
diff --git a/uploader/templates/samples/select-population.html b/uploader/templates/samples/select-population.html
index 8e22ac1..f437780 100644
--- a/uploader/templates/samples/select-population.html
+++ b/uploader/templates/samples/select-population.html
@@ -12,20 +12,25 @@
{{flash_all_messages()}}
<div class="row">
- <p>Select the population to use with your samples:</p>
+ <p>You have selected "{{species.FullName}}" as the species that your data relates to.</p>
+ <p>Next, we need information regarding the population your data relates to. Do please select the population from the existing ones below</p>
+</div>
+
+<div class="row">
{{select_population_form(
url_for("species.populations.samples.select_population", species_id=species.SpeciesId),
populations)}}
</div>
-<div class="row">
- <p><strong>Cannot find your population in the list?</strong></p>
- <p>If you cannot find the population you want in the drop-down above, you can
- instead,
+<div class="row">
+ <p>
+ If you cannot find the population your data relates to in the drop-down
+ above, you might want to
<a href="{{url_for('species.populations.create_population',
species_id=species.SpeciesId)}}"
title="Create a new population for species '{{species.FullName}},">
- create a new population</a>.
+ add a new population to GeneNetwork</a>
+ instead.
</div>
{%endblock%}
diff --git a/uploader/templates/samples/upload-samples.html b/uploader/templates/samples/upload-samples.html
index b101b2e..25d3290 100644
--- a/uploader/templates/samples/upload-samples.html
+++ b/uploader/templates/samples/upload-samples.html
@@ -23,7 +23,12 @@
{{flash_all_messages()}}
<div class="row">
- <p>You can now upload a character-separated value (CSV) file that contains
+ <p>
+ You can now upload the samples for the "{{population.FullName}}" population
+ from the "{{species.FullName}}" species here.
+ </p>
+ <p>
+ Upload a <strong>character-separated value (CSV)</strong> file that contains
details about your samples. The CSV file should have the following fields:
<dl>
<dt>Name</dt>
diff --git a/uploader/templates/species/list-species.html b/uploader/templates/species/list-species.html
index 573bcee..85c9d40 100644
--- a/uploader/templates/species/list-species.html
+++ b/uploader/templates/species/list-species.html
@@ -29,29 +29,35 @@
<caption>Available Species</caption>
<thead>
<tr>
- <th>Common Name</th>
- <th>Scientific Name</th>
- <th>TaxonId</th>
- <th>Use</th>
+ <th>#</td>
+ <th title="A common, layman's name for the species.">Common Name</th>
+ <th title="The scientific name for the species">Organism Name</th>
+ <th title="An identifier for the species in the NCBI taxonomy database">
+ Taxonomy ID
+ </th>
+ <th title="A generic grouping used internally by GeneNetwork for organising species.">
+ Family
+ </th>
</tr>
</thead>
<tbody>
{%for species in allspecies%}
<tr>
+ <td>{{species["sequence_number"]}}</td>
<td>{{species["SpeciesName"]}}</td>
- <td>{{species["FullName"]}}</td>
- <td>
- <a href="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={{species['TaxonomyId']}}"
- title="View species details on NCBI"
- target="_blank">{{species["TaxonomyId"]}}</a>
- </td>
<td>
<a href="{{url_for('species.view_species',
species_id=species['SpeciesId'])}}"
- title="">
- {{species["SpeciesName"]}} ({{species["FullName"]}})
+ title="View details in GeneNetwork on {{species['FullName']}}">
+ {{species["FullName"]}}
</a>
</td>
+ <td>
+ <a href="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={{species['TaxonomyId']}}"
+ title="View species details on NCBI"
+ target="_blank">{{species["TaxonomyId"]}}</a>
+ </td>
+ <td>{{species.Family}}</td>
</tr>
{%else%}
<tr>
diff --git a/uploader/templates/species/macro-select-species.html b/uploader/templates/species/macro-select-species.html
index 3dbfc95..6955134 100644
--- a/uploader/templates/species/macro-select-species.html
+++ b/uploader/templates/species/macro-select-species.html
@@ -4,7 +4,7 @@
<legend>Select Species</legend>
<div class="form-group">
- <label for="select-species" class="form-label">Select Species</label>
+ <label for="select-species" class="form-label">Species</label>
<select id="select-species"
name="species_id"
class="form-control"
diff --git a/uploader/templates/species/view-species.html b/uploader/templates/species/view-species.html
index 6942168..b01864d 100644
--- a/uploader/templates/species/view-species.html
+++ b/uploader/templates/species/view-species.html
@@ -40,16 +40,11 @@
<ol>
<li>
- <a href="#"
- title="Upload genotypes for {{species.FullName}}">Upload Genotypes</a>
- </li>
- <li>
<a href="{{url_for('species.populations.list_species_populations',
species_id=species.SpeciesId)}}"
title="Create/Edit populations for {{species.FullName}}">
Manage populations</a>
</li>
- <li><a href="#" title="">any other action, perhaps &hellip;</a></li>
</ol>