From 4d6e02fedcf1e4e4e2a03f588e5914d0ff4ce68e Mon Sep 17 00:00:00 2001 From: Aleksei Pirogov Date: Wed, 10 Feb 2021 21:52:45 +0300 Subject: [PATCH] add save/load of the game state, make the router "cheat-proof" --- djaif/book/migrations/0012_progresssave.py | 23 ++++++ .../0013_progresssave_updated_at.py | 18 +++++ djaif/book/models.py | 29 +++++++ djaif/book/templates/page.html | 6 +- djaif/book/templates/saves.html | 28 +++++++ djaif/book/urls.py | 25 ++++++- djaif/book/views.py | 75 +++++++++++++------ 7 files changed, 177 insertions(+), 27 deletions(-) create mode 100644 djaif/book/migrations/0012_progresssave.py create mode 100644 djaif/book/migrations/0013_progresssave_updated_at.py create mode 100644 djaif/book/templates/saves.html diff --git a/djaif/book/migrations/0012_progresssave.py b/djaif/book/migrations/0012_progresssave.py new file mode 100644 index 0000000..24c4932 --- /dev/null +++ b/djaif/book/migrations/0012_progresssave.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.5 on 2021-02-10 17:01 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('book', '0011_auto_20200526_1529'), + ] + + operations = [ + migrations.CreateModel( + name='ProgressSave', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('book_page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='book.BookPage')), + ('items', models.ManyToManyField(blank=True, to='book.Item')), + ('progress', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='book.BookProgress')), + ], + ), + ] diff --git a/djaif/book/migrations/0013_progresssave_updated_at.py b/djaif/book/migrations/0013_progresssave_updated_at.py new file mode 100644 index 0000000..53301ed --- /dev/null +++ b/djaif/book/migrations/0013_progresssave_updated_at.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2021-02-10 17:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book', '0012_progresssave'), + ] + + operations = [ + migrations.AddField( + model_name='progresssave', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/djaif/book/models.py b/djaif/book/models.py index eaaf317..a1d6fa8 100644 --- a/djaif/book/models.py +++ b/djaif/book/models.py @@ -67,6 +67,35 @@ def start_reading(cls, user, book): progress.save() return progress + def save_to(self, save_id): + if save_id is None: + state = ProgressSave.objects.create( + progress=self, + book_page=self.book_page, + ) + state.items.set(self.items.all()) + else: + state = ProgressSave.objects.get(id=save_id) + state.book_page=self.book_page + state.save() + state.items.set(self.items.all()) + + def load_from(self, save_id): + state = ProgressSave.objects.get(id=save_id) + self.book_page=state.book_page + self.save() + self.items.set(state.items.all()) + + +class ProgressSave(models.Model): + progress = models.ForeignKey(BookProgress, on_delete=models.CASCADE) + + updated_at = models.DateTimeField(auto_now=True) + + book_page = models.ForeignKey(BookPage, on_delete=models.CASCADE) + + items = models.ManyToManyField('book.Item', blank=True) # noqa: WPS110 + class Item(models.Model): name = models.TextField() diff --git a/djaif/book/templates/page.html b/djaif/book/templates/page.html index 5560db8..c45eb45 100644 --- a/djaif/book/templates/page.html +++ b/djaif/book/templates/page.html @@ -14,7 +14,7 @@

Вы видите

{% endif %} +
+ Сохранения diff --git a/djaif/book/templates/saves.html b/djaif/book/templates/saves.html new file mode 100644 index 0000000..278cb25 --- /dev/null +++ b/djaif/book/templates/saves.html @@ -0,0 +1,28 @@ + + + + + {{ book.title }}: Сохранения + + +

+ {{ book.title }} +

+

+ {{ page.title }} +

+ [Продолжить игру] +
+ [Новое сохранение] + + + diff --git a/djaif/book/urls.py b/djaif/book/urls.py index 7065351..1796a57 100644 --- a/djaif/book/urls.py +++ b/djaif/book/urls.py @@ -5,11 +5,28 @@ urlpatterns = [ path('', views.view_books), path('book/', views.view_book, name='book'), - path('book//page/', views.view_page, name='page'), + path('book//go/', views.go_to, name='go_to'), + path('book//take/', views.take, name='take'), + path('book//saves', views.view_saves, name='saves'), path( - 'book//page//take/', - views.take_item, - name='take', + 'book//saves/new', + views.save_to, + name='save_new', + ), + path( + 'book//saves/save_to/', + views.save_to, + name='save_to', + ), + path( + 'book//saves/load_from/', + views.load_from, + name='load_from', + ), + path( + 'book//saves/delete/', + views.delete_save, + name='delete_save', ), path('book//map.svg', views.view_book_map), ] diff --git a/djaif/book/views.py b/djaif/book/views.py index ace419e..8cd6016 100644 --- a/djaif/book/views.py +++ b/djaif/book/views.py @@ -9,6 +9,10 @@ from djaif.book import models +def _return_to(book_id): + return redirect(reverse('book', kwargs={'book_id': book_id})) + + def on_progress(view): @wraps(view) # noqa: WPS430 def inner(request, book_id, **kwargs): @@ -32,33 +36,19 @@ def view_books(request): context={'books': models.Book.objects.all()}, ) - def view_book(request, book_id): book = get_object_or_404(models.Book, id=book_id) - if not book.first_page: - return render(request, 'book.html', context={'book': book}) + assert book.first_page try: progress = models.BookProgress.objects.get( book=book, user=request.user, ) except models.BookProgress.DoesNotExist: progress = models.BookProgress.start_reading( - user=request.user, book=book, + book=book, user=request.user, ) - return redirect( - reverse( - 'page', - kwargs={'book_id': book.id, 'page_id': progress.book_page.id}, - ), - ) - -@on_progress -def view_page(request, progress, book_id, page_id): - page = get_object_or_404(models.BookPage, book__id=book_id, id=page_id) - - progress.book_page = page - progress.save() + page = progress.book_page links = [ (link, link.has_all_needed(list(progress.items.all()))) @@ -78,16 +68,59 @@ def view_page(request, progress, book_id, page_id): @on_progress -def take_item(request, progress, book_id, page_id, item_id): +def go_to(request, progress, book_id, pagelink_id): + link = get_object_or_404(models.PageLink, id=pagelink_id) + if ( + link.from_page.id == progress.book_page.id + and + link.has_all_needed(progress.items.all()) + ): + progress.book_page = link.to_page + progress.save() + return _return_to(book_id) + + +@on_progress +def take(request, progress, book_id, item_id): item = get_object_or_404(models.Item, id=item_id) # noqa: WPS110 - progress.items.add(item) + if item in progress.book_page.items.all(): + progress.items.add(item) + + return _return_to(book_id) + - return redirect( - reverse('page', kwargs={'book_id': book_id, 'page_id': page_id}), +@on_progress +def view_saves(request, progress, book_id): + saves = progress.progresssave_set.order_by('-updated_at').all() + return render( + request, + 'saves.html', + context={ + 'book': progress.book, + 'page': progress.book_page, + 'saves': saves, + }, ) +@on_progress +def save_to(request, progress, book_id, save_id=None): + progress.save_to(save_id) + return redirect(reverse('saves', kwargs={'book_id': book_id})) + + +@on_progress +def load_from(request, progress, book_id, save_id): + progress.load_from(save_id) + return _return_to(book_id) + + +def delete_save(request, book_id, save_id): + models.ProgressSave.objects.get(id=save_id).delete() + return redirect(reverse('saves', kwargs={'book_id': book_id})) + + def view_book_map(request, book_id): book = get_object_or_404(models.Book, id=book_id)