diff --git a/src/naturerec_model/logic/__init__.py b/src/naturerec_model/logic/__init__.py index 332291f..40facb6 100644 --- a/src/naturerec_model/logic/__init__.py +++ b/src/naturerec_model/logic/__init__.py @@ -7,7 +7,8 @@ from .species_status_ratings import create_species_status_rating, get_species_status_rating, \ list_species_status_ratings, close_species_status_rating from .job_statuses import create_job_status, complete_job_status, list_job_status -from .reports import location_individuals_report, location_days_report, save_report_barchart +from .reports import location_individuals_report, location_days_report, save_report_barchart, \ + get_report_barchart_base64 __all__ = [ @@ -44,5 +45,6 @@ "list_job_status", "location_individuals_report", "location_days_report", - "save_report_barchart" + "save_report_barchart", + "get_report_barchart_base64" ] diff --git a/src/naturerec_model/logic/reports.py b/src/naturerec_model/logic/reports.py index 7b48e20..22a25cc 100644 --- a/src/naturerec_model/logic/reports.py +++ b/src/naturerec_model/logic/reports.py @@ -3,10 +3,20 @@ """ import datetime +import base64 +import uuid +import tempfile +import os import pandas as pd -import matplotlib.pyplot as plt from ..model import Engine, Sighting +# Need to do this to prevent it from attempting to start the Matplotlib GUI +import matplotlib + +matplotlib.use('Agg') + +import matplotlib.pyplot as plt + def location_individuals_report(from_date, location_id, category_id, to_date=None): """ @@ -74,7 +84,7 @@ def location_days_report(from_date, location_id, category_id, to_date=None): def save_report_barchart(report_df, y_column_name, x_label, y_label, title, image_path, x_column_name=None): """ - Export a PNG image containing the data for a report + Export a PNG image containing a report barchart :param report_df: Report dataframe :param y_column_name: Name of the column containing the Y-axis values @@ -106,3 +116,33 @@ def save_report_barchart(report_df, y_column_name, x_label, y_label, title, imag # Save to the specified file in PNG format plt.savefig(image_path, format='png', dpi=300) + + +def get_image_base64(image_path): + """ + Given the path to an image file, read it and return the base-64 representation of its contents + + :param image_path: Path to the image file to read + :return: Base-64 representation of the image + """ + with open(image_path, mode="rb") as f: + return base64.b64encode(f.read()) + + +def get_report_barchart_base64(report_df, y_column_name, x_label, y_label, title, x_column_name=None): + """ + Return the base-64 representation of a PNG image containing a report barchart + + :param report_df: Report dataframe + :param y_column_name: Name of the column containing the Y-axis values + :param x_label: X-axis label + :param y_label: Y-axis label + :param title: TItle + :param x_column_name: Name of the column containing the X-axis labels or None to use the index + :return: String containing the Base-64 representation of the barchart + """ + image_path = os.path.join(tempfile.gettempdir(), f"{str(uuid.uuid4())}.png") + save_report_barchart(report_df, y_column_name, x_label, y_label, title, image_path, x_column_name) + barchart_base64 = get_image_base64(image_path).decode("utf-8") + os.unlink(image_path) + return barchart_base64 diff --git a/src/naturerec_web/reports/reports_blueprint.py b/src/naturerec_web/reports/reports_blueprint.py index 59c91ce..7a276be 100644 --- a/src/naturerec_web/reports/reports_blueprint.py +++ b/src/naturerec_web/reports/reports_blueprint.py @@ -3,21 +3,22 @@ """ from flask import Blueprint, render_template, request -from naturerec_model.logic import list_locations -from naturerec_model.logic import list_categories -from naturerec_model.logic import location_individuals_report, location_days_report +from naturerec_model.logic import list_locations, get_location +from naturerec_model.logic import list_categories, get_category +from naturerec_model.logic import location_individuals_report, location_days_report, get_report_barchart_base64 from naturerec_model.model import Sighting from naturerec_web.request_utils import get_posted_date, get_posted_int reports_bp = Blueprint("reports", __name__, template_folder='templates') -def _render_location_report_page(title, report_generator=None, from_date=None, to_date=None, location_id=None, - category_id=None): +def _render_location_report_page(title, y_label=None, report_generator=None, from_date=None, to_date=None, + location_id=None, category_id=None): """ Helper to show a location-based reporting page :param title: Title of the report + :param y_lable: Y-axis label of the report barchart :param report_generator: Report generator method :param from_date: From date for the reporting period :param to_date: To date for the reporting period @@ -29,20 +30,34 @@ def _render_location_report_page(title, report_generator=None, from_date=None, t to_date_string = to_date.strftime(Sighting.DATE_DISPLAY_FORMAT) if to_date else "" if report_generator and from_date and location_id and category_id: - report = report_generator(from_date=from_date, to_date=to_date, location_id=location_id, - category_id=category_id) + # Generate the report + report_df = report_generator(from_date=from_date, to_date=to_date, location_id=location_id, + category_id=category_id) + + # Create a barchart from the report + barchart_base64 = get_report_barchart_base64(report_df, "Count", "Species", y_label, title, None) + + # Get the location and category details + location = get_location(location_id) + category = get_category(category_id) else: - report = None + report_df = None + barchart_base64 = None + location = None + category = None return render_template("reports/location_report.html", title=title, locations=list_locations(), categories=list_categories(), category_id=category_id, + category=category, location_id=location_id, + location=location, from_date=from_date_string, to_date=to_date_string, - report=report) + report=report_df, + chart=barchart_base64) @reports_bp.route("/location/individuals", methods=["GET", "POST"]) @@ -59,8 +74,8 @@ def individuals_by_species_and_location(): to_date = get_posted_date("to_date") location_id = get_posted_int("location") category_id = get_posted_int("category") - return _render_location_report_page(title, location_individuals_report, from_date, to_date, location_id, - category_id) + return _render_location_report_page(title, "Individuals", location_individuals_report, from_date, to_date, + location_id, category_id) else: return _render_location_report_page(title) @@ -78,6 +93,7 @@ def sightings_by_species_and_location(): to_date = get_posted_date("to_date") location_id = get_posted_int("location") category_id = get_posted_int("category") - return _render_location_report_page(title, location_days_report, from_date, to_date, location_id, category_id) + return _render_location_report_page(title, "Sightings", location_days_report, from_date, to_date, location_id, + category_id) else: return _render_location_report_page(title) diff --git a/src/naturerec_web/reports/templates/reports/location_report.html b/src/naturerec_web/reports/templates/reports/location_report.html index 700324d..cb369d5 100644 --- a/src/naturerec_web/reports/templates/reports/location_report.html +++ b/src/naturerec_web/reports/templates/reports/location_report.html @@ -5,21 +5,23 @@ {% set index_column_name = "Species" %} {% block content %} -
{% endif %} - {% endblock %} diff --git a/src/naturerec_web/reports/templates/reports/report.html b/src/naturerec_web/reports/templates/reports/report.html index 8efd05c..4656771 100644 --- a/src/naturerec_web/reports/templates/reports/report.html +++ b/src/naturerec_web/reports/templates/reports/report.html @@ -1,4 +1,8 @@ {% if report.index | length > 0 %} +