Add plotting functionality

This commit is contained in:
coolneng 2020-01-10 23:29:23 +01:00
parent 6baa8779ca
commit ee9558a1bb
Signed by: coolneng
GPG Key ID: 9893DA236405AF57
13 changed files with 190 additions and 55 deletions

View File

@ -14,6 +14,7 @@ iso3166 = "*"
flask-wtf = "*" flask-wtf = "*"
flask-login = "*" flask-login = "*"
flask-bootstrap = "*" flask-bootstrap = "*"
matplotlib = "*"
[requires] [requires]
python_version = "3.8" python_version = "3.8"

77
code/Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "b25fa5cfde97f34d3f4210b7c3e602df640d50105eb290daefa14c194b289936" "sha256": "80f6c409aa1104974ff405c48d5527140030fb7e8d6bbf8aa21e884b3d151b5a"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -23,6 +23,13 @@
], ],
"version": "==7.0" "version": "==7.0"
}, },
"cycler": {
"hashes": [
"sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d",
"sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"
],
"version": "==0.10.0"
},
"dominate": { "dominate": {
"hashes": [ "hashes": [
"sha256:6e833aea505f0236a9fc692326bac575f8bd38ae0f3a1bdc73d20ca606ac75d5", "sha256:6e833aea505f0236a9fc692326bac575f8bd38ae0f3a1bdc73d20ca606ac75d5",
@ -90,6 +97,48 @@
], ],
"version": "==2.10.3" "version": "==2.10.3"
}, },
"kiwisolver": {
"hashes": [
"sha256:05b5b061e09f60f56244adc885c4a7867da25ca387376b02c1efc29cc16bcd0f",
"sha256:210d8c39d01758d76c2b9a693567e1657ec661229bc32eac30761fa79b2474b0",
"sha256:26f4fbd6f5e1dabff70a9ba0d2c4bd30761086454aa30dddc5b52764ee4852b7",
"sha256:3b15d56a9cd40c52d7ab763ff0bc700edbb4e1a298dc43715ecccd605002cf11",
"sha256:3b2378ad387f49cbb328205bda569b9f87288d6bc1bf4cd683c34523a2341efe",
"sha256:400599c0fe58d21522cae0e8b22318e09d9729451b17ee61ba8e1e7c0346565c",
"sha256:47b8cb81a7d18dbaf4fed6a61c3cecdb5adec7b4ac292bddb0d016d57e8507d5",
"sha256:53eaed412477c836e1b9522c19858a8557d6e595077830146182225613b11a75",
"sha256:58e626e1f7dfbb620d08d457325a4cdac65d1809680009f46bf41eaf74ad0187",
"sha256:5a52e1b006bfa5be04fe4debbcdd2688432a9af4b207a3f429c74ad625022641",
"sha256:5c7ca4e449ac9f99b3b9d4693debb1d6d237d1542dd6a56b3305fe8a9620f883",
"sha256:682e54f0ce8f45981878756d7203fd01e188cc6c8b2c5e2cf03675390b4534d5",
"sha256:76275ee077772c8dde04fb6c5bc24b91af1bb3e7f4816fd1852f1495a64dad93",
"sha256:79bfb2f0bd7cbf9ea256612c9523367e5ec51d7cd616ae20ca2c90f575d839a2",
"sha256:7f4dd50874177d2bb060d74769210f3bce1af87a8c7cf5b37d032ebf94f0aca3",
"sha256:8944a16020c07b682df861207b7e0efcd2f46c7488619cb55f65882279119389",
"sha256:8aa7009437640beb2768bfd06da049bad0df85f47ff18426261acecd1cf00897",
"sha256:9105ce82dcc32c73eb53a04c869b6a4bc756b43e4385f76ea7943e827f529e4d",
"sha256:933df612c453928f1c6faa9236161a1d999a26cd40abf1dc5d7ebbc6dbfb8fca",
"sha256:939f36f21a8c571686eb491acfffa9c7f1ac345087281b412d63ea39ca14ec4a",
"sha256:9491578147849b93e70d7c1d23cb1229458f71fc79c51d52dce0809b2ca44eea",
"sha256:9733b7f64bd9f807832d673355f79703f81f0b3e52bfce420fc00d8cb28c6a6c",
"sha256:a02f6c3e229d0b7220bd74600e9351e18bc0c361b05f29adae0d10599ae0e326",
"sha256:a0c0a9f06872330d0dd31b45607197caab3c22777600e88031bfe66799e70bb0",
"sha256:aa716b9122307c50686356cfb47bfbc66541868078d0c801341df31dca1232a9",
"sha256:acc4df99308111585121db217681f1ce0eecb48d3a828a2f9bbf9773f4937e9e",
"sha256:b64916959e4ae0ac78af7c3e8cef4becee0c0e9694ad477b4c6b3a536de6a544",
"sha256:d22702cadb86b6fcba0e6b907d9f84a312db9cd6934ee728144ce3018e715ee1",
"sha256:d3fcf0819dc3fea58be1fd1ca390851bdb719a549850e708ed858503ff25d995",
"sha256:d52e3b1868a4e8fd18b5cb15055c76820df514e26aa84cc02f593d99fef6707f",
"sha256:db1a5d3cc4ae943d674718d6c47d2d82488ddd94b93b9e12d24aabdbfe48caee",
"sha256:e3a21a720791712ed721c7b95d433e036134de6f18c77dbe96119eaf7aa08004",
"sha256:e8bf074363ce2babeb4764d94f8e65efd22e6a7c74860a4f05a6947afc020ff2",
"sha256:f16814a4a96dc04bf1da7d53ee8d5b1d6decfc1a92a63349bb15d37b6a263dd9",
"sha256:f2b22153870ca5cf2ab9c940d7bc38e8e9089fa0f7e5856ea195e1cf4ff43d5a",
"sha256:f790f8b3dff3d53453de6a7b7ddd173d2e020fb160baff578d578065b108a05f",
"sha256:fe51b79da0062f8e9d49ed0182a626a7dc7a0cbca0328f612c6ee5e4711c81e4"
],
"version": "==1.1.0"
},
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
@ -123,6 +172,25 @@
], ],
"version": "==1.1.1" "version": "==1.1.1"
}, },
"matplotlib": {
"hashes": [
"sha256:08ccc8922eb4792b91c652d3e6d46b1c99073f1284d1b6705155643e8046463a",
"sha256:161dcd807c0c3232f4dcd4a12a382d52004a498174cbfafd40646106c5bcdcc8",
"sha256:1f9e885bfa1b148d16f82a6672d043ecf11197f6c71ae222d0546db706e52eb2",
"sha256:2d6ab54015a7c0d727c33e36f85f5c5e4172059efdd067f7527f6e5d16ad01aa",
"sha256:5d2e408a2813abf664bd79431107543ecb449136912eb55bb312317edecf597e",
"sha256:61c8b740a008218eb604de518eb411c4953db0cb725dd0b32adf8a81771cab9e",
"sha256:80f10af8378fccc136da40ea6aa4a920767476cdfb3241acb93ef4f0465dbf57",
"sha256:819d4860315468b482f38f1afe45a5437f60f03eaede495d5ff89f2eeac89500",
"sha256:8cc0e44905c2c8fda5637cad6f311eb9517017515a034247ab93d0cf99f8bb7a",
"sha256:8e8e2c2fe3d873108735c6ee9884e6f36f467df4a143136209cff303b183bada",
"sha256:98c2ffeab8b79a4e3a0af5dd9939f92980eb6e3fec10f7f313df5f35a84dacab",
"sha256:d59bb0e82002ac49f4152963f8a1079e66794a4f454457fd2f0dcc7bf0797d30",
"sha256:ee59b7bb9eb75932fe3787e54e61c99b628155b0cedc907864f24723ba55b309"
],
"index": "pypi",
"version": "==3.1.2"
},
"numpy": { "numpy": {
"hashes": [ "hashes": [
"sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6",
@ -182,6 +250,13 @@
"index": "pypi", "index": "pypi",
"version": "==0.9.3" "version": "==0.9.3"
}, },
"pyparsing": {
"hashes": [
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
],
"version": "==2.4.6"
},
"python-dateutil": { "python-dateutil": {
"hashes": [ "hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",

View File

@ -10,7 +10,7 @@ class LoginForm(FlaskForm):
submit = SubmitField("Sign In") submit = SubmitField("Sign In")
class YearForm(FlaskForm): class AnnualForm(FlaskForm):
year_list = [ year_list = [
("2011", 2011), ("2011", 2011),
("2012", 2012), ("2012", 2012),
@ -26,22 +26,6 @@ class YearForm(FlaskForm):
submit = SubmitField("Search") submit = SubmitField("Search")
class IntervalForm(FlaskForm): class PlotForm(FlaskForm):
year_list = [ name = StringField("Glacier Name", validators=[DataRequired()])
("2011", 2011),
("2012", 2012),
("2013", 2013),
("2014", 2014),
("2015", 2015),
("2016", 2016),
("2017", 2017),
("2018", 2018),
]
name = StringField("Glacier Name")
lower_bound = SelectField(
"First year", validators=[DataRequired()], choices=year_list
)
upper_bound = SelectField(
"Second year", validators=[DataRequired()], choices=year_list
)
submit = SubmitField("Search") submit = SubmitField("Search")

View File

@ -1,11 +1,10 @@
from app import app, db from app import app
from app.forms import LoginForm, YearForm, IntervalForm from app.forms import AnnualForm, LoginForm, PlotForm
from app.models import User, Annual_Data, Glacier from database.queries import query_annual_data, query_plot_data, query_user
from flask import flash, redirect, render_template, url_for, request from flask import flash, redirect, render_template, request, url_for, send_file
from flask_login import current_user, login_user, logout_user, login_required from flask_login import current_user, login_required, login_user, logout_user
from processing.dataframe import create_table, create_plot
from werkzeug.urls import url_parse from werkzeug.urls import url_parse
from processing.tabulate import create_table
from database.queries import query_annual_data, query_user
@app.route("/") @app.route("/")
@ -57,7 +56,7 @@ def data():
@app.route("/table_selection", methods=["GET", "POST"]) @app.route("/table_selection", methods=["GET", "POST"])
def table_selection(): def table_selection():
form = YearForm() form = AnnualForm()
if form.validate_on_submit(): if form.validate_on_submit():
query = query_annual_data(form) query = query_annual_data(form)
table = create_table(query.statement) table = create_table(query.statement)
@ -70,6 +69,22 @@ def table():
return render_template("table.html", table=table, title="Table") return render_template("table.html", table=table, title="Table")
@app.route("/plots") @app.route("/plot_selection", methods=["GET", "POST"])
def plots(): def plot_selection():
return render_template("data.html", title="Data") form = PlotForm()
if form.validate_on_submit():
query = query_plot_data(form)
plot = create_plot(query.statement)
return render_template("plot.html", title="Plot", plot=plot)
return render_template("plot_selection.html", title="Data", form=form)
@app.route("/plot")
def plot():
return render_template("plot.html", title="Plot", plot=plot)
@app.route("/figure")
def figure(query):
fig = create_plot(query)
return send_file(fig, mimetype="image/png")

BIN
code/app/static/plot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -4,12 +4,12 @@
<h1 class="text-center">What do you want to check out?</h1> <h1 class="text-center">What do you want to check out?</h1>
<li> <li>
<p class="text-center"> <p class="text-center">
<a href="{{ url_for('table_selection') }}">Tables</a> <a href="{{ url_for('table_selection') }}" class="btn btn-success" role="button" aria-pressed="true">Tables</a>
</p> </p>
</li> </li>
<li> <li>
<p class="text-center"> <p class="text-center">
<a href="{{ url_for('plots') }}">Plots</a> <a href="{{ url_for('plot_selection') }}" class="btn btn-success" role="button" aria-pressed="true">Plots</a>
</p> </p>
</li> </li>
{% endblock %} {% endblock %}

View File

@ -2,7 +2,8 @@
{% block content %} {% block content %}
<div class="jumbotron"> <div class="jumbotron">
<h1 id="igdb-internation-glacier-database">IGDB: Internation Glacier Database</h1> <h1 id="igdb-internation-glacier-database">IGDB: Internation Glacier Database</h1>
<p>The IGDB is a database, that uses data from the <a href="http://dx.doi.org/10.5904/wgms-fog-2018-11">WGMS</a> to illustrate the consequences of climate change.</p> <p>The IGDB is a database, that uses data from the <a href="http://dx.doi.org/10.5904/wgms-fog-2018-11">WGMS</a> to illustrate the consequences of climate change.</p>
<p>Our system allows you to visualize data with tables and plots, via our intuitive Web UI.</p>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Plot</h1>
<img src="data:image/png;base64,{{ plot }}" alt="Image Placeholder">
<p><a href="{{ url_for('plot_selection') }}">Back</a></p>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Plot selection</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
<br>
{% endblock %}

View File

@ -23,3 +23,16 @@ def query_annual_data(form) -> BaseQuery:
def query_user(form) -> BaseQuery: def query_user(form) -> BaseQuery:
user = User.query.filter_by(username=form.username.data).first() user = User.query.filter_by(username=form.username.data).first()
return user return user
def query_plot_data(form) -> BaseQuery:
query = (
db.session.query(Annual_Data)
.join(Glacier, Glacier.id == Annual_Data.id)
.filter_by(name=form.name.data)
.group_by(Annual_Data.year)
)
if query.first() is None:
flash("Sorry, no results found")
return redirect(url_for("plot_selection"))
return query

View File

@ -1,6 +1,6 @@
from app import app from app import app
from database import db_setup, export, parser from database import db_setup, export, parser
from processing import tabulate from processing import dataframe
db_setup.main() db_setup.main()
parser.main() parser.main()

View File

@ -0,0 +1,45 @@
from app import db
from io import BytesIO
from pandas import DataFrame, read_sql
from base64 import b64encode
def create_dataframe(query) -> DataFrame:
df = read_sql(sql=query, con=db.engine)
return df
def render_table(df) -> str:
df.fillna(value=0, inplace=True)
table = df.to_html(classes=["table-striped", "table-hover"])
return table
def render_plot(df):
df.fillna(value=0, inplace=True)
plot = df.plot("year", ["surface", "length", "elevation"], kind="bar")
plot_figure = plot.get_figure()
figure = BytesIO()
plot_figure.savefig(figure)
figure.seek(0)
return figure
def encode_plot(plot):
buffer = b"".join(plot)
buf = b64encode(buffer)
encoded_plot = buf.decode("utf-8")
return encoded_plot
def create_table(query) -> str:
df = create_dataframe(query)
html_table = render_table(df)
return html_table
def create_plot(query):
df = create_dataframe(query)
plot = render_plot(df)
encoded_plot = encode_plot(plot)
return encoded_plot

View File

@ -1,19 +0,0 @@
from app import db
from pandas import DataFrame, read_sql
def create_dataframe(query) -> DataFrame:
df = read_sql(sql=query, con=db.engine)
return df
def render_table(df) -> str:
df.fillna(value=0, inplace=True)
table = df.to_html(classes=["table-striped", "table-hover"])
return table
def create_table(query) -> str:
df = create_dataframe(query)
html_table = render_table(df)
return html_table