From f0424f714ce19e35daa498b9235a58b5183235d0 Mon Sep 17 00:00:00 2001 From: Aleksei Pirogov Date: Fri, 1 May 2020 12:28:43 +0300 Subject: [PATCH] add autologin, book progress tracking, inventory, conditional links --- djaif/book/admin.py | 2 + .../migrations/0007_auto_20200430_1629.py | 33 +++++++ .../migrations/0008_auto_20200430_1703.py | 25 +++++ djaif/book/migrations/0009_bookpage_items.py | 18 ++++ djaif/book/migrations/0010_pagelink_items.py | 18 ++++ djaif/book/models.py | 35 ++++++- djaif/book/templates/page.html | 38 +++++++- djaif/book/urls.py | 11 ++- djaif/book/views.py | 94 +++++++++++++++---- djaif/middleware.py | 15 +++ djaif/settings.py | 1 + djaif/views.py | 0 pyproject.toml | 1 + setup.cfg | 9 +- 14 files changed, 270 insertions(+), 30 deletions(-) create mode 100644 djaif/book/migrations/0007_auto_20200430_1629.py create mode 100644 djaif/book/migrations/0008_auto_20200430_1703.py create mode 100644 djaif/book/migrations/0009_bookpage_items.py create mode 100644 djaif/book/migrations/0010_pagelink_items.py create mode 100644 djaif/middleware.py delete mode 100644 djaif/views.py diff --git a/djaif/book/admin.py b/djaif/book/admin.py index fac91c8..bc7f887 100644 --- a/djaif/book/admin.py +++ b/djaif/book/admin.py @@ -5,3 +5,5 @@ admin.site.register(models.Book) admin.site.register(models.BookPage) admin.site.register(models.PageLink) +admin.site.register(models.BookProgress) +admin.site.register(models.Item) diff --git a/djaif/book/migrations/0007_auto_20200430_1629.py b/djaif/book/migrations/0007_auto_20200430_1629.py new file mode 100644 index 0000000..d7034d9 --- /dev/null +++ b/djaif/book/migrations/0007_auto_20200430_1629.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.5 on 2020-04-30 16:29 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('book', '0006_book_cover_art'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='cover_art', + field=models.ImageField(null=True, upload_to=''), + ), + migrations.CreateModel( + name='BookProgress', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='book.Book')), + ('book_page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='book.BookPage')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'book')}, + }, + ), + ] diff --git a/djaif/book/migrations/0008_auto_20200430_1703.py b/djaif/book/migrations/0008_auto_20200430_1703.py new file mode 100644 index 0000000..329b03a --- /dev/null +++ b/djaif/book/migrations/0008_auto_20200430_1703.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.5 on 2020-04-30 17:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book', '0007_auto_20200430_1629'), + ] + + operations = [ + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField()), + ], + ), + migrations.AddField( + model_name='bookprogress', + name='items', + field=models.ManyToManyField(to='book.Item'), + ), + ] diff --git a/djaif/book/migrations/0009_bookpage_items.py b/djaif/book/migrations/0009_bookpage_items.py new file mode 100644 index 0000000..7c9845b --- /dev/null +++ b/djaif/book/migrations/0009_bookpage_items.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-04-30 17:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book', '0008_auto_20200430_1703'), + ] + + operations = [ + migrations.AddField( + model_name='bookpage', + name='items', + field=models.ManyToManyField(to='book.Item'), + ), + ] diff --git a/djaif/book/migrations/0010_pagelink_items.py b/djaif/book/migrations/0010_pagelink_items.py new file mode 100644 index 0000000..1a00111 --- /dev/null +++ b/djaif/book/migrations/0010_pagelink_items.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-04-30 17:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book', '0009_bookpage_items'), + ] + + operations = [ + migrations.AddField( + model_name='pagelink', + name='items', + field=models.ManyToManyField(to='book.Item'), + ), + ] diff --git a/djaif/book/models.py b/djaif/book/models.py index b1e9ee1..984aa65 100644 --- a/djaif/book/models.py +++ b/djaif/book/models.py @@ -1,3 +1,4 @@ +from django.contrib.auth.models import User from django.db import models @@ -19,6 +20,8 @@ class BookPage(models.Model): title = models.TextField(name='title') body = models.TextField(name='body') + items = models.ManyToManyField('book.Item', blank=True) # noqa: WPS110 + def __str__(self): return '{self.title} ({self.id})'.format(self=self) @@ -30,6 +33,11 @@ class PageLink(models.Model): ) name = models.TextField() + items = models.ManyToManyField('book.Item', blank=True) # noqa: WPS110 + + class Meta: + unique_together = ['from_page', 'to_page'] + def __str__(self): return ( '{self.from_page.title} ➝ {self.to_page.title} ' @@ -38,5 +46,30 @@ def __str__(self): ) ) + def has_all_needed(self, items): + return all(i in items for i in self.items.all()) + + +class BookProgress(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + + book = models.ForeignKey(Book, on_delete=models.CASCADE) + book_page = models.ForeignKey(BookPage, on_delete=models.CASCADE) + + items = models.ManyToManyField('book.Item', blank=True) # noqa: WPS110 + class Meta: - unique_together = ['from_page', 'to_page'] + unique_together = ['user', 'book'] + + @classmethod + def start_reading(cls, user, book): + progress = BookProgress(user=user, book=book, book_page=book.first_page) + progress.save() + return progress + + +class Item(models.Model): + name = models.TextField() + + def __str__(self): + return '{self.name}'.format(self=self) diff --git a/djaif/book/templates/page.html b/djaif/book/templates/page.html index 6d0867f..5560db8 100644 --- a/djaif/book/templates/page.html +++ b/djaif/book/templates/page.html @@ -5,13 +5,45 @@ {{ page.book.title }}: {{ page.title }} +

+ {{ page.title }} +

{{ page.body }} + {% if page_items %} +

Вы видите

+ {% endif %} +

Куда податься?

+ + {% if progress.items.all %} +

Инвентарь

+ + {% endif %} diff --git a/djaif/book/urls.py b/djaif/book/urls.py index a2eac36..9115621 100644 --- a/djaif/book/urls.py +++ b/djaif/book/urls.py @@ -3,7 +3,12 @@ from djaif.book import views urlpatterns = [ - path('', views.index), - path('book/', views.book), - path('book//page/', views.page, name='page'), + path('', views.view_books), + path('book/', views.view_book, name='book'), + path('book//page/', views.view_page, name='page'), + path( + 'book//page//take/', + views.take_item, + name='take', + ), ] diff --git a/djaif/book/views.py b/djaif/book/views.py index d716102..4697727 100644 --- a/djaif/book/views.py +++ b/djaif/book/views.py @@ -1,29 +1,85 @@ -from django.shortcuts import render, redirect, get_object_or_404 +from functools import wraps + +from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from djaif.book import models -def index(request): - return render(request, "book_index.html", context={ - 'books': models.Book.objects.all(), - }) +def on_progress(view): + @wraps(view) # noqa: WPS430 + def inner(request, book_id, **kwargs): + try: + progress = models.BookProgress.objects.get( + book=book_id, user=request.user, + ) + except models.BookProgress.DoesNotExist: + return redirect(reverse('book', kwargs={'book_id': book_id})) + return view( + request=request, progress=progress, book_id=book_id, **kwargs, + ) + + return inner -def book(request, book_id): - b = get_object_or_404(models.Book, id=book_id) - if not b.first_page: - return render(request, "book.html", context={ - 'book': b, - }) - return redirect(reverse('page', kwargs={ - 'book_id': b.id, 'page_id': b.first_page.id - })) +def view_books(request): + return render( + request, + 'book_index.html', + context={'books': models.Book.objects.all()}, + ) -def page(request, book_id, page_id): - return render(request, "page.html", context={ - 'page': get_object_or_404( - models.BookPage, book__id=book_id, id=page_id, +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}) + 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, + ) + 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() + + links = [ + (link, link.has_all_needed(list(progress.items.all()))) + for link in page.pagelink_set.all() + ] + + return render( + request, + 'page.html', + context={ + 'page': page, + 'progress': progress, + 'links': links, + 'page_items': page.items.exclude(id__in=progress.items.only('id')), + }, + ) + + +@on_progress +def take_item(request, progress, book_id, page_id, item_id): + item = get_object_or_404(models.Item, id=item_id) # noqa: WPS110 + + progress.items.add(item) + + return redirect( + reverse('page', kwargs={'book_id': book_id, 'page_id': page_id}), + ) diff --git a/djaif/middleware.py b/djaif/middleware.py new file mode 100644 index 0000000..e48499a --- /dev/null +++ b/djaif/middleware.py @@ -0,0 +1,15 @@ +from django.contrib.auth import authenticate, login + + +def auto_login(get_response): + def middleware(request): # noqa: WPS430 + + if not request.user.is_authenticated: + user = authenticate( # noqa: S106 + username='admin', password='admin', + ) + login(request, user) + + return get_response(request) + + return middleware diff --git a/djaif/settings.py b/djaif/settings.py index 189e988..707fc27 100644 --- a/djaif/settings.py +++ b/djaif/settings.py @@ -48,6 +48,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'djaif.middleware.auto_login', ] ROOT_URLCONF = 'djaif.urls' diff --git a/djaif/views.py b/djaif/views.py deleted file mode 100644 index e69de29..0000000 diff --git a/pyproject.toml b/pyproject.toml index 44a5200..e5a2a31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ black = "^19.10b0" [tool.black] skip-string-normalization=true +line-length=80 [build-system] requires = ["poetry>=0.12"] diff --git a/setup.cfg b/setup.cfg index 9893514..e8c66b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,9 @@ [isort] -include_trailing_comma = true -multi_line_output = 3 +force_grid_wrap = 0 +include_trailing_comma = True line_length = 80 +multi_line_output = 3 +use_parentheses = True [flake8] format = wemake @@ -12,7 +14,6 @@ doctests = True max-complexity = 6 max-line-length = 80 -# Self settings: max-imports = 16 # Excluding some directories: @@ -24,7 +25,7 @@ exclude = *.egg **/migrations/** -ignore = D100, D101, D105, D106, WPS326, WPS306, WPS317 +ignore = D100, D101, D103, D105, D106, WPS326, WPS306, WPS317 per-file-ignores = settings.py: WPS407, E501, C812, WPS221, WPS226