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) }}
+Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}
+
+{% endmacro %}
+
{% block main %}
-{% for post in current_lexicon.posts %}
-
-{{ render_post_body(post) }}
-Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}
-
+{% for post in new_posts %}
+{{ make_post(post, True) }}
+{% endfor %}
+{% for post in old_posts %}
+{{ make_post(post, False) }}
{% endfor %}
{% endblock %}