Semantic HTML and post feed #23

Merged
Jaculabilis merged 10 commits from tvb/post-feed into develop 2021-10-02 00:18:32 +00:00
5 changed files with 75 additions and 14 deletions
Showing only changes of commit 3cc487f94b - Show all commits

View File

@ -2,12 +2,13 @@
Post query interface 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 import DbContext, Post
from amanuensis.db.models import Lexicon from amanuensis.db.models import Lexicon, Membership
from amanuensis.errors import ArgumentError, BackendArgumentTypeError from amanuensis.errors import ArgumentError, BackendArgumentTypeError
@ -47,3 +48,42 @@ def create(
db.session.add(new_post) db.session.add(new_post)
db.session.commit() db.session.commit()
return new_post 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

View File

@ -251,7 +251,9 @@ class Lexicon(ModelBase):
indices = relationship("ArticleIndex", back_populates="lexicon") indices = relationship("ArticleIndex", back_populates="lexicon")
index_rules = relationship("ArticleIndexRule", back_populates="lexicon") index_rules = relationship("ArticleIndexRule", back_populates="lexicon")
content_rules = relationship("ArticleContentRule", 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 # # Derived information #
@ -654,7 +656,7 @@ class Post(ModelBase):
################ ################
# The timestamp the post was created # 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 # The body of the post
body = Column(Text, nullable=False) body = Column(Text, nullable=False)

View File

@ -215,6 +215,10 @@ details.setting-help {
#index-definition-table td input[type=number] { #index-definition-table td input[type=number] {
width: 4em; width: 4em;
} }
section.new-post {
padding: 9px;
border: 1px dashed red;
}
p.post-byline { p.post-byline {
color: #606060; color: #606060;
text-align: right; text-align: right;

View File

@ -7,7 +7,12 @@ from amanuensis.backend import postq
from amanuensis.db import Post from amanuensis.db import Post
from amanuensis.parser import RenderableVisitor, parse_raw_markdown from amanuensis.parser import RenderableVisitor, parse_raw_markdown
from amanuensis.parser.core import * 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 from .forms import CreatePostForm
@ -22,10 +27,10 @@ class PostFormatter(RenderableVisitor):
return span.innertext return span.innertext
def LineBreak(self, span: LineBreak): def LineBreak(self, span: LineBreak):
return '<br>' return "<br>"
def ParsedArticle(self, span: ParsedArticle): def ParsedArticle(self, span: ParsedArticle):
return '\n'.join(span.recurse(self)) return "\n".join(span.recurse(self))
def BodyParagraph(self, span: BodyParagraph): def BodyParagraph(self, span: BodyParagraph):
return f'<p>{"".join(span.recurse(self))}</p>' return f'<p>{"".join(span.recurse(self))}</p>'
@ -34,7 +39,7 @@ class PostFormatter(RenderableVisitor):
return ( return (
'<hr><span class="signature"><p>' '<hr><span class="signature"><p>'
f'{"".join(span.recurse(self))}' f'{"".join(span.recurse(self))}'
'</p></span>' "</p></span>"
) )
def BoldSpan(self, span: BoldSpan): def BoldSpan(self, span: BoldSpan):
@ -59,11 +64,14 @@ def render_post_body(post: Post) -> str:
@player_required @player_required
def list(lexicon_name): def list(lexicon_name):
form = CreatePostForm() form = CreatePostForm()
new_posts, old_posts = postq.get_posts_for_membership(g.db, current_membership.id)
return render_template( return render_template(
"posts.jinja", "posts.jinja",
lexicon_name=lexicon_name, lexicon_name=lexicon_name,
form=form, form=form,
render_post_body=render_post_body, render_post_body=render_post_body,
new_posts=new_posts,
old_posts=old_posts,
) )

View File

@ -2,6 +2,13 @@
{% set current_page = "characters" %} {% set current_page = "characters" %}
{% block title %}Character | {{ lexicon_title }}{% endblock %} {% 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 %} {% block main %}
<section> <section>
<form action="" method="post" novalidate> <form action="" method="post" novalidate>
@ -11,10 +18,10 @@
</form> </form>
</section> </section>
{% for post in current_lexicon.posts %} {% for post in new_posts %}
<section> {{ make_post(post, True) }}
<p>{{ render_post_body(post) }}</p> {% endfor %}
<p class="post-byline">Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}</p> {% for post in old_posts %}
</section> {{ make_post(post, False) }}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}