Repository: bookwyrm-social/bookwyrm

Issue:
create new readthroughs
A user should be able to add a new ReadThrough entry on a book page. Currently, a ReadThrough is created automatically when a user moves a book to the `reading` or `read` shelf. Readthroughs are displayed on the book page for the relevant book and logged in user. (`bookwyrm/templates/book.html`). On the book page, a user should be able to click a button to show a form for creating a new ReadThrough object.

Steps I think this entails
- Create a create readthrough form and toggle to show it on the book page
- Add a url and handler in `bookwyrm/views.py` to create the new readthrough

Note: these aren't federated so no need to handle outgoing/incoming activities


Discussion:

Resolving patch:
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -110,6 +110,7 @@
     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'^create-readthrough/?$', actions.create_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
--- a/bookwyrm/view_actions.py
+++ b/bookwyrm/view_actions.py
@@ -522,6 +522,18 @@ def delete_readthrough(request):
     return redirect(request.headers.get('Referer', '/'))
 
 
+@login_required
+@require_POST
+def create_readthrough(request):
+    ''' can't use the form because the dates are too finnicky '''
+    book = get_object_or_404(models.Edition, id=request.POST.get('book'))
+    readthrough = update_readthrough(request, create=True, book=book)
+    if not readthrough:
+        return redirect(book.local_path)
+    readthrough.save()
+    return redirect(request.headers.get('Referer', '/'))
+
+
 @login_required
 @require_POST
 def rate(request):
@@ -824,7 +836,7 @@ def update_readthrough(request, book=None, create=True):
     start_date = request.POST.get('start_date')
     if start_date:
         try:
-            start_date = dateutil.parser.parse(start_date)
+            start_date = timezone.make_aware(dateutil.parser.parse(start_date))
             readthrough.start_date = start_date
         except ParserError:
             pass
@@ -832,9 +844,13 @@ def update_readthrough(request, book=None, create=True):
     finish_date = request.POST.get('finish_date')
     if finish_date:
         try:
-            finish_date = dateutil.parser.parse(finish_date)
+            finish_date = timezone.make_aware(
+                dateutil.parser.parse(finish_date))
             readthrough.finish_date = finish_date
         except ParserError:
             pass
 
+    if not readthrough.start_date and not readthrough.finish_date:
+        return None
+
     return readthrough


Test patch:
diff --git a/bookwyrm/tests/test_view_actions.py b/bookwyrm/tests/test_view_actions.py
--- a/bookwyrm/tests/test_view_actions.py
+++ b/bookwyrm/tests/test_view_actions.py
@@ -1,12 +1,14 @@
 ''' test for app action functionality '''
 from unittest.mock import patch
 
+import dateutil
 from django.core.exceptions import PermissionDenied
 from django.contrib.auth.models import Group, Permission
 from django.contrib.contenttypes.models import ContentType
 from django.http.response import Http404
 from django.test import TestCase
 from django.test.client import RequestFactory
+from django.utils import timezone
 
 from bookwyrm import forms, models, view_actions as actions
 from bookwyrm.settings import DOMAIN
@@ -358,6 +360,71 @@ def test_edit_author_invalid_form(self):
         self.assertEqual(resp.template_name, 'edit_author.html')
 
 
+    def test_edit_readthrough(self):
+        ''' adding dates to an ongoing readthrough '''
+        start = timezone.make_aware(dateutil.parser.parse('2021-01-03'))
+        readthrough = models.ReadThrough.objects.create(
+            book=self.book, user=self.local_user, start_date=start)
+        request = self.factory.post(
+            '', {
+                'start_date': '2017-01-01',
+                'finish_date': '2018-03-07',
+                'book': '',
+                'id': readthrough.id,
+            })
+        request.user = self.local_user
+
+        actions.edit_readthrough(request)
+        readthrough.refresh_from_db()
+        self.assertEqual(readthrough.start_date.year, 2017)
+        self.assertEqual(readthrough.start_date.month, 1)
+        self.assertEqual(readthrough.start_date.day, 1)
+        self.assertEqual(readthrough.finish_date.year, 2018)
+        self.assertEqual(readthrough.finish_date.month, 3)
+        self.assertEqual(readthrough.finish_date.day, 7)
+        self.assertEqual(readthrough.book, self.book)
+
+
+    def test_delete_readthrough(self):
+        ''' remove a readthrough '''
+        readthrough = models.ReadThrough.objects.create(
+            book=self.book, user=self.local_user)
+        models.ReadThrough.objects.create(
+            book=self.book, user=self.local_user)
+        request = self.factory.post(
+            '', {
+                'id': readthrough.id,
+            })
+        request.user = self.local_user
+
+        actions.delete_readthrough(request)
+        self.assertFalse(
+            models.ReadThrough.objects.filter(id=readthrough.id).exists())
+
+
+    def test_create_readthrough(self):
+        ''' adding new read dates '''
+        request = self.factory.post(
+            '', {
+                'start_date': '2017-01-01',
+                'finish_date': '2018-03-07',
+                'book': self.book.id,
+                'id': '',
+            })
+        request.user = self.local_user
+
+        actions.create_readthrough(request)
+        readthrough = models.ReadThrough.objects.get()
+        self.assertEqual(readthrough.start_date.year, 2017)
+        self.assertEqual(readthrough.start_date.month, 1)
+        self.assertEqual(readthrough.start_date.day, 1)
+        self.assertEqual(readthrough.finish_date.year, 2018)
+        self.assertEqual(readthrough.finish_date.month, 3)
+        self.assertEqual(readthrough.finish_date.day, 7)
+        self.assertEqual(readthrough.book, self.book)
+        self.assertEqual(readthrough.user, self.local_user)
+
+
     def test_tag(self):
         ''' add a tag to a book '''
         request = self.factory.post(