From 357cc3d8d9f2b02e60f0763f4701bd614b876436 Mon Sep 17 00:00:00 2001 From: Frederick Yin Date: Wed, 2 Feb 2022 11:16:23 +0800 Subject: Adapt web interface --- jimbrella/admin.py | 24 +++++++++-------- jimbrella/auth.py | 2 +- jimbrella/users.py | 79 ++++++++++++++++++++++-------------------------------- jimbrella/web.py | 2 +- 4 files changed, 47 insertions(+), 60 deletions(-) diff --git a/jimbrella/admin.py b/jimbrella/admin.py index 49cbf55..4454c69 100644 --- a/jimbrella/admin.py +++ b/jimbrella/admin.py @@ -1,14 +1,14 @@ from flask import Blueprint, request, session, render_template, redirect, url_for, abort from user_agents import parse as user_agent -from .database import Database +from .umbrellas import Umbrellas from .admin_log import AdminLog from .users import Users from .exceptions import * from .config import * bp = Blueprint("admin", __name__, url_prefix="/admin") -db = Database(DATABASE_PATH) -users = Users(USERS_PATH) +db = Umbrellas(DATABASE_PATH) +users = Users(DATABASE_PATH) admin_log = AdminLog(ADMIN_LOG_PATH) @@ -28,7 +28,11 @@ def check_privilege(): @bp.route("/") def index(): umbrellas = db.read() - statuses = Database.group_by_status(umbrellas) + # count # of umbrellas in each status + statuses = { + status: len([u for u in umbrellas if u["status"] == status]) + for status in ("available", "lent", "overdue") + } return render_template( "admin/index.html", umbrellas=umbrellas, @@ -52,7 +56,7 @@ def umbrellas(): return render_template( template, umbrellas=umbrellas, - edit=int(edit) if edit else None, + edit=int(edit) if edit.isnumeric() else None, error=error, ) @@ -61,13 +65,11 @@ def umbrellas(): def umbrellas_edit(): data = {} for key in [ - "serial", - "alias", + "id", "status", "tenant_name", "tenant_id", "tenant_phone", - "tenant_email", "lent_at", ]: data[key] = request.form.get(key) @@ -78,11 +80,11 @@ def umbrellas_edit(): # invalid field is in `e.message`. return redirect( "{0}?edit={1}&error={2}".format( - url_for("admin.umbrellas"), request.form.get("serial"), e.message + url_for("admin.umbrellas"), request.form.get("id"), e.message ) ) except UmbrellaNotFoundError: - pass # impossible on web console + abort(400) for column, value_pair in diff.items(): past, new = value_pair @@ -90,7 +92,7 @@ def umbrellas_edit(): "ADMIN_MODIFY_DB", { "admin_name": session["username"], - "serial": data["serial"], + "id": data["id"], "column": column, "past_value": past, "new_value": new, diff --git a/jimbrella/auth.py b/jimbrella/auth.py index 2d8b1cc..c7e2fa3 100644 --- a/jimbrella/auth.py +++ b/jimbrella/auth.py @@ -5,7 +5,7 @@ from .exceptions import UsernameTakenError from .config import * bp = Blueprint("auth", __name__, url_prefix="/") -users = Users(USERS_PATH) +users = Users(DATABASE_PATH) def show_error(action, message): diff --git a/jimbrella/users.py b/jimbrella/users.py index 4a6348e..641e360 100644 --- a/jimbrella/users.py +++ b/jimbrella/users.py @@ -1,16 +1,14 @@ -from .csv_table import CsvTable +import sqlite3 from .exceptions import UsernameTakenError -class Users(CsvTable): +class Users: """A table of users, including admins. A user must register through the interface provided by JImbrella to be one of the users in this table. Those who borrow umbrellas via jForm are not stored here. - The file format is csv. Each user takes one row. - Columns: - username | identifier Unicode string. must be unique. - password | salted and hashed password. @@ -21,61 +19,48 @@ class Users(CsvTable): - id | (optional) student/faculty ID. - phone | (optional) phone number. - email | (optional) email address. + + Schema: + CREATE TABLE Users( + username TEXT PRIMARY KEY, + password TEXT, + role TEXT, + status TEXT, + language TEXT, + realname TEXT, + id TEXT, + phone TEXT, + email TEXT + ); """ def __init__(self, path): self.path = path - super().__init__( - path, - [ - {"name": col} - for col in [ - "username", - "password", - "role", - "status", - "language", - "realname", - "id", - "phone", - "email", - ] - ], - ) - - def read(self): - """Alias for `_read.`""" - return self._read() def register(self, username, password, language): """Create a new user. If username is already taken, raise UsernameTakenError. """ - users = self._read() - usernames = [user["username"] for user in users] - if username in usernames: + db = sqlite3.connect(self.path) + try: + db.execute( + "INSERT INTO Users (username, password, role, status, language) " + + "VALUES (?, ?, ?, ?, ?)", + (username, password, "user", "normal", "en-US"), + ) + except sqlite3.IntegrityError: raise UsernameTakenError(username) - user = { - "username": username, - "password": password, - "role": "user", - "status": "normal", - "language": language, - "realname": "", - "id": "", - "phone": "", - "email": "", - } - self._append(user) + db.commit() + db.close() def find(self, username) -> dict: - """Find a user and return it in its `dict` structure.""" - - users = self._read() - for user in users: - if user["username"] == username: - return user + """Find a user and return it in its `dict` structure. If user is not found, return None.""" + db = sqlite3.connect(self.path) + db.row_factory = sqlite3.Row - return None + user = db.execute( + "SELECT * FROM Users WHERE username = ?", (username,) + ).fetchone() + return user diff --git a/jimbrella/web.py b/jimbrella/web.py index 33e059a..a2b66b8 100644 --- a/jimbrella/web.py +++ b/jimbrella/web.py @@ -5,7 +5,7 @@ from .auth import bp as auth_bp from .config import * # this logger is shared across modules -logging.basicConfig(filename=LOG_PATH, level=logging.DEBUG) +# logging.basicConfig(filename=LOG_PATH, level=logging.DEBUG) app = Flask("jimbrella") app.secret_key = FLASK_SECRET_KEY -- cgit v1.2.3