Compare commits
1 Commits
0d022af335
...
5e5b29b3e7
Author | SHA1 | Date |
---|---|---|
Tim Van Baak | 5e5b29b3e7 |
|
@ -89,16 +89,17 @@ def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> N
|
|||
An extant index not matched to an input is deleted, and an input index not
|
||||
matched to a an extant index is created. Matched indices are updated with
|
||||
the input logical and display orders and capacity.
|
||||
|
||||
Note that this scheme does not allow for an existing index to have its type
|
||||
or pattern updated: such an operation will always result in the deletion of
|
||||
the old index and the creation of a new index.
|
||||
"""
|
||||
extant_indices: Sequence[ArticleIndex] = list(get_for_lexicon(db, lexicon_id))
|
||||
s = lambda i: f"{i.index_type}:{i.pattern}"
|
||||
for extant_index in extant_indices:
|
||||
match = None
|
||||
for new_index in indices:
|
||||
if extant_index.name == new_index.name:
|
||||
is_match = (
|
||||
extant_index.index_type == new_index.index_type
|
||||
and extant_index.pattern == new_index.pattern
|
||||
)
|
||||
if is_match:
|
||||
match = new_index
|
||||
break
|
||||
if match:
|
||||
|
@ -110,9 +111,14 @@ def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> N
|
|||
for new_index in indices:
|
||||
match = None
|
||||
for extant_index in extant_indices:
|
||||
if extant_index.name == new_index.name:
|
||||
is_match = (
|
||||
extant_index.index_type == new_index.index_type
|
||||
and extant_index.pattern == new_index.pattern
|
||||
)
|
||||
if is_match:
|
||||
match = extant_index
|
||||
break
|
||||
if not match:
|
||||
new_index.lexicon_id = lexicon_id
|
||||
db.session.add(new_index)
|
||||
db.session.commit()
|
||||
|
|
|
@ -34,18 +34,9 @@ def create(
|
|||
).scalar_one_or_none()
|
||||
if not character:
|
||||
raise ArgumentError("Character does not exist")
|
||||
if character.lexicon_id != lexicon_id:
|
||||
if character.lexicon.id != lexicon_id:
|
||||
raise ArgumentError("Character belongs to the wrong lexicon")
|
||||
|
||||
# Verify the index belongs to the lexicon
|
||||
index: ArticleIndex = db(
|
||||
select(ArticleIndex).where(ArticleIndex.id == index_id)
|
||||
).scalar_one_or_none()
|
||||
if not index:
|
||||
raise ArgumentError("Index does not exist")
|
||||
if index.lexicon_id != lexicon_id:
|
||||
raise ArgumentError("Index belongs to the wrong lexicon")
|
||||
|
||||
new_assignment: ArticleIndexRule = ArticleIndexRule(
|
||||
lexicon_id=lexicon_id,
|
||||
character_id=character_id,
|
||||
|
@ -60,41 +51,5 @@ def create(
|
|||
def get_for_lexicon(db: DbContext, lexicon_id: int) -> Sequence[ArticleIndex]:
|
||||
"""Returns all index rules for a lexicon."""
|
||||
return db(
|
||||
select(ArticleIndexRule)
|
||||
.join(ArticleIndexRule.index)
|
||||
.join(ArticleIndexRule.character)
|
||||
.where(ArticleIndexRule.lexicon_id == lexicon_id)
|
||||
.order_by(ArticleIndexRule.turn, ArticleIndex.pattern, Character.name)
|
||||
select(ArticleIndexRule).where(ArticleIndexRule.lexicon_id == lexicon_id)
|
||||
).scalars()
|
||||
|
||||
|
||||
def update(db: DbContext, lexicon_id: int, rules: Sequence[ArticleIndexRule]) -> None:
|
||||
"""
|
||||
Update the index assignments for a lexicon. An index assignment is a tuple
|
||||
of turn, index, and character. Unlike indices themselves, assignments have
|
||||
no other attributes that can be updated, so they are simply created or
|
||||
deleted based on their presence or absence in the desired rule list.
|
||||
"""
|
||||
print(rules)
|
||||
extant_rules: Sequence[ArticleIndexRule] = list(get_for_lexicon(db, lexicon_id))
|
||||
for extant_rule in extant_rules:
|
||||
if not any(
|
||||
[
|
||||
extant_rule.character_id == new_rule.character_id
|
||||
and extant_rule.index_id == new_rule.index_id
|
||||
and extant_rule.turn == new_rule.turn
|
||||
for new_rule in rules
|
||||
]
|
||||
):
|
||||
db.session.delete(extant_rule)
|
||||
for new_rule in rules:
|
||||
if not any(
|
||||
[
|
||||
extant_rule.character_id == new_rule.character_id
|
||||
and extant_rule.index_id == new_rule.index_id
|
||||
and extant_rule.turn == new_rule.turn
|
||||
for extant_rule in extant_rules
|
||||
]
|
||||
):
|
||||
db.session.add(new_rule)
|
||||
db.session.commit()
|
||||
|
|
|
@ -505,10 +505,6 @@ class ArticleIndex(ModelBase):
|
|||
lexicon = relationship("Lexicon", back_populates="indices")
|
||||
index_rules = relationship("ArticleIndexRule", back_populates="index")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return f"{self.index_type}:{self.pattern}"
|
||||
|
||||
|
||||
class ArticleIndexRule(ModelBase):
|
||||
"""
|
||||
|
@ -518,7 +514,6 @@ class ArticleIndexRule(ModelBase):
|
|||
"""
|
||||
|
||||
__tablename__ = "article_index_rule"
|
||||
__table_args__ = (UniqueConstraint("character_id", "index_id", "turn"),)
|
||||
|
||||
###################
|
||||
# Index rule info #
|
||||
|
|
|
@ -12,12 +12,7 @@ from amanuensis.server.helpers import (
|
|||
current_lexicon,
|
||||
)
|
||||
|
||||
from .forms import (
|
||||
PlayerSettingsForm,
|
||||
SetupSettingsForm,
|
||||
IndexSchemaForm,
|
||||
IndexAssignmentsForm,
|
||||
)
|
||||
from .forms import PlayerSettingsForm, SetupSettingsForm, IndexSchemaForm, IndexAssignmentsForm
|
||||
|
||||
|
||||
bp = Blueprint("settings", __name__, url_prefix="/settings", template_folder=".")
|
||||
|
@ -166,14 +161,7 @@ def index_post(lexicon_name):
|
|||
if form.validate():
|
||||
# Valid data, strip out all indices with the blank type
|
||||
indices = [
|
||||
ArticleIndex(
|
||||
lexicon_id=current_lexicon.id,
|
||||
index_type=index_def.index_type.data,
|
||||
pattern=index_def.pattern.data,
|
||||
logical_order=index_def.logical_order.data,
|
||||
display_order=index_def.display_order.data,
|
||||
capacity=index_def.capacity.data,
|
||||
)
|
||||
index_def.to_model()
|
||||
for index_def in form.indices.entries
|
||||
if index_def.index_type.data
|
||||
]
|
||||
|
@ -194,84 +182,27 @@ def index_post(lexicon_name):
|
|||
@editor_required
|
||||
def assign(lexicon_name):
|
||||
# Get the current assignments
|
||||
rules: Sequence[ArticleIndexRule] = list(
|
||||
irq.get_for_lexicon(g.db, current_lexicon.id)
|
||||
)
|
||||
rules: Sequence[ArticleIndexRule] = list(irq.get_for_lexicon(g.db, current_lexicon.id))
|
||||
rule_data = [
|
||||
{
|
||||
"turn": rule.turn,
|
||||
"index": rule.index.name,
|
||||
"character": str(rule.character.public_id),
|
||||
"index": rule.index.pattern,
|
||||
"character": rule.character.public_id,
|
||||
}
|
||||
for rule in rules
|
||||
]
|
||||
# Add a blank rule to allow for adding rules
|
||||
rule_data.append(
|
||||
{
|
||||
"turn": 0,
|
||||
"index": "",
|
||||
"character": "",
|
||||
}
|
||||
)
|
||||
rule_data.append({
|
||||
"turn": 0,
|
||||
"index": "",
|
||||
"character": "",
|
||||
})
|
||||
form = IndexAssignmentsForm(rules=rule_data)
|
||||
form.populate(current_lexicon)
|
||||
return render_template(
|
||||
"settings.jinja",
|
||||
lexicon_name=lexicon_name,
|
||||
page_name=assign.__name__,
|
||||
form=form,
|
||||
"settings.jinja", lexicon_name=lexicon_name, page_name=assign.__name__, form=form
|
||||
)
|
||||
|
||||
|
||||
@bp.post("/assign/")
|
||||
@lexicon_param
|
||||
@editor_required
|
||||
def assign_post(lexicon_name):
|
||||
# Initialize the form
|
||||
form = IndexAssignmentsForm()
|
||||
form.populate(current_lexicon)
|
||||
if form.validate():
|
||||
# Valid data
|
||||
indices = list(current_lexicon.indices)
|
||||
characters = list(current_lexicon.characters)
|
||||
rules = []
|
||||
for rule_def in form.rules.entries:
|
||||
# Strip out all assignments with no character
|
||||
if not rule_def.character.data:
|
||||
continue
|
||||
# Look up the necessary ids from the public representations
|
||||
character = [
|
||||
c for c in characters if c.public_id == rule_def.character.data
|
||||
]
|
||||
if not character:
|
||||
return redirect(
|
||||
url_for("lexicon.settings.assign", lexicon_name=lexicon_name)
|
||||
)
|
||||
index = [i for i in indices if i.name == rule_def.index.data]
|
||||
if not index:
|
||||
return redirect(
|
||||
url_for("lexicon.settings.assign", lexicon_name=lexicon_name)
|
||||
)
|
||||
rules.append(
|
||||
ArticleIndexRule(
|
||||
lexicon_id=current_lexicon.id,
|
||||
character_id=character[0].id,
|
||||
index_id=index[0].id,
|
||||
turn=rule_def.turn.data,
|
||||
)
|
||||
)
|
||||
irq.update(g.db, current_lexicon.id, rules)
|
||||
return redirect(url_for("lexicon.settings.assign", lexicon_name=lexicon_name))
|
||||
else:
|
||||
# Invalid data
|
||||
return render_template(
|
||||
"settings.jinja",
|
||||
lexicon_name=lexicon_name,
|
||||
page_name=assign.__name__,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@bp.get("/publish/")
|
||||
@lexicon_param
|
||||
@editor_required
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import uuid
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
|
@ -15,7 +13,8 @@ from wtforms import (
|
|||
from wtforms.validators import Optional, DataRequired, ValidationError
|
||||
from wtforms.widgets.html5 import NumberInput
|
||||
|
||||
from amanuensis.db import IndexType, Lexicon
|
||||
from amanuensis.db import ArticleIndex, IndexType, Lexicon
|
||||
from amanuensis.server.helpers import current_lexicon
|
||||
|
||||
|
||||
class PlayerSettingsForm(FlaskForm):
|
||||
|
@ -82,6 +81,15 @@ class IndexDefinitionForm(FlaskForm):
|
|||
if form.index_type.data and not field.data:
|
||||
raise ValidationError("Pattern must be defined")
|
||||
|
||||
def to_model(self):
|
||||
return ArticleIndex(
|
||||
index_type=self.index_type.data,
|
||||
pattern=self.pattern.data,
|
||||
logical_order=self.logical_order.data,
|
||||
display_order=self.display_order.data,
|
||||
capacity=self.capacity.data,
|
||||
)
|
||||
|
||||
|
||||
class IndexSchemaForm(FlaskForm):
|
||||
"""/lexicon/<name>/settings/index/"""
|
||||
|
@ -90,12 +98,6 @@ class IndexSchemaForm(FlaskForm):
|
|||
submit = SubmitField("Submit")
|
||||
|
||||
|
||||
def parse_uuid(uuid_str):
|
||||
if not uuid_str:
|
||||
return None
|
||||
return uuid.UUID(uuid_str)
|
||||
|
||||
|
||||
class AssignmentDefinitionForm(FlaskForm):
|
||||
"""/lexicon/<name>/settings/assign/"""
|
||||
|
||||
|
@ -106,7 +108,13 @@ class AssignmentDefinitionForm(FlaskForm):
|
|||
|
||||
turn = IntegerField(widget=NumberInput(min=0, max=99))
|
||||
index = SelectField()
|
||||
character = SelectField(coerce=parse_uuid)
|
||||
character = SelectField()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
lexicon: Lexicon = current_lexicon
|
||||
self.index.choices = [i.pattern for i in lexicon.indices]
|
||||
self.character.choices = [(c.public_id, c.name) for c in lexicon.characters]
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class IndexAssignmentsForm(FlaskForm):
|
||||
|
@ -114,15 +122,3 @@ class IndexAssignmentsForm(FlaskForm):
|
|||
|
||||
rules = FieldList(FormField(AssignmentDefinitionForm))
|
||||
submit = SubmitField("Submit")
|
||||
|
||||
def populate(self, lexicon: Lexicon):
|
||||
"""Populate the select fields with indices and characters"""
|
||||
index_choices = []
|
||||
for i in lexicon.indices:
|
||||
index_choices.append((i.name, i.pattern))
|
||||
char_choices = [("", "")]
|
||||
for c in lexicon.characters:
|
||||
char_choices.append((str(c.public_id), c.name))
|
||||
for rule in self.rules:
|
||||
rule.index.choices = index_choices
|
||||
rule.character.choices = char_choices
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
</details>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<table id="index-definition-table2">
|
||||
<table id="index-definition-table">
|
||||
<tr>
|
||||
<th>Turn</th>
|
||||
<th>Index</th>
|
||||
|
@ -166,12 +166,63 @@
|
|||
{% endif %}
|
||||
|
||||
{% if page_name == "publish" %}
|
||||
<h3>Turn Publishing</h3>
|
||||
<h3>Turn Publishing</h3>
|
||||
{# <h3>Turn Publishing</h3>
|
||||
<p>
|
||||
{{ form.publishDeadlines(autocomplete="off") }}
|
||||
{{ form.publishDeadlines.label }}<br>
|
||||
{{ flag_setting(form.publishAsap) }}
|
||||
{% for error in form.publishDeadlines.errors %}
|
||||
<span style="color: #ff0000">{{ error }}</span><br>
|
||||
{% endfor %}
|
||||
{{ flag_setting(form.publishBlockOnReady) }}
|
||||
{{ number_setting(form.publishQuorum) }}
|
||||
|
||||
<p>
|
||||
{{ form.turnAssignment.label }}:<br>
|
||||
{{ form.turnAssignment(class_="fullwidth", rows=10) }}
|
||||
Transfer editorial control
|
||||
</p> #}
|
||||
{% endif %}
|
||||
|
||||
{% if page_name == "article" %}
|
||||
<h3>Article Requirements</h3>
|
||||
<h3>Article Requirements</h3>
|
||||
{# <h3>Article Requirements</h3>
|
||||
<p>
|
||||
{{ flag_setting(form.articleCitationAllowSelf) }}
|
||||
{{ number_setting(form.articleCitationMinExtant)}}
|
||||
{{ number_setting(form.articleCitationMaxExtant)}}
|
||||
{{ number_setting(form.articleCitationMinPhantom)}}
|
||||
{{ number_setting(form.articleCitationMaxPhantom)}}
|
||||
{{ number_setting(form.articleCitationMinTotal)}}
|
||||
{{ number_setting(form.articleCitationMaxTotal)}}
|
||||
{{ number_setting(form.articleCitationMinChars)}}
|
||||
{{ number_setting(form.articleCitationMaxChars)}}
|
||||
{{ number_setting(form.articleWordLimitSoft)}}
|
||||
{{ number_setting(form.articleWordLimitHard)}}
|
||||
{{ flag_setting(form.articleAddendumAllowed) }}
|
||||
{{ number_setting(form.articleAddendumMax) }}
|
||||
</p> #}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{# <p>
|
||||
Id: {{ g.lexicon.lid }}<br>
|
||||
Name: {{ g.lexicon.cfg.name }}<br>
|
||||
Created: {{ g.lexicon.cfg.time.created|asdate }}<br>
|
||||
Completed: {{ g.lexicon.cfg.time.completed|asdate }}<br>
|
||||
Players:
|
||||
{% for uid in g.lexicon.cfg.join.joined %}
|
||||
{{ uid|user_attr('username') }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}<br>
|
||||
Characters:
|
||||
{% for char in g.lexicon.cfg.character.values() %}
|
||||
{{ char.name }}{% if char.player %}
|
||||
({{ char.player|user_attr('username') }}){% endif %}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}<br>
|
||||
</p> #}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% set template_content_blocks = [self.main()] %}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from amanuensis.backend import irq
|
||||
from amanuensis.db import *
|
||||
from amanuensis.errors import ArgumentError
|
||||
|
||||
from tests.conftest import ObjectFactory
|
||||
|
||||
|
||||
def test_create_assign(db: DbContext, make: ObjectFactory):
|
||||
"""Test new index assignment creation"""
|
||||
lexicon: Lexicon = make.lexicon()
|
||||
user: User = make.user()
|
||||
mem: Membership = make.membership(lexicon_id=lexicon.id, user_id=user.id)
|
||||
char: Character = make.character(lexicon_id=lexicon.id, user_id=user.id)
|
||||
ind1: ArticleIndex = make.index(lexicon_id=lexicon.id)
|
||||
|
||||
defaults: dict = {
|
||||
"db": db,
|
||||
"lexicon_id": lexicon.id,
|
||||
"character_id": char.id,
|
||||
"index_id": ind1.id,
|
||||
"turn": 1,
|
||||
}
|
||||
kwargs: dict
|
||||
|
||||
# Index assignments must key to objects in the same lexicon
|
||||
lexicon2: Lexicon = make.lexicon()
|
||||
mem2: Membership = make.membership(lexicon_id=lexicon2.id, user_id=user.id)
|
||||
char2: Character = make.character(lexicon_id=lexicon2.id, user_id=user.id)
|
||||
ind2: ArticleIndex = make.index(lexicon_id=lexicon2.id)
|
||||
with pytest.raises(ArgumentError):
|
||||
kwargs = {**defaults, "index_id": ind2.id}
|
||||
irq.create(**kwargs)
|
||||
with pytest.raises(ArgumentError):
|
||||
kwargs = {**defaults, "character_id": char2.id, "index_id": ind2.id}
|
||||
irq.create(**kwargs)
|
||||
with pytest.raises(ArgumentError):
|
||||
kwargs = {**defaults, "character_id": char2.id}
|
||||
irq.create(**kwargs)
|
|
@ -10,9 +10,9 @@ from bs4 import BeautifulSoup
|
|||
from flask.testing import FlaskClient
|
||||
from sqlalchemy.orm.session import close_all_sessions
|
||||
|
||||
from amanuensis.backend import *
|
||||
from amanuensis.backend import charq, lexiq, memq, userq
|
||||
from amanuensis.config import AmanuensisConfig
|
||||
from amanuensis.db import *
|
||||
from amanuensis.db import DbContext, User, Lexicon, Membership, Character
|
||||
from amanuensis.server import get_app
|
||||
|
||||
|
||||
|
@ -122,19 +122,6 @@ class ObjectFactory:
|
|||
updated_kwargs: dict = {**default_kwargs, **kwargs}
|
||||
return charq.create(self.db, **updated_kwargs)
|
||||
|
||||
def index(self, state={"nonce": ord("A")}, **kwargs) -> ArticleIndex:
|
||||
"""Factory function for creating indices, with valid defaut values."""
|
||||
default_kwargs: dict = {
|
||||
"index_type": IndexType.CHAR,
|
||||
"pattern": chr(state["nonce"]),
|
||||
"logical_order": 0,
|
||||
"display_order": 0,
|
||||
"capacity": None,
|
||||
}
|
||||
state["nonce"] += 1
|
||||
updated_kwargs = {**default_kwargs, **kwargs}
|
||||
return indq.create(self.db, **updated_kwargs)
|
||||
|
||||
def client(self, user_id: int) -> UserClient:
|
||||
"""Factory function for user test clients."""
|
||||
return UserClient(self.db, user_id)
|
||||
|
|
Loading…
Reference in New Issue