Skip to content

Commit

Permalink
enable multiple posts associate with one piece
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Aug 26, 2023
1 parent 64fd20a commit 6d7295f
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 64 deletions.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ RUN --mount=type=cache,sharing=locked,target=/var/cache/apt-run apt-get update \
gettext-base
RUN busybox --install

# postgresql and redis cli are not required, but install for development convenience
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt-run apt-get install -y --no-install-recommends postgresql-client redis-tools

COPY . /neodb
WORKDIR /neodb
COPY --from=build /neodb-venv /neodb-venv
Expand Down
10 changes: 5 additions & 5 deletions catalog/templates/_item_comments.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
data-uuid="{{ comment.item.uuid }}"><i class="fa-regular fa-circle-play"></i></a>
</span>
{% endif %}
{% if comment.post %}
{% include "action_reply_post.html" with post=comment.post %}
{% include "action_like_post.html" with post=comment.post %}
{% include "action_open_post.html" with post=comment.post %}
{% if comment.latest_post %}
{% include "action_reply_piece.html" with post=comment.latest_post piece=comment %}
{% include "action_like_post.html" with post=comment.latest_post %}
{% include "action_open_post.html" with post=comment.latest_post %}
{% endif %}
</span>
<span>
Expand All @@ -66,7 +66,7 @@
</span>
{% if comment.item != item %}<a href="{{ comment.item_url }}">{{ comment.item.title }}</a>{% endif %}
<div>{{ comment.html|safe }}</div>
{% if comment.post_id %}<div id="replies_{{ comment.post_id }}"></div>{% endif %}
{% if comment.latest_post %}<div id="replies_{{ comment.latest_post.pk }}"></div>{% endif %}
</section>
{% else %}
<a hx-get="{% url 'catalog:comments' comment.item.url_path comment.item.uuid %}?last={{ comment.created_time|date:'Y-m-d H:i:s.uO'|urlencode }}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 4.2.4 on 2023-08-25 18:50

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("takahe", "0001_initial"),
("journal", "0015_use_identity_support_remote_piece"),
]

operations = [
migrations.CreateModel(
name="PiecePost",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"piece",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="journal.piece"
),
),
(
"post",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.CASCADE,
to="takahe.post",
),
),
],
),
migrations.AddField(
model_name="piece",
name="posts",
field=models.ManyToManyField(
related_name="pieces", through="journal.PiecePost", to="takahe.post"
),
),
migrations.AddConstraint(
model_name="piecepost",
constraint=models.UniqueConstraint(
fields=("piece", "post"), name="unique_piece_post"
),
),
]
25 changes: 25 additions & 0 deletions journal/migrations/0017_alter_piece_options_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.4 on 2023-08-26 00:59

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("journal", "0016_piecepost_piece_posts_piecepost_unique_piece_post"),
]

operations = [
migrations.AlterModelOptions(
name="piece",
options={"base_manager_name": "objects"},
),
migrations.RemoveIndex(
model_name="piece",
name="journal_pie_post_id_6a74ff_idx",
),
migrations.RemoveField(
model_name="piece",
name="post_id",
),
]
2 changes: 1 addition & 1 deletion journal/models/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ def update_by_ap_object(cls, owner, item, obj, post_id, visibility):
"text": content,
"local": False,
"remote_id": obj["id"],
"post_id": post_id,
"visibility": visibility,
"created_time": datetime.fromisoformat(obj["published"]),
"edited_time": datetime.fromisoformat(obj["updated"]),
}
p, _ = cls.objects.update_or_create(owner=owner, item=item, defaults=d)
p.link_post_id(post_id)
return p

@property
Expand Down
67 changes: 49 additions & 18 deletions journal/models/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import re
import uuid
from functools import cached_property
from typing import TYPE_CHECKING

from django.conf import settings
from django.db import connection, models
Expand All @@ -10,13 +12,14 @@
from polymorphic.models import PolymorphicModel

from catalog.common.models import AvailableItemCategory, Item, ItemCategory
from catalog.models import *
from catalog.models import item_categories, item_content_types
from takahe.utils import Takahe
from users.models import APIdentity, User

from .mixins import UserOwnedObjectMixin

_logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from takahe.models import Post


class VisibilityType(models.IntegerChoices):
Expand Down Expand Up @@ -117,12 +120,9 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin):
url_path = "p" # subclass must specify this
uid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
local = models.BooleanField(default=True)
post_id = models.BigIntegerField(null=True, default=None)

class Meta:
indexes = [
models.Index(fields=["post_id"]),
]
posts = models.ManyToManyField(
"takahe.Post", related_name="pieces", through="PiecePost"
)

@property
def uuid(self):
Expand All @@ -140,32 +140,32 @@ def absolute_url(self):
def api_url(self):
return f"/api/{self.url}" if self.url_path else None

@property
def post(self):
return Takahe.get_post(self.post_id) if self.post_id else None

@property
def shared_link(self):
return Takahe.get_post_url(self.post_id) if self.post_id else None
return Takahe.get_post_url(self.latest_post.pk) if self.latest_post else None

@property
def like_count(self):
return (
Takahe.get_post_stats(self.post_id).get("likes", 0) if self.post_id else 0
Takahe.get_post_stats(self.latest_post.pk).get("likes", 0)
if self.latest_post
else 0
)

def is_liked_by(self, user):
return self.post_id and Takahe.post_liked_by(self.post_id, user)
return self.latest_post and Takahe.post_liked_by(self.latest_post.pk, user)

@property
def reply_count(self):
return (
Takahe.get_post_stats(self.post_id).get("replies", 0) if self.post_id else 0
Takahe.get_post_stats(self.latest_post.pk).get("replies", 0)
if self.latest_post
else 0
)

def get_replies(self, viewing_identity):
return Takahe.get_post_replies(
self.post_id, viewing_identity.pk if viewing_identity else None
return Takahe.get_replies_for_posts(
self.all_post_ids, viewing_identity.pk if viewing_identity else None
)

@classmethod
Expand All @@ -189,6 +189,37 @@ def update_by_ap_object(cls, owner, item, obj, post_id, visibility):
def ap_object(self):
raise NotImplemented

def link_post_id(self, post_id: int):
PiecePost.objects.get_or_create(piece=self, post_id=post_id)

def link_post(self, post: "Post"):
return self.link_post_id(post.pk)

@cached_property
def latest_post(self):
# local post id is ordered by their created time
pp = PiecePost.objects.filter(piece=self).order_by("-post_id").first()
return Takahe.get_post(pp.post_id) if pp else None # type: ignore

@cached_property
def all_post_ids(self):
post_ids = list(
PiecePost.objects.filter(piece=self).values_list("post_id", flat=True)
)
return post_ids


class PiecePost(models.Model):
piece = models.ForeignKey(Piece, on_delete=models.CASCADE)
post = models.ForeignKey(
"takahe.Post", db_constraint=False, db_index=True, on_delete=models.CASCADE
)

class Meta:
constraints = [
models.UniqueConstraint(fields=["piece", "post"], name="unique_piece_post"),
]


class Content(Piece):
owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT)
Expand Down
4 changes: 2 additions & 2 deletions journal/models/mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from catalog.common import jsondata
from catalog.common.models import Item, ItemCategory
from catalog.common.utils import DEFAULT_ITEM_COVER, piece_cover_path
from catalog.models import *
from mastodon.api import boost_toot
from takahe.utils import Takahe
from users.models import APIdentity
Expand Down Expand Up @@ -137,7 +136,8 @@ def update(
or visibility != self.visibility
)
if shelf_type is None or visibility != self.visibility:
Takahe.delete_mark(self)
if self.shelfmember:
Takahe.delete_posts(self.shelfmember.all_post_ids)
if created_time and created_time >= timezone.now():
created_time = None
post_as_new = shelf_type != self.shelf_type or visibility != self.visibility
Expand Down
2 changes: 1 addition & 1 deletion journal/models/rating.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ def update_by_ap_object(cls, owner, item, obj, post_id, visibility):
"grade": value,
"local": False,
"remote_id": obj["id"],
"post_id": post_id,
"visibility": visibility,
"created_time": datetime.fromisoformat(obj["published"]),
"edited_time": datetime.fromisoformat(obj["updated"]),
}
p, _ = cls.objects.update_or_create(owner=owner, item=item, defaults=d)
p.link_post_id(post_id)
return p

@staticmethod
Expand Down
11 changes: 5 additions & 6 deletions journal/models/shelf.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ def update_by_ap_object(
"position": 0,
"local": False,
# "remote_id": obj["id"],
"post_id": post_id,
"visibility": visibility,
"created_time": datetime.fromisoformat(obj["published"]),
"edited_time": datetime.fromisoformat(obj["updated"]),
}
p, _ = cls.objects.update_or_create(owner=owner, item=item, defaults=d)
p.link_post_id(post_id)
return p

@cached_property
Expand Down Expand Up @@ -277,12 +277,11 @@ def get_latest_members(

@classmethod
def get_action_label(
cls, shelf_type: ShelfType, item_category: ItemCategory
cls, shelf_type: ShelfType | str, item_category: ItemCategory
) -> str:
sts = [
n[2] for n in ShelfTypeNames if n[0] == item_category and n[1] == shelf_type
]
return sts[0] if sts else str(shelf_type)
st = str(shelf_type)
sts = [n[2] for n in ShelfTypeNames if n[0] == item_category and n[1] == st]
return sts[0] if sts else st

@classmethod
def get_label(cls, shelf_type: ShelfType, item_category: ItemCategory):
Expand Down
9 changes: 9 additions & 0 deletions journal/templates/action_reply_piece.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<span>
<a hx-get="{% url 'journal:piece_replies' piece.uuid %}"
hx-swap="outerHTML"
hx-trigger="click once"
hx-target="#replies_{{ post.pk }}">
<i class="fa-solid fa-reply"></i>
{% if post.stats.replies %}<span>{{ post.stats.replies }}</span>{% endif %}
</a>
</span>
4 changes: 2 additions & 2 deletions journal/templatetags/user_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def wish_item_action(context, item):
def like_piece_action(context, piece):
user = context["request"].user
action = {}
if user and user.is_authenticated and piece and piece.post_id:
if user and user.is_authenticated and piece and piece.latest_post:
action = {
"taken": Takahe.post_liked_by(piece.post_id, user),
"taken": Takahe.post_liked_by(piece.latest_post.pk, user),
"url": reverse("journal:like", args=[piece.uuid]),
}
return action
Expand Down
10 changes: 6 additions & 4 deletions journal/views/mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ def like(request: AuthedHttpRequest, piece_uuid):
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
if not piece:
raise Http404()
if piece.post_id:
Takahe.like_post(piece.post_id, request.user.identity.pk)
post = piece.latest_post
if post:
Takahe.like_post(post.pk, request.user.identity.pk)
if request.GET.get("back"):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
elif request.GET.get("stats"):
Expand All @@ -76,8 +77,9 @@ def unlike(request: AuthedHttpRequest, piece_uuid):
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
if not piece:
raise Http404()
if piece.post_id:
Takahe.unlike_post(piece.post_id, request.user.identity.pk)
post = piece.latest_post
if post:
Takahe.unlike_post(post.pk, request.user.identity.pk)
if request.GET.get("back"):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
elif request.GET.get("stats"):
Expand Down
9 changes: 5 additions & 4 deletions journal/views/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from catalog.models import *
from common.utils import (
AuthedHttpRequest,
PageLinksGenerator,
Expand All @@ -23,12 +22,14 @@ def piece_replies(request: AuthedHttpRequest, piece_uuid: str):
if not piece.is_visible_to(request.user):
raise PermissionDenied()
replies = piece.get_replies(request.user.identity)
return render(request, "replies.html", {"post": piece.post, "replies": replies})
return render(
request, "replies.html", {"post": piece.latest_post, "replies": replies}
)


@login_required
def post_replies(request: AuthedHttpRequest, post_id: int):
replies = Takahe.get_post_replies(post_id, request.user.identity.pk)
replies = Takahe.get_replies_for_posts([post_id], request.user.identity.pk)
return render(
request, "replies.html", {"post": Takahe.get_post(post_id), "replies": replies}
)
Expand All @@ -41,7 +42,7 @@ def post_reply(request: AuthedHttpRequest, post_id: int):
if request.method != "POST" or not content:
raise BadRequest()
Takahe.reply_post(post_id, request.user.identity.pk, content, visibility)
replies = Takahe.get_post_replies(post_id, request.user.identity.pk)
replies = Takahe.get_replies_for_posts([post_id], request.user.identity.pk)
return render(
request, "replies.html", {"post": Takahe.get_post(post_id), "replies": replies}
)
Expand Down
Loading

0 comments on commit 6d7295f

Please sign in to comment.