diff --git a/bookwyrm/migrations/0041_auto_20210131_0500.py b/bookwyrm/migrations/0041_auto_20210131_0500.py new file mode 100644 index 000000000..eb00e3f37 --- /dev/null +++ b/bookwyrm/migrations/0041_auto_20210131_0500.py @@ -0,0 +1,65 @@ +# Generated by Django 3.0.7 on 2021-01-31 05:00 + +import bookwyrm.models.base_model +import bookwyrm.models.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0040_auto_20210122_0057'), + ] + + operations = [ + migrations.CreateModel( + name='List', + 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', bookwyrm.models.fields.RemoteIdField(max_length=255, null=True, validators=[bookwyrm.models.fields.validate_remote_id])), + ('name', bookwyrm.models.fields.CharField(max_length=100)), + ('description', bookwyrm.models.fields.TextField(blank=True, null=True)), + ('privacy', bookwyrm.models.fields.CharField(choices=[('public', 'Public'), ('unlisted', 'Unlisted'), ('followers', 'Followers'), ('direct', 'Direct')], default='public', max_length=255)), + ('curation', bookwyrm.models.fields.CharField(choices=[('closed', 'Closed'), ('open', 'Open'), ('moderated', 'Moderated')], default='closed', max_length=255)), + ], + options={ + 'abstract': False, + }, + bases=(bookwyrm.models.base_model.OrderedCollectionMixin, models.Model), + ), + migrations.CreateModel( + name='ListItem', + 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', bookwyrm.models.fields.RemoteIdField(max_length=255, null=True, validators=[bookwyrm.models.fields.validate_remote_id])), + ('notes', bookwyrm.models.fields.TextField(blank=True, null=True)), + ('approved', models.BooleanField(default=True)), + ('order', bookwyrm.models.fields.IntegerField(blank=True, null=True)), + ('added_by', bookwyrm.models.fields.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('book', bookwyrm.models.fields.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bookwyrm.Edition')), + ('book_list', bookwyrm.models.fields.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bookwyrm.List')), + ('endorsement', models.ManyToManyField(related_name='endorsers', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-created_date',), + 'unique_together': {('book', 'book_list')}, + }, + bases=(bookwyrm.models.base_model.ActivitypubMixin, models.Model), + ), + migrations.AddField( + model_name='list', + name='books', + field=models.ManyToManyField(through='bookwyrm.ListItem', to='bookwyrm.Edition'), + ), + migrations.AddField( + model_name='list', + name='user', + field=bookwyrm.models.fields.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index b232e98fa..0aef63850 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -7,6 +7,7 @@ from .author import Author from .connector import Connector from .shelf import Shelf, ShelfBook +from .list import List, ListItem from .status import Status, GeneratedNote, Review, Comment, Quotation from .status import Boost diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py new file mode 100644 index 000000000..2bc49261a --- /dev/null +++ b/bookwyrm/models/list.py @@ -0,0 +1,83 @@ +''' make a list of books!! ''' +from django.db import models + +from bookwyrm import activitypub +from .base_model import ActivitypubMixin, BookWyrmModel +from .base_model import OrderedCollectionMixin +from . import fields + + +CurationType = models.TextChoices('Curation', [ + 'closed', + 'open', + 'moderated', +]) + +class List(OrderedCollectionMixin, BookWyrmModel): + ''' a list of books ''' + name = fields.CharField(max_length=100) + user = fields.ForeignKey( + 'User', on_delete=models.PROTECT, activitypub_field='owner') + description = fields.TextField(blank=True, null=True) + privacy = fields.CharField( + max_length=255, + default='public', + choices=fields.PrivacyLevels.choices + ) + curation = fields.CharField( + max_length=255, + default='closed', + choices=CurationType.choices + ) + books = models.ManyToManyField( + 'Edition', + symmetrical=False, + through='ListItem', + through_fields=('book_list', 'book'), + ) + @property + def collection_queryset(self): + ''' list of books for this shelf, overrides OrderedCollectionMixin ''' + return self.books.all().order_by('listitem') + + +class ListItem(ActivitypubMixin, BookWyrmModel): + ''' ok ''' + book = fields.ForeignKey( + 'Edition', on_delete=models.PROTECT, activitypub_field='object') + book_list = fields.ForeignKey( + 'List', on_delete=models.CASCADE, activitypub_field='target') + added_by = fields.ForeignKey( + 'User', + on_delete=models.PROTECT, + activitypub_field='actor' + ) + notes = fields.TextField(blank=True, null=True) + approved = models.BooleanField(default=True) + order = fields.IntegerField(blank=True, null=True) + endorsement = models.ManyToManyField('User', related_name='endorsers') + + activity_serializer = activitypub.AddBook + + def to_add_activity(self, user): + ''' AP for shelving a book''' + return activitypub.Add( + id='%s#add' % self.remote_id, + actor=user.remote_id, + object=self.book.to_activity(), + target=self.book_list.remote_id, + ).serialize() + + def to_remove_activity(self, user): + ''' AP for un-shelving a book''' + return activitypub.Remove( + id='%s#remove' % self.remote_id, + actor=user.remote_id, + object=self.book.to_activity(), + target=self.book_list.to_activity() + ).serialize() + + class Meta: + ''' an opinionated constraint! you can't put a book on a list twice ''' + unique_together = ('book', 'book_list') + ordering = ('-created_date',)