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 .logger import Logger from .users import Users from .exceptions import * from .config import config from .utils import human_datetime, human_timedelta, CST bp = Blueprint("admin", __name__, url_prefix="/admin") db = Umbrellas(config.get("general", "db_path")) users = Users(config.get("general", "db_path")) logger = Logger(config.get("general", "db_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 = [dict(umb) for umb in db.read()] # sort umbrellas by their status statuses = { "available": [], "lent": [], "overdue": [], } for umb in umbrellas: if umb["status"] == "available": statuses["available"].append(umb) elif umb["status"] in ("lent", "overdue"): statuses[umb["status"]].append(umb) try: lent_at = isoparse(umb["lent_at"]) umb["lent_at"] = human_datetime(lent_at) umb["lent_time_ago"] = ( human_timedelta(datetime.now(tz=CST) - lent_at) + " ago" ) except ValueError: umb["lent_at"] = "Invalid date" umb["lent_time_ago"] = "Invalid date" return render_template( "admin/index.html", umbrellas=umbrellas, available=statuses["available"], lent=statuses["lent"], overdue=statuses["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(tz=CST).replace(tzinfo=None).isoformat(timespec="seconds") elif umb["lent_at"]: try: lent_at = isoparse(umb["lent_at"]) umb["lent_at"] = human_datetime(lent_at) umb["lent_time_ago"] = ( human_timedelta(datetime.now(tz=CST) - lent_at) + " ago" ) except ValueError: umb["lent_at"] = "Invalid date" umb["lent_time_ago"] = "Invalid date" 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: db.admin_modify(session["username"], 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) return redirect(url_for("admin.umbrellas")) @bp.route("/logs") def logs(): logs = logger.read_admin() return render_template("admin/admin_logs.html", logs=logs)