Semantic HTML and post feed #23
|
@ -2,12 +2,13 @@
|
|||
Post query interface
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from typing import Optional, Sequence, Tuple
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import select, update, func
|
||||
from sqlalchemy.sql.sqltypes import DateTime
|
||||
|
||||
from amanuensis.db import DbContext, Post
|
||||
from amanuensis.db.models import Lexicon
|
||||
from amanuensis.db.models import Lexicon, Membership
|
||||
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||
|
||||
|
||||
|
@ -47,3 +48,42 @@ def create(
|
|||
db.session.add(new_post)
|
||||
db.session.commit()
|
||||
return new_post
|
||||
|
||||
|
||||
def get_posts_for_membership(
|
||||
db: DbContext, membership_id: int
|
||||
) -> Tuple[Sequence[Post], Sequence[Post]]:
|
||||
"""
|
||||
Returns posts for the membership's lexicon, split into posts that
|
||||
are new since the last view and posts that were previously seen.
|
||||
"""
|
||||
# Save the current timestamp, so we don't miss posts created between now
|
||||
# and when we finish looking stuff up
|
||||
now: DateTime = db(select(func.now())).scalar_one()
|
||||
|
||||
# Save the previous last-seen timestamp for splitting new from old posts,
|
||||
# then update the membership with the current time
|
||||
last_seen: DateTime = db(
|
||||
select(Membership.last_post_seen).where(Membership.id == membership_id)
|
||||
).scalar_one()
|
||||
db(
|
||||
update(Membership)
|
||||
.where(Membership.id == membership_id)
|
||||
.values(last_post_seen=now)
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
# Fetch posts in two groups, new ones after the last-seen time and old ones
|
||||
# If last-seen is null, then just return everything as new
|
||||
new_posts = db(
|
||||
select(Post)
|
||||
.where(last_seen is None or Post.created > last_seen)
|
||||
.order_by(Post.created.desc())
|
||||
).scalars()
|
||||
old_posts = db(
|
||||
select(Post)
|
||||
.where(last_seen is not None and Post.created <= last_seen)
|
||||
.order_by(Post.created.desc())
|
||||
).scalars()
|
||||
|
||||
return new_posts, old_posts
|
||||
|
|
|
@ -251,7 +251,9 @@ class Lexicon(ModelBase):
|
|||
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", order_by="Post.created.desc()")
|
||||
posts = relationship(
|
||||
"Post", back_populates="lexicon", order_by="Post.created.desc()"
|
||||
)
|
||||
|
||||
#######################
|
||||
# Derived information #
|
||||
|
@ -654,7 +656,7 @@ class Post(ModelBase):
|
|||
################
|
||||
|
||||
# The timestamp the post was created
|
||||
created = Column(DateTime, nullable=False, server_default=func.now())
|
||||
created = Column(DateTime, nullable=False, server_default=func.utcnow())
|
||||
|
||||
# The body of the post
|
||||
body = Column(Text, nullable=False)
|
||||
|
|
|
@ -215,6 +215,10 @@ details.setting-help {
|
|||
#index-definition-table td input[type=number] {
|
||||
width: 4em;
|
||||
}
|
||||
section.new-post {
|
||||
padding: 9px;
|
||||
border: 1px dashed red;
|
||||
}
|
||||
p.post-byline {
|
||||
color: #606060;
|
||||
text-align: right;
|
||||
|
|
|
@ -7,7 +7,12 @@ from amanuensis.backend import postq
|
|||
from amanuensis.db import Post
|
||||
from amanuensis.parser import RenderableVisitor, parse_raw_markdown
|
||||
from amanuensis.parser.core import *
|
||||
from amanuensis.server.helpers import lexicon_param, player_required, current_lexicon
|
||||
from amanuensis.server.helpers import (
|
||||
lexicon_param,
|
||||
player_required,
|
||||
current_lexicon,
|
||||
current_membership,
|
||||
)
|
||||
|
||||
from .forms import CreatePostForm
|
||||
|
||||
|
@ -22,10 +27,10 @@ class PostFormatter(RenderableVisitor):
|
|||
return span.innertext
|
||||
|
||||
def LineBreak(self, span: LineBreak):
|
||||
return '<br>'
|
||||
return "<br>"
|
||||
|
||||
def ParsedArticle(self, span: ParsedArticle):
|
||||
return '\n'.join(span.recurse(self))
|
||||
return "\n".join(span.recurse(self))
|
||||
|
||||
def BodyParagraph(self, span: BodyParagraph):
|
||||
return f'<p>{"".join(span.recurse(self))}</p>'
|
||||
|
@ -34,7 +39,7 @@ class PostFormatter(RenderableVisitor):
|
|||
return (
|
||||
'<hr><span class="signature"><p>'
|
||||
f'{"".join(span.recurse(self))}'
|
||||
'</p></span>'
|
||||
"</p></span>"
|
||||
)
|
||||
|
||||
def BoldSpan(self, span: BoldSpan):
|
||||
|
@ -59,11 +64,14 @@ def render_post_body(post: Post) -> str:
|
|||
@player_required
|
||||
def list(lexicon_name):
|
||||
form = CreatePostForm()
|
||||
new_posts, old_posts = postq.get_posts_for_membership(g.db, current_membership.id)
|
||||
return render_template(
|
||||
"posts.jinja",
|
||||
lexicon_name=lexicon_name,
|
||||
form=form,
|
||||
render_post_body=render_post_body,
|
||||
new_posts=new_posts,
|
||||
old_posts=old_posts,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
{% set current_page = "characters" %}
|
||||
{% block title %}Character | {{ lexicon_title }}{% endblock %}
|
||||
|
||||
{% macro make_post(post, is_new) %}
|
||||
<section{% if is_new %} class="new-post"{% endif %}>
|
||||
<p>{{ render_post_body(post) }}</p>
|
||||
<p class="post-byline">Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}</p>
|
||||
</section>
|
||||
{% endmacro %}
|
||||
|
||||
{% block main %}
|
||||
<section>
|
||||
<form action="" method="post" novalidate>
|
||||
|
@ -11,10 +18,10 @@
|
|||
</form>
|
||||
</section>
|
||||
|
||||
{% for post in current_lexicon.posts %}
|
||||
<section>
|
||||
<p>{{ render_post_body(post) }}</p>
|
||||
<p class="post-byline">Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}</p>
|
||||
</section>
|
||||
{% for post in new_posts %}
|
||||
{{ make_post(post, True) }}
|
||||
{% endfor %}
|
||||
{% for post in old_posts %}
|
||||
{{ make_post(post, False) }}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue