Implement index assignments #22
|
@ -1,9 +1,10 @@
|
|||
import amanuensis.backend.article as artiq
|
||||
import amanuensis.backend.character as charq
|
||||
import amanuensis.backend.index as indq
|
||||
import amanuensis.backend.indexrule as irq
|
||||
import amanuensis.backend.lexicon as lexiq
|
||||
import amanuensis.backend.membership as memq
|
||||
import amanuensis.backend.post as postq
|
||||
import amanuensis.backend.user as userq
|
||||
|
||||
__all__ = ["artiq", "charq", "indq", "lexiq", "memq", "postq", "userq"]
|
||||
__all__ = ["artiq", "charq", "indq", "irq", "lexiq", "memq", "postq", "userq"]
|
||||
|
|
|
@ -87,19 +87,18 @@ def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> N
|
|||
"""
|
||||
Update the indices for a lexicon. Indices are matched by type and pattern.
|
||||
An extant index not matched to an input is deleted, and an input index not
|
||||
matched to a an extant index is created. Matched indexes are updated with
|
||||
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:
|
||||
is_match = (
|
||||
extant_index.index_type == new_index.index_type
|
||||
and extant_index.pattern == new_index.pattern
|
||||
)
|
||||
if is_match:
|
||||
if extant_index.name == new_index.name:
|
||||
match = new_index
|
||||
break
|
||||
if match:
|
||||
|
@ -111,14 +110,9 @@ def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> N
|
|||
for new_index in indices:
|
||||
match = None
|
||||
for extant_index in extant_indices:
|
||||
is_match = (
|
||||
extant_index.index_type == new_index.index_type
|
||||
and extant_index.pattern == new_index.pattern
|
||||
)
|
||||
if is_match:
|
||||
if extant_index.name == new_index.name:
|
||||
match = extant_index
|
||||
break
|
||||
if not match:
|
||||
new_index.lexicon_id = lexicon_id
|
||||
db.session.add(new_index)
|
||||
db.session.commit()
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
"""
|
||||
Index rule query interface
|
||||
"""
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from amanuensis.db import *
|
||||
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||
|
||||
|
||||
def create(
|
||||
db: DbContext,
|
||||
lexicon_id: int,
|
||||
character_id: int,
|
||||
index_id: int,
|
||||
turn: int,
|
||||
) -> ArticleIndexRule:
|
||||
"""Create an index assignment."""
|
||||
# Verify argument types are correct
|
||||
if not isinstance(lexicon_id, int):
|
||||
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||
if character_id is not None and not isinstance(character_id, int):
|
||||
raise BackendArgumentTypeError(int, character_id=character_id)
|
||||
if not isinstance(index_id, int):
|
||||
raise BackendArgumentTypeError(int, index_id=index_id)
|
||||
if not isinstance(turn, int):
|
||||
raise BackendArgumentTypeError(int, turn=turn)
|
||||
|
||||
# Verify the character belongs to the lexicon
|
||||
character: Character = db(
|
||||
select(Character).where(Character.id == character_id)
|
||||
).scalar_one_or_none()
|
||||
if not character:
|
||||
raise ArgumentError("Character does not exist")
|
||||
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,
|
||||
index_id=index_id,
|
||||
turn=turn,
|
||||
)
|
||||
db.session.add(new_assignment)
|
||||
db.session.commit()
|
||||
return new_assignment
|
||||
|
||||
|
||||
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)
|
||||
).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()
|
|
@ -1,4 +1,3 @@
|
|||
import enum
|
||||
import logging
|
||||
|
||||
from amanuensis.backend import *
|
||||
|
@ -8,7 +7,7 @@ from .helpers import add_argument
|
|||
|
||||
|
||||
COMMAND_NAME = "index"
|
||||
COMMAND_HELP = "Interact with indexes."
|
||||
COMMAND_HELP = "Interact with indices."
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -40,3 +39,26 @@ def command_create(args) -> int:
|
|||
)
|
||||
LOG.info(f"Created {index.index_type}:{index.pattern} in {lexicon.full_title}")
|
||||
return 0
|
||||
|
||||
|
||||
@add_argument("--lexicon", required=True, help="The lexicon's name")
|
||||
@add_argument("--character", help="The character's public id")
|
||||
@add_argument("--index", required=True, help="The index pattern")
|
||||
@add_argument("--turn", required=True, type=int)
|
||||
def command_assign(args) -> int:
|
||||
"""
|
||||
Create a turn assignment for a lexicon.
|
||||
"""
|
||||
db: DbContext = args.get_db()
|
||||
lexicon = lexiq.try_from_name(db, args.lexicon)
|
||||
if not lexicon:
|
||||
raise ValueError("Lexicon does not exist")
|
||||
char = charq.try_from_public_id(db, args.character)
|
||||
assert char
|
||||
indices = indq.get_for_lexicon(db, lexicon.id)
|
||||
index = [i for i in indices if i.pattern == args.index]
|
||||
if not index:
|
||||
raise ValueError("Index not found")
|
||||
assignment = irq.create(db, lexicon.id, char.id, index[0].id, args.turn)
|
||||
LOG.info("Created")
|
||||
return 0
|
||||
|
|
|
@ -248,7 +248,7 @@ class Lexicon(ModelBase):
|
|||
memberships = relationship("Membership", back_populates="lexicon")
|
||||
characters = relationship("Character", back_populates="lexicon")
|
||||
articles = relationship("Article", back_populates="lexicon")
|
||||
indexes = relationship("ArticleIndex", back_populates="lexicon")
|
||||
indices = relationship("ArticleIndex", back_populates="lexicon")
|
||||
index_rules = relationship("ArticleIndexRule", back_populates="lexicon")
|
||||
content_rules = relationship("ArticleContentRule", back_populates="lexicon")
|
||||
posts = relationship("Post", back_populates="lexicon")
|
||||
|
@ -502,9 +502,13 @@ class ArticleIndex(ModelBase):
|
|||
# Foreign key relationships #
|
||||
#############################
|
||||
|
||||
lexicon = relationship("Lexicon", back_populates="indexes")
|
||||
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):
|
||||
"""
|
||||
|
@ -514,6 +518,7 @@ class ArticleIndexRule(ModelBase):
|
|||
"""
|
||||
|
||||
__tablename__ = "article_index_rule"
|
||||
__table_args__ = (UniqueConstraint("character_id", "index_id", "turn"),)
|
||||
|
||||
###################
|
||||
# Index rule info #
|
||||
|
|
|
@ -392,7 +392,7 @@ def sort_by_index_spec(articles, index_specs, key=None):
|
|||
indexed = OrderedDict()
|
||||
for index in index_list_order:
|
||||
indexed[index.pattern] = []
|
||||
# Sort articles into indexes
|
||||
# Sort articles into indices
|
||||
for article in articles_titlesorted:
|
||||
for index in index_eval_order:
|
||||
if index_match(index, key(article)):
|
||||
|
|
|
@ -201,7 +201,7 @@ ul.unordered-tabs li a[href]:hover {
|
|||
background-color: var(--button-hover);
|
||||
border-color: var(--button-hover);
|
||||
}
|
||||
#index-definition-help {
|
||||
details.setting-help {
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ from amanuensis.server.helpers import (
|
|||
current_lexicon,
|
||||
)
|
||||
|
||||
from .forms import PlayerSettingsForm, SetupSettingsForm, IndexSchemaForm
|
||||
from .forms import (
|
||||
PlayerSettingsForm,
|
||||
SetupSettingsForm,
|
||||
IndexSchemaForm,
|
||||
IndexAssignmentsForm,
|
||||
)
|
||||
|
||||
|
||||
bp = Blueprint("settings", __name__, url_prefix="/settings", template_folder=".")
|
||||
|
@ -159,9 +164,16 @@ def index_post(lexicon_name):
|
|||
# Initialize the form
|
||||
form = IndexSchemaForm()
|
||||
if form.validate():
|
||||
# Valid data, strip out all indexes with the blank type
|
||||
# Valid data, strip out all indices with the blank type
|
||||
indices = [
|
||||
index_def.to_model()
|
||||
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,
|
||||
)
|
||||
for index_def in form.indices.entries
|
||||
if index_def.index_type.data
|
||||
]
|
||||
|
@ -177,6 +189,89 @@ def index_post(lexicon_name):
|
|||
)
|
||||
|
||||
|
||||
@bp.get("/assign/")
|
||||
@lexicon_param
|
||||
@editor_required
|
||||
def assign(lexicon_name):
|
||||
# Get the current assignments
|
||||
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),
|
||||
}
|
||||
for rule in rules
|
||||
]
|
||||
# Add a blank rule to allow for adding rules
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
@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,3 +1,5 @@
|
|||
import uuid
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
|
@ -13,7 +15,7 @@ from wtforms import (
|
|||
from wtforms.validators import Optional, DataRequired, ValidationError
|
||||
from wtforms.widgets.html5 import NumberInput
|
||||
|
||||
from amanuensis.db import ArticleIndex, IndexType
|
||||
from amanuensis.db import IndexType, Lexicon
|
||||
|
||||
|
||||
class PlayerSettingsForm(FlaskForm):
|
||||
|
@ -80,18 +82,47 @@ 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/"""
|
||||
|
||||
indices = FieldList(FormField(IndexDefinitionForm))
|
||||
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/"""
|
||||
|
||||
class Meta:
|
||||
# Disable CSRF on the individual assignment definitions, since the
|
||||
# schema form will have one
|
||||
csrf = False
|
||||
|
||||
turn = IntegerField(widget=NumberInput(min=0, max=99))
|
||||
index = SelectField()
|
||||
character = SelectField(coerce=parse_uuid)
|
||||
|
||||
|
||||
class IndexAssignmentsForm(FlaskForm):
|
||||
"""/lexicon/<name>/settings/assign/"""
|
||||
|
||||
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
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<li>{{ settings_page_link("player", "Player Settings") }}</li>
|
||||
<li>{{ settings_page_link("setup", "Game Setup") }}</li>
|
||||
<li>{{ settings_page_link("index", "Article Indices") }}</li>
|
||||
<li>{{ settings_page_link("assign", "Index Assignments") }}</li>
|
||||
<li>{{ settings_page_link("publish", "Turn Publishing") }}</li>
|
||||
<li>{{ settings_page_link("article", "Article Requirements") }}</li>
|
||||
</ul>
|
||||
|
@ -86,7 +87,7 @@
|
|||
|
||||
{% if page_name == "index" %}
|
||||
<h3>Article Indexes</h3>
|
||||
<details id="index-definition-help">
|
||||
<details class="setting-help">
|
||||
<summary>Index definition help</summary>
|
||||
<p>An index is a rule that matches the title of a lexicon article based on its <em>index type</em> and <em>pattern</em>. A <em>char</em> index matches a title if the first letter of the title (excluding "A", "An", and "The") is one of the letters in the pattern. A <em>range</em> index has a pattern denoting a range of letters, such as "A-F", and matches a title if the first letter of the title is in the range. A <em>prefix</em> index matches any title that begins with the pattern. An <em>etc</em> index always matches a title.</p>
|
||||
<p>When a title is to be sorted under an index, indices are checked in order, sorted first by descending order of <em>logical priority</em>, and then by alphabetical order of index pattern. The title is sorted under the first index that matches it.</p>
|
||||
|
@ -128,6 +129,42 @@
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if page_name == "assign" %}
|
||||
<h3>Index Assignments</h3>
|
||||
<details class="setting-help">
|
||||
<summary>Index assignment help</summary>
|
||||
<p>An index assignment is a rule that requires a player to write an article under certain indices for a particular turn. If more than one rule applies to a player, any index satisfying one of those rules is permitted. If no rule applies to a player, any index is permitted.</p>
|
||||
</details>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<table id="index-definition-table2">
|
||||
<tr>
|
||||
<th>Turn</th>
|
||||
<th>Index</th>
|
||||
<th>Character</th>
|
||||
</tr>
|
||||
{% for rule_form in form.rules %}
|
||||
<tr>
|
||||
<td>{{ rule_form.turn() }}</td>
|
||||
<td>{{ rule_form.index() }}</td>
|
||||
<td>{{ rule_form.character() }}</td>
|
||||
</tr>
|
||||
{% for field in index_form %}
|
||||
{% for error in field.errors %}
|
||||
<tr>
|
||||
<td colspan="5"><span style="color: #ff0000">{{ error }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
{% for message in get_flashed_messages() %}
|
||||
<span style="color:#ff0000">{{ message }}</span><br>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if page_name == "publish" %}
|
||||
<h3>Turn Publishing</h3>
|
||||
{% endif %}
|
||||
|
|
|
@ -4,5 +4,6 @@ pkgs.mkShell {
|
|||
buildInputs = [
|
||||
pkgs.python3
|
||||
pkgs.poetry
|
||||
pkgs.sqlite
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
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 charq, lexiq, memq, userq
|
||||
from amanuensis.backend import *
|
||||
from amanuensis.config import AmanuensisConfig
|
||||
from amanuensis.db import DbContext, User, Lexicon, Membership, Character
|
||||
from amanuensis.db import *
|
||||
from amanuensis.server import get_app
|
||||
|
||||
|
||||
|
@ -122,6 +122,19 @@ 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