Source code for ResearchNotes.report

# -*- coding: utf-8 -*-
"""
Report module.

All functions/views dealing with report. Defines the report blueprint
"""
import os
import shutil
import typing

from werkzeug.wrappers.response import Response

from flask import Blueprint, flash, g, redirect, render_template, url_for, current_app, abort

# from werkzeug.exceptions import HTTPException, abort

import ResearchNotes.database_transactions as dbt
from ResearchNotes.auth import login_required
from ResearchNotes.database import db, Measurements, Reports
from ResearchNotes.form import ReportCreateForm
from ResearchNotes.files import uploaddir_path, make_file_list
from ResearchNotes.url_security import token_decode

bp = Blueprint("report", __name__, url_prefix="/reports")


[docs]@bp.route("/<int:mid>/create", methods=("GET", "POST")) @login_required def create(mid: int) -> typing.Union[str, Response]: """ Create a report in a given PMM for the PMM id. Parameters ---------- mid : int PPM ID for which the report is created. Returns ------- str|Flask.Response Render create report template or redirect to report view. """ measurement = db.get_or_404(Measurements, mid) form = ReportCreateForm() if form.validate_on_submit(): new_report = { "creator_id": g.user.id, "title": form.title.data, "long_dis": form.long_dis.data, "creator": g.user.UserName, "sample_id": measurement.sample_id, "measurement_id": mid, } r_id = dbt.create_report(db, new_report) flash(f"Report {new_report['title']} created", "alert-info") return redirect(url_for("report.report_view", rid=r_id)) return render_template("report/create.html", form=form, measurement=measurement)
[docs]def get_report(rid: int, check_author: bool = True) -> Reports: """ Get report from database. Retrieve report ID from database. Check, if user has the right to access the database entry first Parameters ---------- rid : int Report ID in database. check_author : TYPE, optional DESCRIPTION. The default is True. Returns ------- report : ResearchNotes.Report Report entry into database. """ report = db.get_or_404( Reports, rid, description=f" : get_report : User {g.user} tried to load report {rid} which does not exist", ) if ( not current_app.config["ESS_PER_GROUP"] and int(report.reports_sample.creator_id) != g.user.id ) and g.user.id not in report.reports_sample.sharedsamplelist: abort( 403, description=f" : get_report : Authorization failure. User {g.user} tried to load report {rid}", ) elif ( current_app.config["ESS_PER_GROUP"] and int(report.reports_sample.group_id) != g.user.group_id and g.user.id not in report.reports_sample.sharedsamplelist ): abort( 403, description=f" : get_report : Authorization failure. User {g.user} tried to load report {rid}", ) return report
[docs]@bp.route("/<int:rid>/update", methods=("GET", "POST")) @login_required def update(rid: int) -> typing.Union[str, Response]: """ Update an existing report in the database. First retrieve it, then put the old values into the Form. Finally, write new values to the database and update modified date. Parameters ---------- rid : int Report ID. Returns ------- str|Flask.Response Render update form or redirect to report view. """ report = get_report(rid) form = ReportCreateForm(obj=report) if form.validate_on_submit(): dbt.update_report(db, report, {"title": form.title.data, "long_dis": form.long_dis.data}) flash("Report information updated", "alert-info") return redirect(url_for("report.report_view", rid=rid)) return render_template( "report/create.html", form=form, report=report, measurement=report.report_measurement )
[docs]@bp.route("/<int:rid>/report", methods=("GET", "POST")) @login_required def report_view(rid: int) -> str: """ Display report and associated files. Parameters ---------- rid : int Report ID Returns ------- str Renders report view template. """ report = get_report(rid) files = make_file_list( uploaddir_path(["r", report.id, report.sample_id, report.reports_sample.identifier]) ) return render_template( "report/report.html", report=report, files=files, )
def _delete_report(report) -> typing.Optional[Exception]: """ Delete a report entry - mainly used by sample.delete and measurement.delete. Sub-function to actually delete the report and all files associated with it. Parameters ---------- report : ResearchNote.Report Report object. Returns ------- error : None|str Returns None at success or the error code string (abort). """ error = None # type: None path = uploaddir_path(["r", report.id, report.sample_id, report.reports_sample.identifier]) if os.path.exists(path): try: shutil.rmtree(path) except shutil.Error as error: abort(500, description=f"Failed to delete {path}. Exception {error}") # with dbt.Transaction(db) as db_session: db_session.delete(report) return error
[docs]@bp.route("/delete/<string:token>") @login_required def delete(token: str) -> Response: """ Delete report entry. Unlike samples, we have don't have to implement a cascade of deletions and cross-references since there are no "one report to many" database relationships. Parameters ---------- token : str Signed string that encodes the id of the report to delete Returns ------- Flask.Response Redirect to measurement view (if _delete not does an abort). """ rid = token_decode(token, current_app.config["SEC_SESSION_KEY"], g.salt) report = get_report(rid) mid = report.measurement_id error = _delete_report(report) if error is None: flash(f"Deleted report {report.title}", "alert-info") return redirect(url_for("measurements.measurement_view", mid=mid))