Merge pull request #1286 from bookwyrm-social/readthrough-dates

Adds database constraint for read-through dates
This commit is contained in:
Mouse Reeve 2021-08-29 11:50:07 -07:00 committed by GitHub
commit a9716f2fd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 133 additions and 15 deletions

View file

@ -0,0 +1,40 @@
# Generated by Django 3.2.4 on 2021-08-27 17:27
from django.db import migrations, models
import django.db.models.expressions
def normalize_readthrough_dates(app_registry, schema_editor):
"""Find any invalid dates and reset them"""
db_alias = schema_editor.connection.alias
app_registry.get_model("bookwyrm", "ReadThrough").objects.using(db_alias).filter(
start_date__gt=models.F("finish_date")
).update(start_date=models.F("finish_date"))
def reverse_func(apps, schema_editor):
"""nothing to do here"""
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0085_user_saved_lists"),
]
operations = [
migrations.RunPython(normalize_readthrough_dates, reverse_func),
migrations.AlterModelOptions(
name="readthrough",
options={"ordering": ("-start_date",)},
),
migrations.AddConstraint(
model_name="readthrough",
constraint=models.CheckConstraint(
check=models.Q(
("finish_date__gte", django.db.models.expressions.F("start_date"))
),
name="chronology",
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.4 on 2021-08-29 18:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0086_auto_20210827_1727"),
("bookwyrm", "0086_auto_20210828_1724"),
]
operations = []

View file

@ -1,7 +1,8 @@
""" progress in a book """ """ progress in a book """
from django.db import models
from django.utils import timezone
from django.core import validators from django.core import validators
from django.db import models
from django.db.models import F, Q
from django.utils import timezone
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -41,6 +42,16 @@ class ReadThrough(BookWyrmModel):
) )
return None return None
class Meta:
"""Don't let readthroughs end before they start"""
constraints = [
models.CheckConstraint(
check=Q(finish_date__gte=F("start_date")), name="chronology"
)
]
ordering = ("-start_date",)
class ProgressUpdate(BookWyrmModel): class ProgressUpdate(BookWyrmModel):
"""Store progress through a book in the database.""" """Store progress through a book in the database."""

View file

@ -1,7 +1,9 @@
""" testing models """ """ testing models """
import datetime
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils import timezone
from bookwyrm import models from bookwyrm import models
@ -21,27 +23,79 @@ class ReadThrough(TestCase):
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.readthrough = models.ReadThrough.objects.create( def test_valid_date(self):
user=self.user, book=self.edition """can't finish a book before you start it"""
start = timezone.now()
finish = start + datetime.timedelta(days=1)
# just make sure there's no errors
models.ReadThrough.objects.create(
user=self.user,
book=self.edition,
start_date=start,
finish_date=finish,
)
def test_valid_date_null_start(self):
"""can't finish a book before you start it"""
start = timezone.now()
finish = start + datetime.timedelta(days=1)
# just make sure there's no errors
models.ReadThrough.objects.create(
user=self.user,
book=self.edition,
finish_date=finish,
)
def test_valid_date_null_finish(self):
"""can't finish a book before you start it"""
start = timezone.now()
# just make sure there's no errors
models.ReadThrough.objects.create(
user=self.user,
book=self.edition,
start_date=start,
)
def test_valid_date_null(self):
"""can't finish a book before you start it"""
# just make sure there's no errors
models.ReadThrough.objects.create(
user=self.user,
book=self.edition,
)
def test_valid_date_same(self):
"""can't finish a book before you start it"""
start = timezone.now()
# just make sure there's no errors
models.ReadThrough.objects.create(
user=self.user,
book=self.edition,
start_date=start,
finish_date=start,
) )
def test_progress_update(self): def test_progress_update(self):
"""Test progress updates""" """Test progress updates"""
self.readthrough.create_update() # No-op, no progress yet readthrough = models.ReadThrough.objects.create(
self.readthrough.progress = 10 user=self.user, book=self.edition
self.readthrough.create_update() )
self.readthrough.progress = 20
self.readthrough.progress_mode = models.ProgressMode.PERCENT
self.readthrough.create_update()
updates = self.readthrough.progressupdate_set.order_by("created_date").all() readthrough.create_update() # No-op, no progress yet
readthrough.progress = 10
readthrough.create_update()
readthrough.progress = 20
readthrough.progress_mode = models.ProgressMode.PERCENT
readthrough.create_update()
updates = readthrough.progressupdate_set.order_by("created_date").all()
self.assertEqual(len(updates), 2) self.assertEqual(len(updates), 2)
self.assertEqual(updates[0].progress, 10) self.assertEqual(updates[0].progress, 10)
self.assertEqual(updates[0].mode, models.ProgressMode.PAGE) self.assertEqual(updates[0].mode, models.ProgressMode.PAGE)
self.assertEqual(updates[1].progress, 20) self.assertEqual(updates[1].progress, 20)
self.assertEqual(updates[1].mode, models.ProgressMode.PERCENT) self.assertEqual(updates[1].mode, models.ProgressMode.PERCENT)
self.readthrough.progress = -10 readthrough.progress = -10
self.assertRaises(ValidationError, self.readthrough.clean_fields) self.assertRaises(ValidationError, readthrough.clean_fields)
update = self.readthrough.create_update() update = readthrough.create_update()
self.assertRaises(ValidationError, update.clean_fields) self.assertRaises(ValidationError, update.clean_fields)

View file

@ -107,7 +107,7 @@ class ReadingViews(TestCase):
{ {
"post-status": True, "post-status": True,
"privacy": "followers", "privacy": "followers",
"finish_date": "2020-01-07", "finish_date": timezone.now().isoformat(),
"id": readthrough.id, "id": readthrough.id,
}, },
) )