from flask import Blueprint, request, session, render_template, redirect, url_for, abort from user_agents import parse as user_agent from datetime import datetime from dateutil.parser import isoparse from .umbrellas import Umbrellas from .admin_log import AdminLog from .users import Users from .exceptions import * from .config import * from .utils import human_timedelta, CST bp = Blueprint("admin", __name__, url_prefix="/admin") db = Umbrellas(DATABASE_PATH) users = Users(DATABASE_PATH) admin_log = AdminLog(ADMIN_LOG_PATH) @bp.before_request def check_privilege(): # only clients who have obtained a session and sent it in the Cookie header # will have a decryptable username here if "username" not in session: return redirect(url_for("auth.auth", action="login")) username = session["username"] user = users.find(username) # under normal circumstances it must exist if user["role"] != "admin": abort(403) @bp.route("/") def index(): umbrellas = db.read() # count # of umbrellas in each status status_count = { status: len([u for u in umbrellas if u["status"] == status]) for status in ("available", "lent", "overdue") } return render_template( "admin/index.html", umbrellas=len(umbrellas), available=status_count["available"], lent=status_count["lent"], overdue=status_count["overdue"], mobile=user_agent(request.user_agent.string).is_mobile, ) @bp.route("/umbrellas") def umbrellas(): # sqlite3.Row does not support item assignment, but dict does umbrellas = [dict(umb) for umb in db.read()] edit = request.args.get("edit") if edit is not None: if not edit.isnumeric(): abort(400) edit = int(edit) for umb in umbrellas: if umb["id"] == edit and not umb["lent_at"]: # autofill current time when admin is editing and lent_at is empty umb["lent_at"] = datetime.now().isoformat(timespec="seconds") elif umb["lent_at"]: try: # web interface provides no timezone so UTC+8 is assumed lent_at = isoparse(umb["lent_at"]).replace(tzinfo=CST) umb["lent_at"] = lent_at.isoformat(timespec="seconds") umb["lent_time_ago"] = human_timedelta( datetime.now().astimezone(CST) - lent_at ) + " ago" except ValueError: umb["lent_at"] = "Invalid date" umb["lent_time_ago"] = "" error = request.args.get("error") template = ( "admin/umbrellas_mobile.html" if user_agent(request.user_agent.string).is_mobile else "admin/umbrellas_desktop.html" ) return render_template( template, umbrellas=umbrellas, edit=edit, error=error, ) @bp.route("/umbrellas/edit", methods=["POST"]) def umbrellas_edit(): data = {} for key in [ "id", "status", "tenant_name", "tenant_id", "tenant_phone", "lent_at", ]: data[key] = request.form.get(key) try: diff = db.update(data) except UmbrellaValueError as e: # invalid field is in `e.message`. return redirect( "{0}?edit={1}&error={2}".format( url_for("admin.umbrellas"), request.form.get("id"), e.message ) ) except UmbrellaNotFoundError: abort(400) for column, value_pair in diff.items(): past, new = value_pair admin_log.log( "ADMIN_MODIFY_DB", { "admin_name": session["username"], "id": data["id"], "column": column, "past_value": past, "new_value": new, }, ) return redirect(url_for("admin.umbrellas")) @bp.route("/logs") def logs(): logs = admin_log.read() return render_template("admin/logs.html", logs=logs)