Track Ersatz status with flag instead of null character #21
|
@ -2,8 +2,6 @@
|
||||||
Article query interface
|
Article query interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
|
@ -13,8 +11,8 @@ from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
def create(
|
def create(
|
||||||
db: DbContext,
|
db: DbContext,
|
||||||
lexicon_id: int,
|
lexicon_id: int,
|
||||||
user_id: int,
|
character_id: int,
|
||||||
character_id: Optional[int],
|
ersatz: bool = False,
|
||||||
) -> Article:
|
) -> Article:
|
||||||
"""
|
"""
|
||||||
Create a new article in a lexicon.
|
Create a new article in a lexicon.
|
||||||
|
@ -22,39 +20,21 @@ def create(
|
||||||
# Verify argument types are correct
|
# Verify argument types are correct
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
if not isinstance(user_id, int):
|
|
||||||
raise BackendArgumentTypeError(int, user_id=user_id)
|
|
||||||
if character_id is not None and not isinstance(character_id, int):
|
if character_id is not None and not isinstance(character_id, int):
|
||||||
raise BackendArgumentTypeError(int, character_id=character_id)
|
raise BackendArgumentTypeError(int, character_id=character_id)
|
||||||
|
|
||||||
# Check that the user is a member of this lexicon
|
# Check that the character belongs to the lexicon
|
||||||
mem: Membership = db(
|
character: Character = db(
|
||||||
select(Membership)
|
select(Character).where(Character.id == character_id)
|
||||||
.where(Membership.user_id == user_id)
|
|
||||||
.where(Membership.lexicon_id == lexicon_id)
|
|
||||||
).scalar_one_or_none()
|
).scalar_one_or_none()
|
||||||
if not mem:
|
if not character:
|
||||||
raise ArgumentError("User is not a member of lexicon")
|
raise ArgumentError("Character does not exist")
|
||||||
|
if character.lexicon.id != lexicon_id:
|
||||||
# If the character id is provided, check that the user owns the character
|
raise ArgumentError("Character belongs to the wrong lexicon")
|
||||||
# and the character belongs to the lexicon
|
signature = character.signature if not ersatz else "~Ersatz Scrivener"
|
||||||
if character_id is not None:
|
|
||||||
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.user.id != user_id:
|
|
||||||
raise ArgumentError("Character is owned by the wrong player")
|
|
||||||
if character.lexicon.id != lexicon_id:
|
|
||||||
raise ArgumentError("Character belongs to the wrong lexicon")
|
|
||||||
signature = character.signature
|
|
||||||
else:
|
|
||||||
signature = "~Ersatz Scrivener"
|
|
||||||
|
|
||||||
new_article = Article(
|
new_article = Article(
|
||||||
lexicon_id=lexicon_id,
|
lexicon_id=lexicon_id,
|
||||||
user_id=user_id,
|
|
||||||
character_id=character_id,
|
character_id=character_id,
|
||||||
title="Article title",
|
title="Article title",
|
||||||
body=f"\n\n{signature}",
|
body=f"\n\n{signature}",
|
||||||
|
|
|
@ -83,7 +83,6 @@ def get_for_lexicon(db: DbContext, lexicon_id: int) -> Sequence[ArticleIndex]:
|
||||||
).scalars()
|
).scalars()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> None:
|
def update(db: DbContext, lexicon_id: int, indices: Sequence[ArticleIndex]) -> None:
|
||||||
"""
|
"""
|
||||||
Update the indices for a lexicon. Indices are matched by type and pattern.
|
Update the indices for a lexicon. Indices are matched by type and pattern.
|
||||||
|
|
|
@ -99,7 +99,6 @@ class User(ModelBase):
|
||||||
|
|
||||||
memberships = relationship("Membership", back_populates="user")
|
memberships = relationship("Membership", back_populates="user")
|
||||||
characters = relationship("Character", back_populates="user")
|
characters = relationship("Character", back_populates="user")
|
||||||
articles = relationship("Article", back_populates="user")
|
|
||||||
posts = relationship("Post", back_populates="user")
|
posts = relationship("Post", back_populates="user")
|
||||||
|
|
||||||
#########################
|
#########################
|
||||||
|
@ -393,11 +392,7 @@ class Article(ModelBase):
|
||||||
lexicon_id = Column(Integer, ForeignKey("lexicon.id"), nullable=False)
|
lexicon_id = Column(Integer, ForeignKey("lexicon.id"), nullable=False)
|
||||||
|
|
||||||
# The character who is the author of this article
|
# The character who is the author of this article
|
||||||
# If this is NULL, the article is written by Ersatz Scrivener
|
character_id = Column(Integer, ForeignKey("character.id"), nullable=False)
|
||||||
character_id = Column(Integer, ForeignKey("character.id"), nullable=True)
|
|
||||||
|
|
||||||
# The user who owns this article
|
|
||||||
user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
|
|
||||||
|
|
||||||
# The article to which this is an addendum
|
# The article to which this is an addendum
|
||||||
addendum_to = Column(Integer, ForeignKey("article.id"), nullable=True)
|
addendum_to = Column(Integer, ForeignKey("article.id"), nullable=True)
|
||||||
|
@ -416,6 +411,9 @@ class Article(ModelBase):
|
||||||
# The number of times the article has been submitted
|
# The number of times the article has been submitted
|
||||||
submit_nonce = Column(Integer, nullable=False, default=0)
|
submit_nonce = Column(Integer, nullable=False, default=0)
|
||||||
|
|
||||||
|
# Whether the article is an Ersatz Scrivener article
|
||||||
|
ersatz = Column(Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# History tracking #
|
# History tracking #
|
||||||
####################
|
####################
|
||||||
|
@ -449,7 +447,6 @@ class Article(ModelBase):
|
||||||
|
|
||||||
lexicon = relationship("Lexicon", back_populates="articles")
|
lexicon = relationship("Lexicon", back_populates="articles")
|
||||||
character = relationship("Character", back_populates="articles")
|
character = relationship("Character", back_populates="articles")
|
||||||
user = relationship("User", back_populates="articles")
|
|
||||||
addenda = relationship("Article", backref=backref("parent", remote_side=[id]))
|
addenda = relationship("Article", backref=backref("parent", remote_side=[id]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,43 +16,26 @@ def test_create_article(db: DbContext, make: ObjectFactory):
|
||||||
lexicon1: Lexicon = make.lexicon()
|
lexicon1: Lexicon = make.lexicon()
|
||||||
make.membership(user_id=user1.id, lexicon_id=lexicon1.id)
|
make.membership(user_id=user1.id, lexicon_id=lexicon1.id)
|
||||||
make.membership(user_id=user2.id, lexicon_id=lexicon1.id)
|
make.membership(user_id=user2.id, lexicon_id=lexicon1.id)
|
||||||
char1_1: Character = make.character(lexicon_id=lexicon1.id, user_id=user1.id)
|
char_l1_u1: Character = make.character(lexicon_id=lexicon1.id, user_id=user1.id)
|
||||||
char1_2: Character = make.character(lexicon_id=lexicon1.id, user_id=user2.id)
|
char_l1_u2: Character = make.character(lexicon_id=lexicon1.id, user_id=user2.id)
|
||||||
|
|
||||||
# Create a lexicon that only one user is in
|
# Create a lexicon that only one user is in
|
||||||
lexicon2: Lexicon = make.lexicon()
|
lexicon2: Lexicon = make.lexicon()
|
||||||
make.membership(user_id=user2.id, lexicon_id=lexicon2.id)
|
make.membership(user_id=user2.id, lexicon_id=lexicon2.id)
|
||||||
char2_2: Character = make.character(lexicon_id=lexicon2.id, user_id=user2.id)
|
char_l2_u2: Character = make.character(lexicon_id=lexicon2.id, user_id=user2.id)
|
||||||
|
|
||||||
# User cannot create article for another user's character
|
# Characters can't own articles in other lexicons
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(ArgumentError):
|
||||||
artiq.create(db, lexicon1.id, user1.id, char1_2.id)
|
artiq.create(db, lexicon1.id, char_l2_u2.id)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(ArgumentError):
|
||||||
artiq.create(db, lexicon1.id, user2.id, char1_1.id)
|
artiq.create(db, lexicon2.id, char_l1_u1.id)
|
||||||
|
|
||||||
# User cannot create article for their character in the wrong lexicon
|
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(ArgumentError):
|
||||||
artiq.create(db, lexicon1.id, user2.id, char2_2.id)
|
artiq.create(db, lexicon2.id, char_l1_u2.id)
|
||||||
with pytest.raises(ArgumentError):
|
|
||||||
artiq.create(db, lexicon2.id, user2.id, char1_2.id)
|
|
||||||
|
|
||||||
# User cannot create article in a lexicon they aren't in
|
|
||||||
with pytest.raises(ArgumentError):
|
|
||||||
artiq.create(db, lexicon2.id, user1.id, char1_1.id)
|
|
||||||
|
|
||||||
# User cannot create anonymous articles in a lexicon they aren't in
|
|
||||||
with pytest.raises(ArgumentError):
|
|
||||||
artiq.create(db, lexicon2.id, user1.id, character_id=None)
|
|
||||||
|
|
||||||
# Users can create character-owned articles
|
# Users can create character-owned articles
|
||||||
assert artiq.create(db, lexicon1.id, user1.id, char1_1.id)
|
assert artiq.create(db, lexicon1.id, char_l1_u1.id)
|
||||||
assert artiq.create(db, lexicon1.id, user2.id, char1_2.id)
|
assert artiq.create(db, lexicon1.id, char_l1_u2.id)
|
||||||
assert artiq.create(db, lexicon2.id, user2.id, char2_2.id)
|
assert artiq.create(db, lexicon2.id, char_l2_u2.id)
|
||||||
|
|
||||||
# Users can create anonymous articles
|
|
||||||
assert artiq.create(db, lexicon1.id, user1.id, character_id=None)
|
|
||||||
assert artiq.create(db, lexicon1.id, user2.id, character_id=None)
|
|
||||||
assert artiq.create(db, lexicon2.id, user2.id, character_id=None)
|
|
||||||
|
|
||||||
|
|
||||||
def test_article_update_ts(db: DbContext, make: ObjectFactory):
|
def test_article_update_ts(db: DbContext, make: ObjectFactory):
|
||||||
|
@ -61,7 +44,7 @@ def test_article_update_ts(db: DbContext, make: ObjectFactory):
|
||||||
lexicon: Lexicon = make.lexicon()
|
lexicon: Lexicon = make.lexicon()
|
||||||
make.membership(user_id=user.id, lexicon_id=lexicon.id)
|
make.membership(user_id=user.id, lexicon_id=lexicon.id)
|
||||||
char: Character = make.character(lexicon_id=lexicon.id, user_id=user.id)
|
char: Character = make.character(lexicon_id=lexicon.id, user_id=user.id)
|
||||||
article = artiq.create(db, lexicon.id, user.id, char.id)
|
article = artiq.create(db, lexicon.id, char.id)
|
||||||
created = article.last_updated
|
created = article.last_updated
|
||||||
time.sleep(1) # The update timestamp has only second-level precision
|
time.sleep(1) # The update timestamp has only second-level precision
|
||||||
article.title = "New title, who dis"
|
article.title = "New title, who dis"
|
||||||
|
|
Loading…
Reference in New Issue