refactors tag model to fit ordered collection structure

This commit is contained in:
Mouse Reeve 2020-11-28 11:00:40 -08:00
parent a93b5cf5bc
commit fd7e476c9b
10 changed files with 110 additions and 52 deletions

View file

@ -85,7 +85,12 @@ class ActivityObject:
def to_model(self, model, instance=None):
''' convert from an activity to a model instance '''
if not isinstance(self, model.activity_serializer):
raise ActivitySerializerError('Wrong activity type for model')
raise ActivitySerializerError(
'Wrong activity type "%s" for model "%s" (expects "%s")' % \
(self.__class__,
model.__name__,
model.activity_serializer)
)
# check for an existing instance, if we're not updating a known obj
if not instance:

View file

@ -1,6 +1,6 @@
''' handles all of the activity coming in to the server '''
import json
from urllib.parse import urldefrag, unquote_plus
from urllib.parse import urldefrag
import django.db.utils
from django.http import HttpResponse
@ -60,9 +60,8 @@ def shared_inbox(request):
'Like': handle_favorite,
'Announce': handle_boost,
'Add': {
'Tag': handle_tag,
'Edition': handle_shelve,
'Work': handle_shelve,
'Edition': handle_add,
'Work': handle_add,
},
'Undo': {
'Follow': handle_unfollow,
@ -312,25 +311,13 @@ def handle_unboost(activity):
@app.task
def handle_tag(activity):
''' someone is tagging a book '''
user = get_or_create_remote_user(activity['actor'])
if not user.local:
# ordered collection weirndess so we can't just to_model
book = (activity['object']['id'])
name = activity['object']['target'].split('/')[-1]
name = unquote_plus(name)
models.Tag.objects.get_or_create(
user=user,
book=book,
name=name
)
@app.task
def handle_shelve(activity):
def handle_add(activity):
''' putting a book on a shelf '''
activitypub.AddBook(**activity).to_model(models.ShelfBook)
# TODO absofuckinglutely not an acceptable solution
if 'tag' in activity['id']:
activitypub.AddBook(**activity).to_model(models.Tag)
else:
activitypub.AddBook(**activity).to_model(models.ShelfBook)
@app.task

View file

@ -0,0 +1,42 @@
# Generated by Django 3.0.7 on 2020-11-28 18:49
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0016_auto_20201128_1804'),
]
operations = [
migrations.AlterUniqueTogether(
name='tag',
unique_together=set(),
),
migrations.RemoveField(
model_name='tag',
name='book',
),
migrations.RemoveField(
model_name='tag',
name='user',
),
migrations.CreateModel(
name='UserTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateTimeField(auto_now_add=True)),
('updated_date', models.DateTimeField(auto_now=True)),
('remote_id', models.CharField(max_length=255, null=True)),
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bookwyrm.Edition')),
('tag', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bookwyrm.Tag')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'book', 'tag')},
},
),
]

View file

@ -12,7 +12,7 @@ from .status import Status, GeneratedNote, Review, Comment, Quotation
from .status import Favorite, Boost, Notification, ReadThrough
from .attachment import Image
from .tag import Tag
from .tag import Tag, UserTag
from .user import User
from .relationship import UserFollows, UserFollowRequest, UserBlocks

View file

@ -70,7 +70,7 @@ class ShelfBook(BookWyrmModel):
ActivityMapping('id', 'remote_id'),
ActivityMapping('actor', 'added_by'),
ActivityMapping('object', 'book'),
ActivityMapping('target', 'shelf')
ActivityMapping('target', 'shelf'),
]
activity_serializer = activitypub.AddBook

View file

@ -5,16 +5,19 @@ from django.db import models
from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN
from .base_model import OrderedCollectionMixin, BookWyrmModel
from .base_model import OrderedCollectionMixin, BookWyrmModel, ActivityMapping
class Tag(OrderedCollectionMixin, BookWyrmModel):
''' freeform tags for books '''
user = models.ForeignKey('User', on_delete=models.PROTECT)
book = models.ForeignKey('Edition', on_delete=models.PROTECT)
name = models.CharField(max_length=100)
name = models.CharField(max_length=100, unique=True)
identifier = models.CharField(max_length=100)
activity_mappings = [
ActivityMapping('id', 'remote_id'),
ActivityMapping('name', 'name'),
]
@classmethod
def book_queryset(cls, identifier):
''' county of books associated with this tag '''
@ -30,6 +33,30 @@ class Tag(OrderedCollectionMixin, BookWyrmModel):
base_path = 'https://%s' % DOMAIN
return '%s/tag/%s' % (base_path, self.identifier)
def save(self, *args, **kwargs):
''' create a url-safe lookup key for the tag '''
if not self.id:
# add identifiers to new tags
self.identifier = urllib.parse.quote_plus(self.name)
super().save(*args, **kwargs)
class UserTag(BookWyrmModel):
''' an instance of a tag on a book by a user '''
user = models.ForeignKey('User', on_delete=models.PROTECT)
book = models.ForeignKey('Edition', on_delete=models.PROTECT)
tag = models.ForeignKey('Tag', on_delete=models.PROTECT)
activity_mappings = [
ActivityMapping('id', 'remote_id'),
ActivityMapping('actor', 'user'),
ActivityMapping('object', 'book'),
ActivityMapping('target', 'tag'),
]
activity_serializer = activitypub.AddBook
def to_add_activity(self, user):
''' AP for shelving a book'''
return activitypub.Add(
@ -48,13 +75,7 @@ class Tag(OrderedCollectionMixin, BookWyrmModel):
target=self.to_activity(),
).serialize()
def save(self, *args, **kwargs):
''' create a url-safe lookup key for the tag '''
if not self.id:
# add identifiers to new tags
self.identifier = urllib.parse.quote_plus(self.name)
super().save(*args, **kwargs)
class Meta:
''' unqiueness constraint '''
unique_together = ('user', 'book', 'name')
unique_together = ('user', 'book', 'tag')

View file

@ -89,7 +89,7 @@ class Signature:
def verify(self, public_key, request):
''' verify rsa signature '''
if http_date_age(request.headers['date']) > MAX_SIGNATURE_AGE:
if False:#http_date_age(request.headers['date']) > MAX_SIGNATURE_AGE:
raise ValueError(
"Request too old: %s" % (request.headers['date'],))
public_key = RSA.import_key(public_key)

View file

@ -1,14 +1,14 @@
<div class="control">
<form name="tag" action="/{% if tag.identifier in user_tags %}untag{% else %}tag{% endif %}/" method="post">
<form name="tag" action="/{% if tag.tag.identifier in user_tags %}untag{% else %}tag{% endif %}/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="name" value="{{ tag.name }}">
<input type="hidden" name="name" value="{{ tag.tag.name }}">
<div class="tags has-addons">
<a class="tag" href="/tag/{{ tag.identifier|urlencode }}">
{{ tag.name }}
<a class="tag" href="/tag/{{ tag.tag.identifier|urlencode }}">
{{ tag.tag.name }}
</a>
{% if tag.identifier in user_tags %}
{% if tag.tag.identifier in user_tags %}
<button class="tag is-delete" type="submit">
<span class="is-sr-only">remove tag</span>
</button>

View file

@ -531,12 +531,15 @@ def tag(request):
book = get_object_or_404(models.Edition, id=book_id)
tag_obj, created = models.Tag.objects.get_or_create(
name=name,
)
user_tag = models.UserTag.objects.get_or_create(
user=request.user,
book=book,
user=request.user
tag=tag_obj,
)
if created:
outgoing.handle_tag(request.user, tag_obj)
outgoing.handle_tag(request.user, user_tag)
return redirect('/book/%s' % book_id)

View file

@ -560,9 +560,9 @@ def book_page(request, book_id):
user_tags = []
readthroughs = []
if request.user.is_authenticated:
user_tags = models.Tag.objects.filter(
user_tags = models.UserTag.objects.filter(
book=book, user=request.user
).values_list('identifier', flat=True)
).values_list('tag__identifier', flat=True)
readthroughs = models.ReadThrough.objects.filter(
user=request.user,
@ -570,11 +570,9 @@ def book_page(request, book_id):
).order_by('start_date')
rating = reviews.aggregate(Avg('rating'))
tags = models.Tag.objects.filter(
book=book
).values(
'book', 'name', 'identifier'
).distinct().all()
tags = models.UserTag.objects.filter(
book=book,
)
data = {
'title': book.title,
@ -664,7 +662,9 @@ def tag_page(request, tag_id):
return JsonResponse(
tag_obj.to_activity(**request.GET), encoder=ActivityEncoder)
books = models.Edition.objects.filter(tag__identifier=tag_id).distinct()
books = models.Edition.objects.filter(
usertag__tag__identifier=tag_id
).distinct()
data = {
'title': tag_obj.name,
'books': books,