about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2024-09-03 14:55:48 -0500
committerFrederick Muriuki Muriithi2024-09-03 16:51:26 -0500
commit61688c00e8734adee4d825571a9c43d926dca001 (patch)
tree0a53c7eff5ec2bb1376be2eb2bdd2333c606ba11
parent2876fe6251bd4dfceef06f638ee74c2728ad1207 (diff)
downloadgn-uploader-61688c00e8734adee4d825571a9c43d926dca001.tar.gz
Initialise the populations package and update references.
-rw-r--r--scripts/insert_samples.py3
-rw-r--r--uploader/__init__.py2
-rw-r--r--uploader/db/__init__.py6
-rw-r--r--uploader/dbinsert.py2
-rw-r--r--uploader/expression_data/rqtl2.py8
-rw-r--r--uploader/expression_data/samples.py9
-rw-r--r--uploader/population/__init__.py3
-rw-r--r--uploader/population/models.py (renamed from uploader/db/populations.py)0
-rw-r--r--uploader/population/views.py120
-rw-r--r--uploader/static/css/styles.css2
-rw-r--r--uploader/templates/base.html1
-rw-r--r--uploader/templates/populations/base.html12
-rw-r--r--uploader/templates/populations/index.html16
-rw-r--r--uploader/templates/populations/list-populations.html87
-rw-r--r--uploader/templates/species/view-species.html3
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%}
+&hellip; maybe provide species details here, perhaps? &hellip;
+{%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>