Get home page and login working #14
|
@ -1,99 +0,0 @@
|
|||
# Standard library imports
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Module imports
|
||||
from amanuensis.cli import describe_commands, get_commands
|
||||
from amanuensis.config import (
|
||||
RootConfigDirectoryContext,
|
||||
ENV_CONFIG_DIR,
|
||||
ENV_LOG_FILE)
|
||||
from amanuensis.errors import AmanuensisError
|
||||
from amanuensis.log import init_logging
|
||||
from amanuensis.models import ModelFactory
|
||||
|
||||
|
||||
def process_doc(docstring):
|
||||
return '\n'.join([
|
||||
line.strip()
|
||||
for line in (docstring or "").strip().splitlines()
|
||||
])
|
||||
|
||||
|
||||
def get_parser(valid_commands):
|
||||
# Set up the top-level parser.
|
||||
parser = argparse.ArgumentParser(
|
||||
description=describe_commands(),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
# The config directory.
|
||||
parser.add_argument("--config-dir",
|
||||
dest="config_dir",
|
||||
default=os.environ.get(ENV_CONFIG_DIR, "./config"),
|
||||
help="The config directory for Amanuensis")
|
||||
# Logging settings.
|
||||
parser.add_argument("--verbose", "-v",
|
||||
action="store_true",
|
||||
dest="verbose",
|
||||
help="Enable verbose console logging")
|
||||
parser.add_argument("--log-file",
|
||||
dest="log_file",
|
||||
default=os.environ.get(ENV_LOG_FILE),
|
||||
help="Enable verbose file logging")
|
||||
parser.set_defaults(func=lambda args: parser.print_help())
|
||||
subp = parser.add_subparsers(
|
||||
metavar="COMMAND",
|
||||
dest="command",
|
||||
help="The command to execute")
|
||||
|
||||
# Set up command subparsers.
|
||||
# command_ functions perform setup or execution depending on
|
||||
# whether their argument is an ArgumentParser.
|
||||
for name, func in valid_commands.items():
|
||||
# Create the subparser, set the docstring as the description.
|
||||
cmd = subp.add_parser(name,
|
||||
description=process_doc(func.__doc__),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
aliases=func.__dict__.get("aliases", []))
|
||||
# Delegate subparser setup to the command.
|
||||
func(cmd)
|
||||
# Store function for later execution.
|
||||
cmd.set_defaults(func=func)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main(argv):
|
||||
# Enumerate valid commands from the CLI module.
|
||||
commands = get_commands()
|
||||
|
||||
# Parse args
|
||||
args = get_parser(commands).parse_args(argv)
|
||||
|
||||
# First things first, initialize logging
|
||||
init_logging(args.verbose, args.log_file)
|
||||
logger = logging.getLogger('amanuensis')
|
||||
|
||||
# The init command initializes a config directory at --config-dir.
|
||||
# All other commands assume that the config dir already exists.
|
||||
if args.command and args.command != "init":
|
||||
args.root = RootConfigDirectoryContext(args.config_dir)
|
||||
args.model_factory = ModelFactory(args.root)
|
||||
|
||||
# If verbose logging, dump args namespace
|
||||
if args.verbose:
|
||||
logger.debug('amanuensis')
|
||||
for key, val in vars(args).items():
|
||||
logger.debug(f' {key}: {val}')
|
||||
|
||||
# Execute command.
|
||||
try:
|
||||
args.func(args)
|
||||
except AmanuensisError as e:
|
||||
logger.error('Unexpected internal {}: {}'.format(
|
||||
type(e).__name__, str(e)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
|
@ -1,104 +0,0 @@
|
|||
"""
|
||||
Submodule of functions for creating and managing lexicons within the
|
||||
general Amanuensis context.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import Iterable
|
||||
import uuid
|
||||
|
||||
from amanuensis.config import RootConfigDirectoryContext, AttrOrderedDict
|
||||
from amanuensis.errors import ArgumentError
|
||||
from amanuensis.models import ModelFactory, UserModel, LexiconModel
|
||||
from amanuensis.resources import get_stream
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def valid_name(name: str) -> bool:
|
||||
"""
|
||||
Validates that a lexicon name consists only of alpahnumerics, dashes,
|
||||
underscores, and spaces
|
||||
"""
|
||||
return re.match(r'^[A-Za-z0-9-_ ]+$', name) is not None
|
||||
|
||||
|
||||
def create_lexicon(
|
||||
root: RootConfigDirectoryContext,
|
||||
name: str,
|
||||
editor: UserModel) -> LexiconModel:
|
||||
"""
|
||||
Creates a lexicon with the given name and sets the given user as its editor
|
||||
"""
|
||||
# Verify arguments
|
||||
if not name:
|
||||
raise ArgumentError(f'Empty lexicon name: "{name}"')
|
||||
if not valid_name(name):
|
||||
raise ArgumentError(f'Invalid lexicon name: "{name}"')
|
||||
with root.lexicon.read_index() as extant_lexicons:
|
||||
if name in extant_lexicons.keys():
|
||||
raise ArgumentError(f'Lexicon name already taken: "{name}"')
|
||||
if editor is None:
|
||||
raise ArgumentError('Editor must not be None')
|
||||
|
||||
# Create the lexicon directory and initialize it with a blank lexicon
|
||||
lid: str = uuid.uuid4().hex
|
||||
lex_dir = os.path.join(root.lexicon.path, lid)
|
||||
os.mkdir(lex_dir)
|
||||
with get_stream("lexicon.json") as s:
|
||||
path: str = os.path.join(lex_dir, 'config.json')
|
||||
with open(path, 'wb') as f:
|
||||
f.write(s.read())
|
||||
|
||||
# Create subdirectories
|
||||
os.mkdir(os.path.join(lex_dir, 'draft'))
|
||||
os.mkdir(os.path.join(lex_dir, 'src'))
|
||||
os.mkdir(os.path.join(lex_dir, 'article'))
|
||||
|
||||
# Update the index with the new lexicon
|
||||
with root.lexicon.edit_index() as index:
|
||||
index[name] = lid
|
||||
|
||||
# Fill out the new lexicon
|
||||
with root.lexicon[lid].edit_config() as cfg:
|
||||
cfg.lid = lid
|
||||
cfg.name = name
|
||||
cfg.editor = editor.uid
|
||||
cfg.time.created = int(time.time())
|
||||
|
||||
with root.lexicon[lid].edit('info', create=True):
|
||||
pass # Create an empry config file
|
||||
|
||||
# Load the lexicon and add the editor and default character
|
||||
model_factory: ModelFactory = ModelFactory(root)
|
||||
lexicon = model_factory.lexicon(lid)
|
||||
with lexicon.ctx.edit_config() as cfg:
|
||||
cfg.join.joined.append(editor.uid)
|
||||
with get_stream('character.json') as template:
|
||||
character = json.load(template, object_pairs_hook=AttrOrderedDict)
|
||||
character.cid = 'default'
|
||||
character.name = 'Ersatz Scrivener'
|
||||
character.player = None
|
||||
cfg.character.new(character.cid, character)
|
||||
|
||||
# Log the creation
|
||||
message = f'Created {lexicon.title}, ed. {editor.cfg.displayname} ({lid})'
|
||||
lexicon.log(message)
|
||||
logger.info(message)
|
||||
|
||||
return lexicon
|
||||
|
||||
|
||||
def load_all_lexicons(
|
||||
root: RootConfigDirectoryContext) -> Iterable[LexiconModel]:
|
||||
"""
|
||||
Iterably loads every lexicon in the config store
|
||||
"""
|
||||
model_factory: ModelFactory = ModelFactory(root)
|
||||
with root.lexicon.read_index() as index:
|
||||
for lid in index.values():
|
||||
lexicon: LexiconModel = model_factory.lexicon(lid)
|
||||
yield lexicon
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"version": "0",
|
||||
"aid": null,
|
||||
"lexicon": null,
|
||||
"character": null,
|
||||
"title": null,
|
||||
"turn": null,
|
||||
"status": {
|
||||
"ready": false,
|
||||
"approved": false
|
||||
},
|
||||
"contents": null
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": "0",
|
||||
"cid": null,
|
||||
"name": null,
|
||||
"player": null,
|
||||
"signature": null
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"version": "0",
|
||||
"secret_key": null,
|
||||
"address": "127.0.0.1",
|
||||
"port": "5000",
|
||||
"lexicon_data": "./lexicon",
|
||||
"static_root": "../resources",
|
||||
"email": {
|
||||
"server": null,
|
||||
"port": null,
|
||||
"tls": null,
|
||||
"username": null,
|
||||
"password": null
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
{
|
||||
"version": "0",
|
||||
"lid": null,
|
||||
"name": null,
|
||||
"title": null,
|
||||
"editor": null,
|
||||
"prompt": null,
|
||||
"time": {
|
||||
"created": null,
|
||||
"completed": null
|
||||
},
|
||||
"turn": {
|
||||
"current": null,
|
||||
"max": 8,
|
||||
"assignment": {
|
||||
}
|
||||
},
|
||||
"join": {
|
||||
"public": false,
|
||||
"open": false,
|
||||
"password": null,
|
||||
"max_players": 4,
|
||||
"chars_per_player": 1,
|
||||
"joined": []
|
||||
},
|
||||
"publish": {
|
||||
"notify": {
|
||||
"editor_on_ready": true,
|
||||
"player_on_reject": true,
|
||||
"player_on_accept": false
|
||||
},
|
||||
"deadlines": null,
|
||||
"asap": false,
|
||||
"quorum": null,
|
||||
"block_on_ready": true
|
||||
},
|
||||
"article": {
|
||||
"index": {
|
||||
"list": [
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "ABC"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "DEF"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "GHI"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "JKL"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "MNO"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "PQRS"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "TUV"
|
||||
},
|
||||
{
|
||||
"type": "char",
|
||||
"pri": 0,
|
||||
"pattern": "WXYZ"
|
||||
}
|
||||
],
|
||||
"capacity": null
|
||||
},
|
||||
"citation": {
|
||||
"allow_self": false,
|
||||
"min_extant": null,
|
||||
"max_extant": null,
|
||||
"min_phantom": null,
|
||||
"max_phantom": null,
|
||||
"min_total": null,
|
||||
"max_total": null,
|
||||
"min_chars": null,
|
||||
"max_chars": null
|
||||
},
|
||||
"word_limit": {
|
||||
"soft": null,
|
||||
"hard": null
|
||||
},
|
||||
"addendum": {
|
||||
"allowed": false,
|
||||
"max": null
|
||||
}
|
||||
},
|
||||
"character": {
|
||||
},
|
||||
"log": [
|
||||
]
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"version": "0",
|
||||
"uid": null,
|
||||
"username": null,
|
||||
"displayname": null,
|
||||
"email": null,
|
||||
"password": null,
|
||||
"created": null,
|
||||
"last_login": null,
|
||||
"last_activity": null,
|
||||
"new_password_required": true,
|
||||
"is_admin": false
|
||||
}
|
|
@ -10,7 +10,7 @@ bp = Blueprint("home", __name__, url_prefix="/home", template_folder=".")
|
|||
|
||||
@bp.get("/")
|
||||
def home():
|
||||
return render_template('home.root.jinja')
|
||||
return render_template("home.root.jinja")
|
||||
|
||||
|
||||
@bp.get("/admin/")
|
||||
|
|
|
@ -2,16 +2,16 @@ from flask_wtf import FlaskForm
|
|||
from wtforms import StringField, SubmitField, TextAreaField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from amanuensis.server.forms import User, Lexicon
|
||||
# from amanuensis.server.forms import User, Lexicon
|
||||
|
||||
|
||||
class LexiconCreateForm(FlaskForm):
|
||||
"""/admin/create/"""
|
||||
lexiconName = StringField(
|
||||
'Lexicon name',
|
||||
validators=[DataRequired(), Lexicon(should_exist=False)])
|
||||
editorName = StringField(
|
||||
'Username of editor',
|
||||
validators=[DataRequired(), User(should_exist=True)])
|
||||
promptText = TextAreaField('Prompt')
|
||||
submit = SubmitField('Create')
|
||||
# class LexiconCreateForm(FlaskForm):
|
||||
# """/admin/create/"""
|
||||
# lexiconName = StringField(
|
||||
# 'Lexicon name',
|
||||
# validators=[DataRequired(), Lexicon(should_exist=False)])
|
||||
# editorName = StringField(
|
||||
# 'Username of editor',
|
||||
# validators=[DataRequired(), User(should_exist=True)])
|
||||
# promptText = TextAreaField('Prompt')
|
||||
# submit = SubmitField('Create')
|
||||
|
|
2
mypy.ini
2
mypy.ini
|
@ -1,4 +1,4 @@
|
|||
[mypy]
|
||||
ignore_missing_imports = true
|
||||
exclude = "|amanuensis/lexicon/.*|amanuensis/models/.*|amanuensis/resources/.*|amanuensis/server/.*|amanuensis/user/.*|amanuensis/__main__.py|"
|
||||
exclude = "|amanuensis/lexicon/.*|amanuensis/server/.*|amanuensis/server/lexicon/.*|amanuensis/server/session/.*|"
|
||||
; mypy stable doesn't support pyproject.toml yet
|
|
@ -22,11 +22,11 @@ amanuensis-cli = "amanuensis.cli:main"
|
|||
amanuensis-server = "amanuensis.server:run"
|
||||
|
||||
[tool.black]
|
||||
extend-exclude = "^/amanuensis/lexicon/.*|^/amanuensis/models/.*|^/amanuensis/resources/.*|^/amanuensis/server/.*|^/amanuensis/user/.*|^/amanuensis/__main__.py"
|
||||
extend-exclude = "^/amanuensis/lexicon/.*|^/amanuensis/server/[^/]*py|^/amanuensis/server/lexicon/.*|^/amanuensis/server/session/.*|"
|
||||
|
||||
[tool.mypy]
|
||||
ignore_missing_imports = true
|
||||
exclude = "amanuensis/cli/.*|amanuensis/config/.*|amanuensis/lexicon/.*|amanuensis/log/.*|amanuensis/models/.*|amanuensis/resources/.*|amanuensis/server/.*|amanuensis/user/.*|amanuensis/__main__.py"
|
||||
exclude = "|amanuensis/lexicon/.*|amanuensis/server/.*|amanuensis/server/lexicon/.*|amanuensis/server/session/.*|"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--show-capture=log"
|
||||
|
|
Loading…
Reference in New Issue