diff options
-rw-r--r-- | scripts/insert_samples.py | 3 | ||||
-rw-r--r-- | uploader/__init__.py | 2 | ||||
-rw-r--r-- | uploader/db/__init__.py | 6 | ||||
-rw-r--r-- | uploader/dbinsert.py | 2 | ||||
-rw-r--r-- | uploader/expression_data/rqtl2.py | 8 | ||||
-rw-r--r-- | uploader/expression_data/samples.py | 9 | ||||
-rw-r--r-- | uploader/population/__init__.py | 3 | ||||
-rw-r--r-- | uploader/population/models.py (renamed from uploader/db/populations.py) | 0 | ||||
-rw-r--r-- | uploader/population/views.py | 120 | ||||
-rw-r--r-- | uploader/static/css/styles.css | 2 | ||||
-rw-r--r-- | uploader/templates/base.html | 1 | ||||
-rw-r--r-- | uploader/templates/populations/base.html | 12 | ||||
-rw-r--r-- | uploader/templates/populations/index.html | 16 | ||||
-rw-r--r-- | uploader/templates/populations/list-populations.html | 87 | ||||
-rw-r--r-- | uploader/templates/species/view-species.html | 3 |
15 files changed, 253 insertions, 21 deletions
diff --git a/scripts/insert_samples.py b/scripts/insert_samples.py index 8493112..d107c25 100644 --- a/scripts/insert_samples.py +++ b/scripts/insert_samples.py @@ -9,7 +9,8 @@ from redis import Redis from uploader.db_utils import database_connection from uploader.check_connections import check_db, check_redis -from uploader.db import species_by_id, population_by_id +from uploader.species.models import species_by_id +from uploader.population.models import population_by_id from uploader.expression_data.samples import ( save_samples_data, read_samples_file, diff --git a/uploader/__init__.py b/uploader/__init__.py index 347f170..5028f79 100644 --- a/uploader/__init__.py +++ b/uploader/__init__.py @@ -10,6 +10,7 @@ from flask_session import Session from uploader.oauth2.client import user_logged_in, authserver_authorise_uri from .base_routes import base +from .population import popbp from .species import speciesbp from .dbinsert import dbinsertbp from .oauth2.views import oauth2 @@ -83,6 +84,7 @@ def create_app(): app.register_blueprint(base, url_prefix="/") app.register_blueprint(oauth2, url_prefix="/oauth2") app.register_blueprint(speciesbp, url_prefix="/species") + app.register_blueprint(popbp, url_prefix="/populations") app.register_blueprint(dbinsertbp, url_prefix="/dbinsert") app.register_blueprint(exprdatabp, url_prefix="/expression-data") diff --git a/uploader/db/__init__.py b/uploader/db/__init__.py index 36e93e8..d2b1d9d 100644 --- a/uploader/db/__init__.py +++ b/uploader/db/__init__.py @@ -1,8 +1,2 @@ """Database functions""" -from .species import species, species_by_id -from .populations import ( - save_population, - population_by_id, - populations_by_species, - population_by_species_and_id) from .datasets import geno_datasets_by_species_and_population diff --git a/uploader/dbinsert.py b/uploader/dbinsert.py index 328ccc8..2116031 100644 --- a/uploader/dbinsert.py +++ b/uploader/dbinsert.py @@ -12,8 +12,8 @@ from flask import ( current_app as app) from uploader.authorisation import require_login +from uploader.population.models import populations_by_species from uploader.db_utils import with_db_connection, database_connection -from uploader.db import species, species_by_id, populations_by_species from uploader.species.models import species_by_id, all_species as species from . import jobs diff --git a/uploader/expression_data/rqtl2.py b/uploader/expression_data/rqtl2.py index 975fc1f..a855699 100644 --- a/uploader/expression_data/rqtl2.py +++ b/uploader/expression_data/rqtl2.py @@ -36,11 +36,9 @@ from uploader.authorisation import require_login from uploader.db.platforms import platform_by_id, platforms_by_species from uploader.db.averaging import averaging_methods, averaging_method_by_id from uploader.db.tissues import all_tissues, tissue_by_id, create_new_tissue -from uploader.db import ( - species_by_id, - save_population, - populations_by_species, - population_by_species_and_id,) +from uploader.population.models import (save_population, + populations_by_species, + population_by_species_and_id) from uploader.species.models import species_by_id from uploader.db.datasets import ( geno_dataset_by_id, diff --git a/uploader/expression_data/samples.py b/uploader/expression_data/samples.py index 0715d14..d430aa9 100644 --- a/uploader/expression_data/samples.py +++ b/uploader/expression_data/samples.py @@ -28,13 +28,10 @@ from uploader.db_utils import ( with_db_connection, database_connection, with_redis_connection) -from uploader.db import ( - species_by_id, - save_population, - population_by_id, - populations_by_species, - species as fetch_species) from uploader.species.models import species_by_id, all_species as fetch_species +from uploader.population.models import(save_population, + population_by_id, + populations_by_species) samples = Blueprint("samples", __name__) diff --git a/uploader/population/__init__.py b/uploader/population/__init__.py new file mode 100644 index 0000000..bf6bf3c --- /dev/null +++ b/uploader/population/__init__.py @@ -0,0 +1,3 @@ +"""Package to handle creation and management of Populations/InbredSets""" + +from .views import popbp diff --git a/uploader/db/populations.py b/uploader/population/models.py index 4485e52..4485e52 100644 --- a/uploader/db/populations.py +++ b/uploader/population/models.py diff --git a/uploader/population/views.py b/uploader/population/views.py new file mode 100644 index 0000000..cd5e20b --- /dev/null +++ b/uploader/population/views.py @@ -0,0 +1,120 @@ +"""Views dealing with populations/inbredsets""" +from flask import (flash, + request, + url_for, + redirect, + Blueprint, + current_app as app) + +from uploader.ui import make_template_renderer +from uploader.authorisation import require_login +from uploader.db_utils import database_connection +from uploader.species.models import (all_species, + species_by_id, + order_species_by_family) + +from .models import (save_population, + populations_by_species) + +__active_link__ = "populations" +popbp = Blueprint("populations", __name__) +render_template = make_template_renderer("populations") + + +@popbp.route("/", methods=["GET", "POST"]) +@require_login +def index(): + """Entry point for populations.""" + with database_connection(app.config["SQL_URI"]) as conn: + if not bool(request.args.get("species_id")): + return render_template( + "populations/index.html", + species=order_species_by_family(all_species(conn))) + species = species_by_id(conn, request.args.get("species_id")) + if not bool(species): + flash("Invalid species identifier provided!", "alert-danger") + return redirect(url_for("populations.index")) + return redirect(url_for("populations.list_species_populations", + species_id=species["SpeciesId"])) + +@popbp.route("/<int:species_id>", methods=["GET"]) +@require_login +def list_species_populations(species_id: int): + """List a particular species' populations.""" + with database_connection(app.config["SQL_URI"]) as conn: + species = species_by_id(conn, species_id) + if not bool(species): + flash("No species was found for given ID.", "alert-danger") + return redirect(url_for("populations.index")) + return render_template( + "populations/list-populations.html", + species=species, + populations=populations_by_species(conn, species_id), + activelink="list-populations") + + +def valid_population_name(population_name) -> bool: + """Check whether the given name is a valid population name.""" + raise NotImplementedError("Please implement this…") + + +@popbp.route("/<int:species_id>/create-population/", methods=["GET", "POST"]) +@require_login +def create_population(species_id: int): + """Create a new population.""" + with database_connection(app.config["SQL_URI"]) as conn: + species = species_by_id(conn, species_id) + + if request.method == "GET": + return render_template( + "populations/create-population.html", + species=species, + continue_to=request.args.get("continue_uri")) + + error = False + + if not bool(species): + flash("You must select a species.", "alert-danger") + error = True + + population_name = request.form.get("population_name", "").strip() + if not bool(population_name): + flash("You must provide a name for the population!", "alert-danger") + error = True + + if not valid_population_name(population_name): + flash("The population name can only contain letters, numbers, " + "hyphens and underscores.", + "alert-danger") + error = True + + population_fullname = request.form.get("inbredset_fullname", "").strip() + if not bool(population_fullname): + flash("You MUST provide a Full Name for the population." + "alert-danger") + error = True + + if error: + return redirect(url_for("populations.create_population", + **dict(request.args))) + + new_population = save_population(conn, { + "SpeciesId": species["SpeciesId"], + "Name": population_name, + "InbredSetName": population_fullname, + "FullName": population_fullname, + "Family": request.form.get("inbredset_family") or None, + "Description": request.form.get("description") or None + }) + + return redirect(url_for("populations.view_population", + species_id=species["SpeciesId"], + population_id=new_population["InbredSetId"])) + + +@popbp.route("/<int:species_id>/populations/<int:population_id>", + methods=["GET"]) +@require_login +def view_population(species_id: int, population_id: int): + """View the details of a population.""" + raise NotImplementedError("Please implement this too …") diff --git a/uploader/static/css/styles.css b/uploader/static/css/styles.css index 4aa776e..6d95fe6 100644 --- a/uploader/static/css/styles.css +++ b/uploader/static/css/styles.css @@ -7,7 +7,7 @@ body { grid-gap: 20px; font-family: Georgia, Garamond, serif; - font-style: normal;. + font-style: normal; } #header { diff --git a/uploader/templates/base.html b/uploader/templates/base.html index 7431767..fd6010b 100644 --- a/uploader/templates/base.html +++ b/uploader/templates/base.html @@ -45,6 +45,7 @@ <ul class="nav flex-column"> <li><a href="/" >Home</a></li> <li><a href="{{url_for('species.list_species')}}" >Species</a></li> + <li><a href="{{url_for('populations.index')}}">Populations</a></li> <li><a href="{{url_for('expression-data.index.index')}}" >Expression Data</a></li> </ul> </aside> diff --git a/uploader/templates/populations/base.html b/uploader/templates/populations/base.html new file mode 100644 index 0000000..1033927 --- /dev/null +++ b/uploader/templates/populations/base.html @@ -0,0 +1,12 @@ +{%extends "base.html"%} + +{%block breadcrumbs%} +<li {%if activelink=="populations"%} + class="breadcrumb-item active" + {%else%} + class="breadcrumb-item" + {%endif%}> + <a href="{{url_for('populations.index')}}">Populations</a> +</li> +{%block lvl3_breadcrumbs%}{%endblock%} +{%endblock%} diff --git a/uploader/templates/populations/index.html b/uploader/templates/populations/index.html new file mode 100644 index 0000000..14cf547 --- /dev/null +++ b/uploader/templates/populations/index.html @@ -0,0 +1,16 @@ +{%extends "populations/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "species/macro-select-species.html" import select_species_form%} + +{%block title%}Populations{%endblock%} + +{%block pagetitle%}Populations{%endblock%} + + +{%block contents%} +<div class="row"> + To continue, you need to select the species: + + {{select_species_form(url_for("populations.index"), species)}} +</div> +{%endblock%} diff --git a/uploader/templates/populations/list-populations.html b/uploader/templates/populations/list-populations.html new file mode 100644 index 0000000..bf6cf98 --- /dev/null +++ b/uploader/templates/populations/list-populations.html @@ -0,0 +1,87 @@ +{%extends "populations/base.html"%} +{%from "flash_messages.html" import flash_all_messages%} +{%from "species/macro-select-species.html" import select_species_form%} + +{%block title%}Populations{%endblock%} + +{%block pagetitle%}Populations{%endblock%} + +{%block lvl3_breadcrumbs%} +<li {%if activelink=="list-populations"%} + class="breadcrumb-item active" + {%else%} + class="breadcrumb-item" + {%endif%}> + <a href="{{url_for('populations.list_species_populations', + species_id=species.SpeciesId)}}">List</a> +</li> +{%endblock%} + + +{%block contents%} +<div class="row"> + <p> + The following populations/groups exist for the '{{species.FullName}}' + species. + </p> + <p> + Click on the population's name to select and continue using the population. + </p> +</div> + +<div class="row"> + <p> + If the population you need for the species '{{species.FullName}}' does not + exist, click on the "Create Population" button below to create a new one. + </p> + <p> + <a href="{{url_for('populations.create_population', + species_id=species.SpeciesId)}}" + title="Create a new population for species '{{species.FullName}}'." + class="btn btn-danger"> + Create Population + </a> + </p> +</div> + +<div class="row"> + <table class="table"> + <caption>Populations for {{species.FullName}}</caption> + <thead> + <tr> + <th>Name</th> + <th>Full Name</th> + <th>Description</th> + </tr> + </thead> + + <tbody> + {%for population in populations%} + <tr> + <td> + <a href="#{{population.InbredSetId}}" + title="Population '{{population.FullName}}' for species '{{species.FullName}}'."> + {{population.Name}} + </a> + </td> + <td>{{population.FullName}}</td> + <td>{{population.Description}}</td> + </tr> + {%else%} + <tr> + <td colspan="3"> + <p class="text-danger"> + <span class="glyphicon glyphicon-exclamation-mark"></span> + There were no populations found for {{species.FullName}}! + </p> + </td> + </tr> + {%endfor%} + </tbody> + </table> +</div> +{%endblock%} + +{%block sidebarcontents%} +… maybe provide species details here, perhaps? … +{%endblock%} diff --git a/uploader/templates/species/view-species.html b/uploader/templates/species/view-species.html index a0eb54b..0a3b0dd 100644 --- a/uploader/templates/species/view-species.html +++ b/uploader/templates/species/view-species.html @@ -60,7 +60,8 @@ title="Upload genotypes for {{species.FullName}}">Upload Genotypes</a> </li> <li> - <a href="#" + <a href="{{url_for('species.populations.list_species_populations', + species_id=species.SpeciesId)}}" title="Create/Edit populations for {{species.FullName}}"> Manage populations</a> </li> |