From ee9558a1bb0717a58008195c13b5ebc7d24fcf0a Mon Sep 17 00:00:00 2001 From: coolneng Date: Fri, 10 Jan 2020 23:29:23 +0100 Subject: [PATCH] Add plotting functionality --- code/Pipfile | 1 + code/Pipfile.lock | 77 ++++++++++++++++++++++++- code/app/forms.py | 22 +------ code/app/routes.py | 37 ++++++++---- code/app/static/plot.png | Bin 0 -> 16863 bytes code/app/templates/data.html | 4 +- code/app/templates/index.html | 5 +- code/app/templates/plot.html | 8 +++ code/app/templates/plot_selection.html | 12 ++++ code/database/queries.py | 13 +++++ code/igdb.py | 2 +- code/processing/dataframe.py | 45 +++++++++++++++ code/processing/tabulate.py | 19 ------ 13 files changed, 190 insertions(+), 55 deletions(-) create mode 100644 code/app/static/plot.png create mode 100644 code/app/templates/plot.html create mode 100644 code/app/templates/plot_selection.html create mode 100644 code/processing/dataframe.py delete mode 100644 code/processing/tabulate.py diff --git a/code/Pipfile b/code/Pipfile index f8644d3..294f719 100644 --- a/code/Pipfile +++ b/code/Pipfile @@ -14,6 +14,7 @@ iso3166 = "*" flask-wtf = "*" flask-login = "*" flask-bootstrap = "*" +matplotlib = "*" [requires] python_version = "3.8" diff --git a/code/Pipfile.lock b/code/Pipfile.lock index 484caa2..cd43be1 100644 --- a/code/Pipfile.lock +++ b/code/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b25fa5cfde97f34d3f4210b7c3e602df640d50105eb290daefa14c194b289936" + "sha256": "80f6c409aa1104974ff405c48d5527140030fb7e8d6bbf8aa21e884b3d151b5a" }, "pipfile-spec": 6, "requires": { @@ -23,6 +23,13 @@ ], "version": "==7.0" }, + "cycler": { + "hashes": [ + "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d", + "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8" + ], + "version": "==0.10.0" + }, "dominate": { "hashes": [ "sha256:6e833aea505f0236a9fc692326bac575f8bd38ae0f3a1bdc73d20ca606ac75d5", @@ -90,6 +97,48 @@ ], "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": { "hashes": [ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", @@ -123,6 +172,25 @@ ], "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": { "hashes": [ "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", @@ -182,6 +250,13 @@ "index": "pypi", "version": "==0.9.3" }, + "pyparsing": { + "hashes": [ + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + ], + "version": "==2.4.6" + }, "python-dateutil": { "hashes": [ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", diff --git a/code/app/forms.py b/code/app/forms.py index 5946cc3..b081359 100644 --- a/code/app/forms.py +++ b/code/app/forms.py @@ -10,7 +10,7 @@ class LoginForm(FlaskForm): submit = SubmitField("Sign In") -class YearForm(FlaskForm): +class AnnualForm(FlaskForm): year_list = [ ("2011", 2011), ("2012", 2012), @@ -26,22 +26,6 @@ class YearForm(FlaskForm): submit = SubmitField("Search") -class IntervalForm(FlaskForm): - year_list = [ - ("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 - ) +class PlotForm(FlaskForm): + name = StringField("Glacier Name", validators=[DataRequired()]) submit = SubmitField("Search") diff --git a/code/app/routes.py b/code/app/routes.py index 0b38a37..69fd314 100644 --- a/code/app/routes.py +++ b/code/app/routes.py @@ -1,11 +1,10 @@ -from app import app, db -from app.forms import LoginForm, YearForm, IntervalForm -from app.models import User, Annual_Data, Glacier -from flask import flash, redirect, render_template, url_for, request -from flask_login import current_user, login_user, logout_user, login_required +from app import app +from app.forms import AnnualForm, LoginForm, PlotForm +from database.queries import query_annual_data, query_plot_data, query_user +from flask import flash, redirect, render_template, request, url_for, send_file +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 processing.tabulate import create_table -from database.queries import query_annual_data, query_user @app.route("/") @@ -57,7 +56,7 @@ def data(): @app.route("/table_selection", methods=["GET", "POST"]) def table_selection(): - form = YearForm() + form = AnnualForm() if form.validate_on_submit(): query = query_annual_data(form) table = create_table(query.statement) @@ -70,6 +69,22 @@ def table(): return render_template("table.html", table=table, title="Table") -@app.route("/plots") -def plots(): - return render_template("data.html", title="Data") +@app.route("/plot_selection", methods=["GET", "POST"]) +def plot_selection(): + 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") diff --git a/code/app/static/plot.png b/code/app/static/plot.png new file mode 100644 index 0000000000000000000000000000000000000000..549d240a71e4f4eada6b4afb31d1da631ca8812b GIT binary patch literal 16863 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 literal 0 HcmV?d00001 diff --git a/code/app/templates/data.html b/code/app/templates/data.html index ce3d756..0674660 100644 --- a/code/app/templates/data.html +++ b/code/app/templates/data.html @@ -4,12 +4,12 @@

What do you want to check out?

  • - Tables + Tables

  • - Plots + Plots

  • {% endblock %} diff --git a/code/app/templates/index.html b/code/app/templates/index.html index 3fb9939..f02eae1 100644 --- a/code/app/templates/index.html +++ b/code/app/templates/index.html @@ -2,7 +2,8 @@ {% block content %}
    -

    IGDB: Internation Glacier Database

    -

    The IGDB is a database, that uses data from the WGMS to illustrate the consequences of climate change.

    +

    IGDB: Internation Glacier Database

    +

    The IGDB is a database, that uses data from the WGMS to illustrate the consequences of climate change.

    +

    Our system allows you to visualize data with tables and plots, via our intuitive Web UI.

    {% endblock %} diff --git a/code/app/templates/plot.html b/code/app/templates/plot.html new file mode 100644 index 0000000..41cb64c --- /dev/null +++ b/code/app/templates/plot.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} +

    Plot

    +Image Placeholder +

    Back

    +{% endblock %} diff --git a/code/app/templates/plot_selection.html b/code/app/templates/plot_selection.html new file mode 100644 index 0000000..2ed5a35 --- /dev/null +++ b/code/app/templates/plot_selection.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} +

    Plot selection

    +
    +
    + {{ wtf.quick_form(form) }} +
    +
    +
    +{% endblock %} diff --git a/code/database/queries.py b/code/database/queries.py index 80b3159..e03df0e 100644 --- a/code/database/queries.py +++ b/code/database/queries.py @@ -23,3 +23,16 @@ def query_annual_data(form) -> BaseQuery: def query_user(form) -> BaseQuery: user = User.query.filter_by(username=form.username.data).first() 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 diff --git a/code/igdb.py b/code/igdb.py index bd0b550..6067591 100644 --- a/code/igdb.py +++ b/code/igdb.py @@ -1,6 +1,6 @@ from app import app from database import db_setup, export, parser -from processing import tabulate +from processing import dataframe db_setup.main() parser.main() diff --git a/code/processing/dataframe.py b/code/processing/dataframe.py new file mode 100644 index 0000000..a799df0 --- /dev/null +++ b/code/processing/dataframe.py @@ -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 diff --git a/code/processing/tabulate.py b/code/processing/tabulate.py deleted file mode 100644 index 275a9db..0000000 --- a/code/processing/tabulate.py +++ /dev/null @@ -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