Add character page and create/edit workflows #18
|
@ -78,4 +78,6 @@ def get_in_lexicon(db: DbContext, lexicon_id: int) -> Sequence[Character]:
|
||||||
|
|
||||||
def try_from_public_id(db: DbContext, public_id: UUID) -> Optional[Character]:
|
def try_from_public_id(db: DbContext, public_id: UUID) -> Optional[Character]:
|
||||||
"""Get a character by its public id."""
|
"""Get a character by its public id."""
|
||||||
return db(select(Character).where(Character.public_id == public_id)).scalar_one_or_none()
|
return db(
|
||||||
|
select(Character).where(Character.public_id == public_id)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
|
|
@ -30,6 +30,7 @@ class Uuid(TypeDecorator):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
impl = CHAR(32)
|
impl = CHAR(32)
|
||||||
|
cache_ok = True
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
def process_bind_param(self, value, dialect):
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|
|
@ -2,7 +2,7 @@ from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from flask import Blueprint, render_template, url_for, g, flash
|
from flask import Blueprint, render_template, url_for, g, flash
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
|
|
||||||
from amanuensis.backend import charq
|
from amanuensis.backend import charq
|
||||||
|
@ -15,14 +15,14 @@ from .forms import CharacterCreateForm
|
||||||
bp = Blueprint("characters", __name__, url_prefix="/characters", template_folder=".")
|
bp = Blueprint("characters", __name__, url_prefix="/characters", template_folder=".")
|
||||||
|
|
||||||
|
|
||||||
@bp.get('/')
|
@bp.get("/")
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@player_required
|
@player_required
|
||||||
def list(name):
|
def list(name):
|
||||||
return render_template('characters.jinja', name=name)
|
return render_template("characters.jinja", name=name)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/edit/<character_id>', methods=['GET', 'POST'])
|
@bp.route("/edit/<character_id>", methods=["GET", "POST"])
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@player_required
|
@player_required
|
||||||
def edit(name, character_id):
|
def edit(name, character_id):
|
||||||
|
@ -30,11 +30,11 @@ def edit(name, character_id):
|
||||||
char_uuid = uuid.UUID(character_id)
|
char_uuid = uuid.UUID(character_id)
|
||||||
except:
|
except:
|
||||||
flash("Character not found")
|
flash("Character not found")
|
||||||
return redirect(url_for('lexicon.characters.list', name=name))
|
return redirect(url_for("lexicon.characters.list", name=name))
|
||||||
character: Optional[Character] = charq.try_from_public_id(g.db, char_uuid)
|
character: Optional[Character] = charq.try_from_public_id(g.db, char_uuid)
|
||||||
if not character:
|
if not character:
|
||||||
flash("Character not found")
|
flash("Character not found")
|
||||||
return redirect(url_for('lexicon.characters.list', name=name))
|
return redirect(url_for("lexicon.characters.list", name=name))
|
||||||
|
|
||||||
form = CharacterCreateForm()
|
form = CharacterCreateForm()
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def edit(name, character_id):
|
||||||
# GET
|
# GET
|
||||||
form.name.data = character.name
|
form.name.data = character.name
|
||||||
form.signature.data = character.signature
|
form.signature.data = character.signature
|
||||||
return render_template('characters.edit.jinja', character=character, form=form)
|
return render_template("characters.edit.jinja", character=character, form=form)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# POST
|
# POST
|
||||||
|
@ -51,18 +51,24 @@ def edit(name, character_id):
|
||||||
character.name = form.name.data
|
character.name = form.name.data
|
||||||
character.signature = form.signature.data
|
character.signature = form.signature.data
|
||||||
g.db.session.commit()
|
g.db.session.commit()
|
||||||
return redirect(url_for('lexicon.characters.list', name=name))
|
return redirect(url_for("lexicon.characters.list", name=name))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# POST submitted invalid data
|
# POST submitted invalid data
|
||||||
return render_template('characters.edit.jinja', character=character, form=form)
|
return render_template(
|
||||||
|
"characters.edit.jinja", character=character, form=form
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.get('/new/')
|
@bp.get("/new/")
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@player_required
|
@player_required
|
||||||
def new(name):
|
def new(name):
|
||||||
dummy_name = f"{current_user.username}'s new character"
|
dummy_name = f"{current_user.username}'s new character"
|
||||||
dummy_signature = "~"
|
dummy_signature = "~"
|
||||||
charq.create(g.db, g.lexicon.id, current_user.id, dummy_name, dummy_signature)
|
char = charq.create(
|
||||||
return redirect(url_for('lexicon.characters.list', name=name))
|
g.db, g.lexicon.id, current_user.id, dummy_name, dummy_signature
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
url_for("lexicon.characters.edit", name=name, character_id=char.public_id)
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import os
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from flask import Flask, url_for
|
||||||
|
|
||||||
|
from amanuensis.backend import memq, charq
|
||||||
|
from amanuensis.db import DbContext
|
||||||
|
|
||||||
|
from tests.conftest import ObjectFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_character_view(db: DbContext, app: Flask, make: ObjectFactory):
|
||||||
|
"""Test the lexicon character list, create, and edit pages."""
|
||||||
|
username: str = f"user_{os.urandom(8).hex()}"
|
||||||
|
charname: str = f"char_{os.urandom(8).hex()}"
|
||||||
|
char_sig: str = f"signature_{os.urandom(8).hex()}"
|
||||||
|
# ub: bytes = username.encode("utf8")
|
||||||
|
|
||||||
|
with app.test_client() as client:
|
||||||
|
# Create the user and log in
|
||||||
|
user = make.user(username=username, password=username)
|
||||||
|
assert user
|
||||||
|
user_client = make.client(user.id)
|
||||||
|
assert client
|
||||||
|
user_client.login(client)
|
||||||
|
|
||||||
|
# Create a lexicon and join
|
||||||
|
lexicon = make.lexicon()
|
||||||
|
assert lexicon
|
||||||
|
mem = memq.create(db, user.id, lexicon.id, is_editor=False)
|
||||||
|
assert mem
|
||||||
|
|
||||||
|
# The character page exists
|
||||||
|
list_url = url_for("lexicon.characters.list", name=lexicon.name)
|
||||||
|
response = client.get(list_url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert charname.encode("utf8") not in response.data
|
||||||
|
assert char_sig.encode("utf8") not in response.data
|
||||||
|
new_url = url_for("lexicon.characters.new", name=lexicon.name)
|
||||||
|
assert new_url.encode("utf8") in response.data
|
||||||
|
|
||||||
|
# The character creation endpoint works
|
||||||
|
response = client.get(new_url)
|
||||||
|
assert response.status_code == 302
|
||||||
|
chars = list(charq.get_in_lexicon(db, lexicon.id))
|
||||||
|
assert len(chars) == 1
|
||||||
|
assert chars[0].user_id == user.id
|
||||||
|
created_redirect = response.location
|
||||||
|
assert str(chars[0].public_id) in created_redirect
|
||||||
|
|
||||||
|
# The character edit page works
|
||||||
|
response = client.get(created_redirect)
|
||||||
|
assert chars[0].name.encode("utf8") in response.data
|
||||||
|
assert chars[0].signature.encode("utf8") in response.data
|
||||||
|
assert b"csrf_token" in response.data
|
||||||
|
|
||||||
|
# Submitting the edit page works
|
||||||
|
soup = BeautifulSoup(response.data, features="html.parser")
|
||||||
|
csrf_token = soup.find(id="csrf_token")["value"]
|
||||||
|
assert csrf_token
|
||||||
|
response = client.post(
|
||||||
|
created_redirect,
|
||||||
|
data={"name": charname, "signature": char_sig, "csrf_token": csrf_token},
|
||||||
|
)
|
||||||
|
print(response.data.decode("utf8"))
|
||||||
|
assert 300 <= response.status_code <= 399
|
||||||
|
|
||||||
|
# The character is updated
|
||||||
|
chars = list(charq.get_in_lexicon(db, lexicon.id))
|
||||||
|
assert len(chars) == 1
|
||||||
|
assert chars[0].user_id == user.id
|
||||||
|
assert chars[0].name == charname
|
||||||
|
assert chars[0].signature == char_sig
|
Loading…
Reference in New Issue