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
"""
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

View File

@ -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)

View File

@ -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;

View File

@ -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,
)

View File

@ -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 %}