Add character page and create/edit workflows #18
|
@ -3,6 +3,7 @@ Character query interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from sqlalchemy import select, func
|
from sqlalchemy import select, func
|
||||||
|
|
||||||
|
@ -73,3 +74,8 @@ def create(
|
||||||
def get_in_lexicon(db: DbContext, lexicon_id: int) -> Sequence[Character]:
|
def get_in_lexicon(db: DbContext, lexicon_id: int) -> Sequence[Character]:
|
||||||
"""Get all characters in a lexicon."""
|
"""Get all characters in a lexicon."""
|
||||||
return db(select(Character).where(Character.lexicon_id == lexicon_id)).scalars()
|
return db(select(Character).where(Character.lexicon_id == lexicon_id)).scalars()
|
||||||
|
|
||||||
|
|
||||||
|
def try_from_public_id(db: DbContext, public_id: UUID) -> Optional[Character]:
|
||||||
|
"""Get a character by its public id."""
|
||||||
|
return db(select(Character).where(Character.public_id == public_id)).scalar_one_or_none()
|
||||||
|
|
|
@ -139,6 +139,10 @@ ul.blockitem-list li {
|
||||||
border-inline-start: 3px solid black;
|
border-inline-start: 3px solid black;
|
||||||
padding-inline-start: 0.5em;
|
padding-inline-start: 0.5em;
|
||||||
}
|
}
|
||||||
|
ul.blockitem-list p {
|
||||||
|
margin-block-start: 0.5em;
|
||||||
|
margin-block-end: 0.5em;
|
||||||
|
}
|
||||||
div.dashboard-lexicon-unstarted {
|
div.dashboard-lexicon-unstarted {
|
||||||
border-left-color: blue;
|
border-left-color: blue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
from flask import Blueprint, render_template, url_for
|
from typing import Optional
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from flask import Blueprint, render_template, url_for, g, flash
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
|
|
||||||
|
from amanuensis.backend import charq
|
||||||
|
from amanuensis.db import Character
|
||||||
from amanuensis.server.helpers import lexicon_param, player_required
|
from amanuensis.server.helpers import lexicon_param, player_required
|
||||||
|
|
||||||
|
from .forms import CharacterCreateForm
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint("characters", __name__, url_prefix="/characters", template_folder=".")
|
bp = Blueprint("characters", __name__, url_prefix="/characters", template_folder=".")
|
||||||
|
|
||||||
|
@ -14,54 +21,40 @@ def characters(name):
|
||||||
return render_template('characters.jinja')
|
return render_template('characters.jinja')
|
||||||
|
|
||||||
|
|
||||||
@bp.post('/')
|
@bp.route('/edit/<character_id>', methods=['GET', 'POST'])
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@player_required
|
@player_required
|
||||||
def update(name):
|
def edit(name, character_id):
|
||||||
return redirect(url_for('lexicon.statistics', name=name))
|
try:
|
||||||
|
char_uuid = uuid.UUID(character_id)
|
||||||
|
except:
|
||||||
|
flash("Character not found")
|
||||||
|
return redirect(url_for('lexicon.characters.characters', name=name))
|
||||||
|
character: Optional[Character] = charq.try_from_public_id(g.db, char_uuid)
|
||||||
|
if not character:
|
||||||
|
flash("Character not found")
|
||||||
|
return redirect(url_for('lexicon.characters.characters', name=name))
|
||||||
|
|
||||||
|
form = CharacterCreateForm()
|
||||||
|
|
||||||
# @bp.route('/', methods=['GET', 'POST'])
|
if not form.is_submitted():
|
||||||
# @lexicon_param
|
# GET
|
||||||
# @player_required
|
form.name.data = character.name
|
||||||
# def characters(name):
|
form.signature.data = character.signature
|
||||||
# return render_template("characters.jinja")
|
return render_template('characters.edit.jinja', character=character, form=form)
|
||||||
# form = LexiconCharacterForm()
|
|
||||||
# cid = request.args.get('cid')
|
|
||||||
# if not cid:
|
|
||||||
# # No character specified, creating a new character
|
|
||||||
# return create_character(name, form)
|
|
||||||
|
|
||||||
# character = g.lexicon.cfg.character.get(cid)
|
else:
|
||||||
# if not character:
|
# POST
|
||||||
# # Bad character id, abort
|
if form.validate():
|
||||||
# flash('Character not found')
|
# Data is valid
|
||||||
# return redirect(url_for('session.session', name=name))
|
character.name = form.name.data
|
||||||
# if current_user.uid not in (character.player, g.lexicon.cfg.editor):
|
character.signature = form.signature.data
|
||||||
# # Only its owner and the editor can edit a character
|
g.db.session.commit()
|
||||||
# flash('Access denied')
|
return redirect(url_for('lexicon.characters.characters', name=name))
|
||||||
# return redirect(url_for('session.session', name=name))
|
|
||||||
# # Edit allowed
|
|
||||||
# return edit_character(name, form, character)
|
|
||||||
|
|
||||||
|
else:
|
||||||
# def edit_character(name, form, character):
|
# POST submitted invalid data
|
||||||
# if not form.is_submitted():
|
return render_template('characters.edit.jinja', character=character, form=form)
|
||||||
# # GET, populate with values
|
|
||||||
# return render_template(
|
|
||||||
# 'session.character.jinja', form=form.for_character(character))
|
|
||||||
|
|
||||||
# if not form.validate():
|
|
||||||
# # POST with invalid data, return unchanged
|
|
||||||
# return render_template('session.character.jinja', form=form)
|
|
||||||
|
|
||||||
# # POST with valid data, update character
|
|
||||||
# with g.lexicon.ctx.edit_config() as cfg:
|
|
||||||
# char = cfg.character[character.cid]
|
|
||||||
# char.name = form.characterName.data
|
|
||||||
# char.signature = form.defaultSignature.data
|
|
||||||
# flash('Character updated')
|
|
||||||
# return redirect(url_for('session.session', name=name))
|
|
||||||
|
|
||||||
|
|
||||||
# def create_character(name: str, form: LexiconCharacterForm):
|
# def create_character(name: str, form: LexiconCharacterForm):
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "lexicon.jinja" %}
|
||||||
|
{% block title %}Edit {{ character.name }} | {{ lexicon_title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<form action="" method="post" novalidate>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<p>
|
||||||
|
{{ form.name.label }}<br>{{ form.name(size=32) }}
|
||||||
|
</p>
|
||||||
|
{% for error in form.name.errors %}
|
||||||
|
<span style="color: #ff0000">{{ error }}</span><br>
|
||||||
|
{% endfor %}</p>
|
||||||
|
<p>
|
||||||
|
{{ form.signature.label }}<br>{{ form.signature(class_='fullwidth') }}
|
||||||
|
</p>
|
||||||
|
<p>{{ form.submit() }}</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<span style="color:#ff0000">{{ message }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% set template_content_blocks = [self.main()] %}
|
|
@ -2,36 +2,23 @@
|
||||||
{% block title %}Character | {{ lexicon_title }}{% endblock %}
|
{% block title %}Character | {{ lexicon_title }}{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<h1>Characters</h1>
|
<h1>Characters</h1>
|
||||||
{% set players = memq.get_players_in_lexicon(db, g.lexicon.id)|list %}
|
{% set players = memq.get_players_in_lexicon(db, g.lexicon.id)|list %}
|
||||||
{% set characters = charq.get_in_lexicon(db, g.lexicon.id)|list %}
|
{% set characters = charq.get_in_lexicon(db, g.lexicon.id)|list %}
|
||||||
<p>This lexicon has <b>{{ players|count }}</b> player{% if players|count > 1 %}s{% endif %} and <b>{{ characters|count }}</b> character{% if characters|count > 1 %}s{% endif %}.</p>
|
<p>This lexicon has <b>{{ players|count }}</b> player{% if players|count > 1 %}s{% endif %} and <b>{{ characters|count }}</b> character{% if characters|count > 1 %}s{% endif %}.</p>
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<span style="color:#ff0000">{{ message }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
<ul class="blockitem-list">
|
<ul class="blockitem-list">
|
||||||
{% for character in characters %}
|
{% for character in characters %}
|
||||||
<li>
|
<li>
|
||||||
<h2>{{ character.name }}</h2>
|
<h3>{{ character.name }}</h3>
|
||||||
<p>Player: {{ character.user.username }}</p>
|
<p>Player: {{ character.user.username }}</p>
|
||||||
|
{% if character.user == current_user %}
|
||||||
|
<p><a href="{{ url_for('lexicon.characters.edit', name=g.lexicon.name, character_id=character.public_id) }}">Edit this character</a></p>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{# <form action="" method="post" novalidate>
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<p>
|
|
||||||
{{ form.characterName.label }}<br>{{ form.characterName(size=32) }}
|
|
||||||
</p>
|
|
||||||
{% for error in form.characterName.errors %}
|
|
||||||
<span style="color: #ff0000">{{ error }}</span><br>
|
|
||||||
{% endfor %}</p>
|
|
||||||
<p>
|
|
||||||
{{ form.defaultSignature.label }}<br>{{ form.defaultSignature(class_='fullwidth') }}
|
|
||||||
</p>
|
|
||||||
<p>{{ form.submit() }}</p>
|
|
||||||
</form> #}
|
|
||||||
|
|
||||||
{# {% for message in get_flashed_messages() %}
|
|
||||||
<span style="color:#ff0000">{{ message }}</span><br>
|
|
||||||
{% endfor %} #}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% set template_content_blocks = [self.main()] %}
|
{% set template_content_blocks = [self.main()] %}
|
|
@ -0,0 +1,11 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField, TextAreaField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
|
class CharacterCreateForm(FlaskForm):
|
||||||
|
"""/lexicon/<name>/characters/edit/<character_id>"""
|
||||||
|
|
||||||
|
name = StringField("Character name", validators=[DataRequired()])
|
||||||
|
signature = TextAreaField("Signature")
|
||||||
|
submit = SubmitField("Submit")
|
Loading…
Reference in New Issue