diff --git a/bookwyrm/activitypub/book.py b/bookwyrm/activitypub/book.py index 6fa80b32c..680365599 100644 --- a/bookwyrm/activitypub/book.py +++ b/bookwyrm/activitypub/book.py @@ -41,6 +41,7 @@ class Edition(Book): pages: int = None physicalFormat: str = '' publishers: List[str] = field(default_factory=lambda: []) + editionRank: int = 0 type: str = 'Edition' diff --git a/bookwyrm/connectors/bookwyrm_connector.py b/bookwyrm/connectors/bookwyrm_connector.py index 3c6f46145..1f877993d 100644 --- a/bookwyrm/connectors/bookwyrm_connector.py +++ b/bookwyrm/connectors/bookwyrm_connector.py @@ -7,7 +7,11 @@ class Connector(AbstractMinimalConnector): ''' this is basically just for search ''' def get_or_create_book(self, remote_id): - return activitypub.resolve_remote_id(models.Edition, remote_id) + edition = activitypub.resolve_remote_id(models.Edition, remote_id) + work = edition.parent_work + work.default_edition = work.get_default_edition() + work.save() + return edition def parse_search_data(self, data): return data diff --git a/bookwyrm/migrations/0035_edition_edition_rank.py b/bookwyrm/migrations/0035_edition_edition_rank.py new file mode 100644 index 000000000..6ccb2142b --- /dev/null +++ b/bookwyrm/migrations/0035_edition_edition_rank.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.7 on 2021-01-11 17:18 + +import bookwyrm.models.fields +from django.db import migrations + + +def set_rank(app_registry, schema_editor): + db_alias = schema_editor.connection.alias + books = app_registry.get_model('bookwyrm', 'Edition') + for book in books.objects.using(db_alias): + book.edition_rank = book.get_rank + book.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0034_importjob_complete'), + ] + + operations = [ + migrations.AddField( + model_name='edition', + name='edition_rank', + field=bookwyrm.models.fields.IntegerField(default=0), + ), + migrations.RunPython(set_rank), + ] diff --git a/bookwyrm/models/base_model.py b/bookwyrm/models/base_model.py index b212d6939..430097c09 100644 --- a/bookwyrm/models/base_model.py +++ b/bookwyrm/models/base_model.py @@ -52,10 +52,11 @@ def execute_after_save(sender, instance, created, *args, **kwargs): instance.save() -def unfurl_related_field(related_field): +def unfurl_related_field(related_field, sort_field=None): ''' load reverse lookups (like public key owner or Status attachment ''' if hasattr(related_field, 'all'): - return [unfurl_related_field(i) for i in related_field.all()] + return [unfurl_related_field(i) for i in related_field.order_by( + sort_field).all()] if related_field.reverse_unfurl: return related_field.field_to_activity() return related_field.remote_id @@ -145,11 +146,11 @@ class ActivitypubMixin: if hasattr(self, 'serialize_reverse_fields'): # for example, editions of a work - for model_field_name, activity_field_name in \ + for model_field_name, activity_field_name, sort_field in \ self.serialize_reverse_fields: related_field = getattr(self, model_field_name) activity[activity_field_name] = \ - unfurl_related_field(related_field) + unfurl_related_field(related_field, sort_field) if not activity.get('id'): activity['id'] = self.get_remote_id() diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 081895109..49b9f0a91 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -122,20 +122,29 @@ class Work(OrderedCollectionPageMixin, Book): load_remote=False ) + def save(self, *args, **kwargs): + ''' set some fields on the edition object ''' + # set rank + for edition in self.editions.all(): + edition.save() + return super().save(*args, **kwargs) + def get_default_edition(self): ''' in case the default edition is not set ''' - return self.default_edition or self.editions.first() + return self.default_edition or self.editions.order_by( + '-edition_rank' + ).first() def to_edition_list(self, **kwargs): ''' an ordered collection of editions ''' return self.to_ordered_collection( - self.editions.order_by('-updated_date').all(), + self.editions.order_by('-edition_rank').all(), remote_id='%s/editions' % self.remote_id, **kwargs ) activity_serializer = activitypub.Work - serialize_reverse_fields = [('editions', 'editions')] + serialize_reverse_fields = [('editions', 'editions', '-edition_rank')] deserialize_reverse_fields = [('editions', 'editions')] @@ -164,17 +173,39 @@ class Edition(Book): parent_work = fields.ForeignKey( 'Work', on_delete=models.PROTECT, null=True, related_name='editions', activitypub_field='work') + edition_rank = fields.IntegerField(default=0) activity_serializer = activitypub.Edition name_field = 'title' + @property + def get_rank(self): + ''' calculate how complete the data is on this edition ''' + if self.parent_work and self.parent_work.default_edition == self: + # default edition has the highest rank + return 20 + rank = 0 + rank += int(bool(self.cover)) * 3 + rank += int(bool(self.isbn_13)) + rank += int(bool(self.isbn_10)) + rank += int(bool(self.oclc_number)) + rank += int(bool(self.pages)) + rank += int(bool(self.physical_format)) + rank += int(bool(self.description)) + # max rank is 9 + return rank + def save(self, *args, **kwargs): - ''' calculate isbn 10/13 ''' + ''' set some fields on the edition object ''' + # calculate isbn 10/13 if self.isbn_13 and self.isbn_13[:3] == '978' and not self.isbn_10: self.isbn_10 = isbn_13_to_10(self.isbn_13) if self.isbn_10 and not self.isbn_13: self.isbn_13 = isbn_10_to_13(self.isbn_10) + # set rank + self.edition_rank = self.get_rank + return super().save(*args, **kwargs) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 2494c4582..dad659741 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -47,7 +47,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): objects = InheritanceManager() activity_serializer = activitypub.Note - serialize_reverse_fields = [('attachments', 'attachment')] + serialize_reverse_fields = [('attachments', 'attachment', 'id')] deserialize_reverse_fields = [('attachments', 'attachment')] @classmethod diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 4cbe387f4..ef68f9928 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -200,7 +200,7 @@ class KeyPair(ActivitypubMixin, BookWyrmModel): blank=True, null=True, activitypub_field='publicKeyPem') activity_serializer = activitypub.PublicKey - serialize_reverse_fields = [('owner', 'owner')] + serialize_reverse_fields = [('owner', 'owner', 'id')] def get_remote_id(self): # self.owner is set by the OneToOneField on User diff --git a/bookwyrm/tests/models/test_book_model.py b/bookwyrm/tests/models/test_book_model.py index a52133eaf..98d6d446e 100644 --- a/bookwyrm/tests/models/test_book_model.py +++ b/bookwyrm/tests/models/test_book_model.py @@ -82,3 +82,19 @@ class Book(TestCase): self.assertEqual(book.edition_info, 'worm, Glorbish language, 2020') self.assertEqual( book.alt_text, 'Test Edition cover (worm, Glorbish language, 2020)') + + + def test_get_rank(self): + ''' sets the data quality index for the book ''' + # basic rank + self.assertEqual(self.first_edition.edition_rank, 0) + + self.first_edition.description = 'hi' + self.first_edition.save() + self.assertEqual(self.first_edition.edition_rank, 1) + + # default edition + self.work.default_edition = self.first_edition + self.work.save() + self.first_edition.refresh_from_db() + self.assertEqual(self.first_edition.edition_rank, 20) diff --git a/bookwyrm/views.py b/bookwyrm/views.py index cc45bfb27..faacd2ff8 100644 --- a/bookwyrm/views.py +++ b/bookwyrm/views.py @@ -736,7 +736,7 @@ def editions_page(request, book_id): data = { 'title': 'Editions of %s' % work.title, - 'editions': work.editions.all(), + 'editions': work.editions.order_by('-edition_rank').all(), 'work': work, } return TemplateResponse(request, 'editions.html', data)