diff --git a/bookwyrm/migrations/0017_auto_20201212_0059.py b/bookwyrm/migrations/0017_auto_20201212_0059.py
new file mode 100644
index 00000000..c9e3fcf4
--- /dev/null
+++ b/bookwyrm/migrations/0017_auto_20201212_0059.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.7 on 2020-12-12 00:59
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bookwyrm', '0016_auto_20201211_2026'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='readthrough',
+ name='book',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bookwyrm.Edition'),
+ ),
+ ]
diff --git a/bookwyrm/migrations/0023_merge_20201216_0112.py b/bookwyrm/migrations/0023_merge_20201216_0112.py
new file mode 100644
index 00000000..e3af4849
--- /dev/null
+++ b/bookwyrm/migrations/0023_merge_20201216_0112.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.7 on 2020-12-16 01:12
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bookwyrm', '0017_auto_20201212_0059'),
+ ('bookwyrm', '0022_auto_20201212_1744'),
+ ]
+
+ operations = [
+ ]
diff --git a/bookwyrm/migrations/0024_merge_20201216_1721.py b/bookwyrm/migrations/0024_merge_20201216_1721.py
new file mode 100644
index 00000000..41f81335
--- /dev/null
+++ b/bookwyrm/migrations/0024_merge_20201216_1721.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.7 on 2020-12-16 17:21
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bookwyrm', '0023_auto_20201214_0511'),
+ ('bookwyrm', '0023_merge_20201216_0112'),
+ ]
+
+ operations = [
+ ]
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
index f0cd3c1d..f2d0279f 100644
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -240,7 +240,7 @@ class Boost(Status):
class ReadThrough(BookWyrmModel):
''' Store progress through a book in the database. '''
user = models.ForeignKey('User', on_delete=models.PROTECT)
- book = models.ForeignKey('Book', on_delete=models.PROTECT)
+ book = models.ForeignKey('Edition', on_delete=models.PROTECT)
pages_read = models.IntegerField(
null=True,
blank=True)
diff --git a/bookwyrm/templates/book.html b/bookwyrm/templates/book.html
index 10c2a27b..3263c2b1 100644
--- a/bookwyrm/templates/book.html
+++ b/bookwyrm/templates/book.html
@@ -91,87 +91,26 @@
{% endif %}
- {% for readthrough in readthroughs %}
-
-
-
-
- {% if readthrough.start_date %}
- Started reading:
- {{ readthrough.start_date | naturalday }}
- {% endif %}
- {% if readthrough.finish_date %}
- Finished reading:
- {{ readthrough.finish_date | naturalday }}
- {% endif %}
-
-
-
-
- Edit read-through dates
-
-
-
-
- Delete this read-through
-
-
-
-
-
-
-
-
+ {# user's relationship to the book #}
-
-
-
-
-
- Delete this read-though?
-
-
-
-
-
-
+ {% for shelf in user_shelves %}
+
+ This edition is on your {{ shelf.shelf.name }} shelf.
+ {% include 'snippets/shelf_selector.html' with current=shelf.shelf %}
+
+ {% endfor %}
+
+ {% for shelf in other_edition_shelves %}
+
+ A different edition of this book is on your {{ shelf.shelf.name }} shelf.
+ {% include 'snippets/switch_edition_button.html' with edition=book %}
+
+ {% endfor %}
+
+ {% for readthrough in readthroughs %}
+ {% include 'snippets/readthrough.html' with readthrough=readthrough %}
+ {% endfor %}
- {% endfor %}
{% if request.user.is_authenticated %}
diff --git a/bookwyrm/templates/snippets/book_tiles.html b/bookwyrm/templates/snippets/book_tiles.html
index 4fc7df31..85f685a8 100644
--- a/bookwyrm/templates/snippets/book_tiles.html
+++ b/bookwyrm/templates/snippets/book_tiles.html
@@ -1,16 +1,11 @@
-
+
{% for book in books %}
- {% if forloop.counter0|divisibleby:"4" %}
-
-
- {% endif %}
{% endfor %}
diff --git a/bookwyrm/templates/snippets/readthrough.html b/bookwyrm/templates/snippets/readthrough.html
new file mode 100644
index 00000000..4d6ca03a
--- /dev/null
+++ b/bookwyrm/templates/snippets/readthrough.html
@@ -0,0 +1,80 @@
+{% load humanize %}
+
+
+
+
+ {% if readthrough.start_date %}
+ Started reading:
+ {{ readthrough.start_date | naturalday }}
+ {% endif %}
+ {% if readthrough.finish_date %}
+ Finished reading:
+ {{ readthrough.finish_date | naturalday }}
+ {% endif %}
+
+
+
+
+ Edit read-through dates
+
+
+
+
+ Delete this read-through
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Delete this read-though?
+
+
+
+
+
+
+
diff --git a/bookwyrm/templates/snippets/shelve_button.html b/bookwyrm/templates/snippets/shelve_button.html
index d452169e..b5470044 100644
--- a/bookwyrm/templates/snippets/shelve_button.html
+++ b/bookwyrm/templates/snippets/shelve_button.html
@@ -4,24 +4,28 @@
{% with book.id|uuid as uuid %}
{% active_shelf book as active_shelf %}
- {% if active_shelf.identifier == 'read' %}
+ {% if switch_mode and active_shelf.book != book %}
+ {% include 'snippets/switch_edition_button.html' with edition=book size='is-small' %}
+ {% else %}
+
+ {% if active_shelf.shelf.identifier == 'read' %}
Read
- {% elif active_shelf.identifier == 'reading' %}
+ {% elif active_shelf.shelf.identifier == 'reading' %}
I'm done!
- {% include 'snippets/finish_reading_modal.html' %}
- {% elif active_shelf.identifier == 'to-read' %}
+ {% include 'snippets/finish_reading_modal.html' with book=active_shelf.book %}
+ {% elif active_shelf.shelf.identifier == 'to-read' %}
Start reading
- {% include 'snippets/start_reading_modal.html' %}
+ {% include 'snippets/start_reading_modal.html' with book=active_shelf.book %}
{% else %}
@@ -40,17 +44,17 @@
{% for shelf in request.user.shelf_set.all %}
- {% if shelf.identifier == 'to-read' %}
+ {% if active_shelf.shelf.identifier == 'to-read' and shelf.identifier == 'reading' %}
Start reading
- {% include 'snippets/start_reading_modal.html' %}
+ {% include 'snippets/start_reading_modal.html' with book=active_shelf.book %}
{% else %}
+ {% endif %}
{% endwith %}
{% endif %}
diff --git a/bookwyrm/templates/snippets/switch_edition_button.html b/bookwyrm/templates/snippets/switch_edition_button.html
new file mode 100644
index 00000000..685aed7c
--- /dev/null
+++ b/bookwyrm/templates/snippets/switch_edition_button.html
@@ -0,0 +1,5 @@
+
diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py
index 849974bf..e6460d58 100644
--- a/bookwyrm/templatetags/bookwyrm_tags.py
+++ b/bookwyrm/templatetags/bookwyrm_tags.py
@@ -132,9 +132,10 @@ def time_since(date):
delta = now - date
if date < (now - relativedelta(weeks=1)):
+ formatter = '%b %-d'
if date.year != now.year:
- return date.strftime('%b %-d %Y')
- return date.strftime('%b %-d')
+ formatter += ' %Y'
+ return date.strftime(formatter)
delta = relativedelta(now, date)
if delta.days:
return '%dd' % delta.days
@@ -150,9 +151,9 @@ def active_shelf(context, book):
''' check what shelf a user has a book on, if any '''
shelf = models.ShelfBook.objects.filter(
shelf__user=context['request'].user,
- book=book
+ book__in=book.parent_work.editions.all()
).first()
- return shelf.shelf if shelf else None
+ return shelf if shelf else {'book': book}
@register.simple_tag(takes_context=False)
diff --git a/bookwyrm/tests/test_view_actions.py b/bookwyrm/tests/test_view_actions.py
index bb0fcdb2..77584c90 100644
--- a/bookwyrm/tests/test_view_actions.py
+++ b/bookwyrm/tests/test_view_actions.py
@@ -237,3 +237,28 @@ class ViewActions(TestCase):
resp = actions.password_reset(request)
self.assertEqual(resp.template_name, 'password_reset.html')
self.assertTrue(models.PasswordReset.objects.exists())
+
+ def test_switch_edition(self):
+ ''' updates user's relationships to a book '''
+ work = models.Work.objects.create(title='test work')
+ edition1 = models.Edition.objects.create(
+ title='first ed', parent_work=work)
+ edition2 = models.Edition.objects.create(
+ title='second ed', parent_work=work)
+ shelf = models.Shelf.objects.create(
+ name='Test Shelf', user=self.local_user)
+ shelf.books.add(edition1)
+ models.ReadThrough.objects.create(
+ user=self.local_user, book=edition1)
+
+ self.assertEqual(models.ShelfBook.objects.get().book, edition1)
+ self.assertEqual(models.ReadThrough.objects.get().book, edition1)
+ request = self.factory.post('', {
+ 'edition': edition2.id
+ })
+ request.user = self.local_user
+ with patch('bookwyrm.broadcast.broadcast_task.delay'):
+ actions.switch_edition(request)
+
+ self.assertEqual(models.ShelfBook.objects.get().book, edition2)
+ self.assertEqual(models.ReadThrough.objects.get().book, edition2)
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
index 3cbb7510..e6c3f79f 100644
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -98,15 +98,16 @@ urlpatterns = [
re_path(r'^edit-profile/?$', actions.edit_profile),
- re_path(r'^import-data/?', actions.import_data),
- re_path(r'^retry-import/?', actions.retry_import),
- re_path(r'^resolve-book/?', actions.resolve_book),
- re_path(r'^edit-book/(?P
\d+)/?', actions.edit_book),
- re_path(r'^upload-cover/(?P\d+)/?', actions.upload_cover),
- re_path(r'^add-description/(?P\d+)/?', actions.add_description),
+ re_path(r'^import-data/?$', actions.import_data),
+ re_path(r'^retry-import/?$', actions.retry_import),
+ re_path(r'^resolve-book/?$', actions.resolve_book),
+ re_path(r'^edit-book/(?P\d+)/?$', actions.edit_book),
+ re_path(r'^upload-cover/(?P\d+)/?$', actions.upload_cover),
+ re_path(r'^add-description/(?P\d+)/?$', actions.add_description),
- re_path(r'^edit-readthrough/?', actions.edit_readthrough),
- re_path(r'^delete-readthrough/?', actions.delete_readthrough),
+ re_path(r'^switch-edition/?$', actions.switch_edition),
+ re_path(r'^edit-readthrough/?$', actions.edit_readthrough),
+ re_path(r'^delete-readthrough/?$', actions.delete_readthrough),
re_path(r'^rate/?$', actions.rate),
re_path(r'^review/?$', actions.review),
diff --git a/bookwyrm/view_actions.py b/bookwyrm/view_actions.py
index f193e127..fcb68476 100644
--- a/bookwyrm/view_actions.py
+++ b/bookwyrm/view_actions.py
@@ -10,6 +10,7 @@ from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, permission_required
from django.core.exceptions import PermissionDenied
from django.core.files.base import ContentFile
+from django.db import transaction
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
@@ -17,6 +18,7 @@ from django.utils import timezone
from django.views.decorators.http import require_GET, require_POST
from bookwyrm import books_manager
+from bookwyrm.broadcast import broadcast
from bookwyrm import forms, models, outgoing
from bookwyrm import goodreads_import
from bookwyrm.emailing import password_reset_email
@@ -246,6 +248,36 @@ def edit_book(request, book_id):
return redirect('/book/%s' % book.id)
+@login_required
+@require_POST
+@transaction.atomic
+def switch_edition(request):
+ ''' switch your copy of a book to a different edition '''
+ edition_id = request.POST.get('edition')
+ new_edition = get_object_or_404(models.Edition, id=edition_id)
+ shelfbooks = models.ShelfBook.objects.filter(
+ book__parent_work=new_edition.parent_work,
+ shelf__user=request.user
+ )
+ for shelfbook in shelfbooks.all():
+ broadcast(request.user, shelfbook.to_remove_activity(request.user))
+
+ shelfbook.book = new_edition
+ shelfbook.save()
+
+ broadcast(request.user, shelfbook.to_add_activity(request.user))
+
+ readthroughs = models.ReadThrough.objects.filter(
+ book__parent_work=new_edition.parent_work,
+ user=request.user
+ )
+ for readthrough in readthroughs.all():
+ readthrough.book = new_edition
+ readthrough.save()
+
+ return redirect('/book/%d' % new_edition.id)
+
+
@login_required
@require_POST
def upload_cover(request, book_id):
diff --git a/bookwyrm/views.py b/bookwyrm/views.py
index f32638d4..097c3577 100644
--- a/bookwyrm/views.py
+++ b/bookwyrm/views.py
@@ -594,8 +594,7 @@ def book_page(request, book_id):
prev_page = '/book/%s/?page=%d' % \
(book_id, reviews_page.previous_page_number())
- user_tags = []
- readthroughs = []
+ user_tags = readthroughs = user_shelves = other_edition_shelves = []
if request.user.is_authenticated:
user_tags = models.UserTag.objects.filter(
book=book, user=request.user
@@ -606,6 +605,16 @@ def book_page(request, book_id):
book=book,
).order_by('start_date')
+ user_shelves = models.ShelfBook.objects.filter(
+ added_by=request.user, book=book
+ )
+
+ other_edition_shelves = models.ShelfBook.objects.filter(
+ ~Q(book=book),
+ added_by=request.user,
+ book__parent_work=book.parent_work,
+ )
+
rating = reviews.aggregate(Avg('rating'))
tags = models.UserTag.objects.filter(
book=book,
@@ -619,6 +628,8 @@ def book_page(request, book_id):
'rating': rating['rating__avg'],
'tags': tags,
'user_tags': user_tags,
+ 'user_shelves': user_shelves,
+ 'other_edition_shelves': other_edition_shelves,
'readthroughs': readthroughs,
'path': '/book/%s' % book_id,
'info_fields': [
@@ -662,10 +673,9 @@ def editions_page(request, book_id):
encoder=ActivityEncoder
)
- editions = models.Edition.objects.filter(parent_work=work).all()
data = {
'title': 'Editions of %s' % work.title,
- 'editions': editions,
+ 'editions': work.editions.all(),
'work': work,
}
return TemplateResponse(request, 'editions.html', data)