From 3cc487f94bff0eee45ee13f0528131af8052eb05 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Fri, 24 Sep 2021 18:04:27 -0700 Subject: [PATCH] Distinguish new posts from already-seen posts --- amanuensis/backend/post.py | 46 +++++++++++++++++++-- amanuensis/db/models.py | 6 ++- amanuensis/resources/page.css | 4 ++ amanuensis/server/lexicon/posts/__init__.py | 16 +++++-- amanuensis/server/lexicon/posts/posts.jinja | 17 +++++--- 5 files changed, 75 insertions(+), 14 deletions(-) diff --git a/amanuensis/backend/post.py b/amanuensis/backend/post.py index 8a170fb..38dbea4 100644 --- a/amanuensis/backend/post.py +++ b/amanuensis/backend/post.py @@ -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 diff --git a/amanuensis/db/models.py b/amanuensis/db/models.py index cc5d2c1..4633e83 100644 --- a/amanuensis/db/models.py +++ b/amanuensis/db/models.py @@ -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) diff --git a/amanuensis/resources/page.css b/amanuensis/resources/page.css index 8fb534b..0a9149d 100644 --- a/amanuensis/resources/page.css +++ b/amanuensis/resources/page.css @@ -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; diff --git a/amanuensis/server/lexicon/posts/__init__.py b/amanuensis/server/lexicon/posts/__init__.py index 9dee642..3d0a7ec 100644 --- a/amanuensis/server/lexicon/posts/__init__.py +++ b/amanuensis/server/lexicon/posts/__init__.py @@ -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 '
' + return "
" def ParsedArticle(self, span: ParsedArticle): - return '\n'.join(span.recurse(self)) + return "\n".join(span.recurse(self)) def BodyParagraph(self, span: BodyParagraph): return f'

{"".join(span.recurse(self))}

' @@ -34,7 +39,7 @@ class PostFormatter(RenderableVisitor): return ( '

' f'{"".join(span.recurse(self))}' - '

' + "

" ) 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, ) diff --git a/amanuensis/server/lexicon/posts/posts.jinja b/amanuensis/server/lexicon/posts/posts.jinja index f68b132..6fca8c6 100644 --- a/amanuensis/server/lexicon/posts/posts.jinja +++ b/amanuensis/server/lexicon/posts/posts.jinja @@ -2,6 +2,13 @@ {% set current_page = "characters" %} {% block title %}Character | {{ lexicon_title }}{% endblock %} +{% macro make_post(post, is_new) %} + +

{{ render_post_body(post) }}

+ + +{% endmacro %} + {% block main %}
@@ -11,10 +18,10 @@
-{% for post in current_lexicon.posts %} -
-

{{ render_post_body(post) }}

- -
+{% for post in new_posts %} +{{ make_post(post, True) }} +{% endfor %} +{% for post in old_posts %} +{{ make_post(post, False) }} {% endfor %} {% endblock %}