From a312791259156254648d5ca0cddf08905204e9a6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 25 Jan 2020 15:25:19 -0800 Subject: [PATCH] Basic authentication views --- fedireads/migrations/0001_initial.py | 45 +++++++++++++---- fedireads/models.py | 49 ++++++++++++++++--- fedireads/openlibrary.py | 17 ++++++- fedireads/settings.py | 4 +- fedireads/static/format.css | 69 +++++++++++++++++++++++++++ fedireads/static/images/med.jpg | Bin 0 -> 10937 bytes fedireads/static/images/profile.jpg | Bin 0 -> 34916 bytes fedireads/static/images/small.jpg | Bin 0 -> 1122 bytes fedireads/templates/feed.html | 51 ++++++++++++++++++++ fedireads/templates/layout.html | 60 +++++++++++++++++++++++ fedireads/templates/login.html | 8 ++++ fedireads/urls.py | 3 ++ fedireads/views.py | 40 +++++++++++++--- 13 files changed, 322 insertions(+), 24 deletions(-) create mode 100644 fedireads/static/format.css create mode 100644 fedireads/static/images/med.jpg create mode 100644 fedireads/static/images/profile.jpg create mode 100644 fedireads/static/images/small.jpg create mode 100644 fedireads/templates/feed.html create mode 100644 fedireads/templates/layout.html create mode 100644 fedireads/templates/login.html diff --git a/fedireads/migrations/0001_initial.py b/fedireads/migrations/0001_initial.py index f647d8866..fcc00f97b 100644 --- a/fedireads/migrations/0001_initial.py +++ b/fedireads/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.13 on 2020-01-25 21:32 +# Generated by Django 2.0.13 on 2020-01-25 23:55 from django.conf import settings import django.contrib.auth.models @@ -50,25 +50,36 @@ class Migration(migrations.Migration): ('objects', django.contrib.auth.models.UserManager()), ], ), + migrations.CreateModel( + name='Author', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('openlibary_key', models.CharField(max_length=255)), + ('data', django.contrib.postgres.fields.jsonb.JSONField()), + ('added_date', models.DateTimeField(auto_now_add=True)), + ('updated_date', models.DateTimeField(auto_now=True)), + ], + ), migrations.CreateModel( name='Book', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('openlibary_key', models.CharField(max_length=255)), ('data', django.contrib.postgres.fields.jsonb.JSONField()), ('added_date', models.DateTimeField(auto_now_add=True)), ('updated_date', models.DateTimeField(auto_now=True)), ('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('authors', models.ManyToManyField(to='fedireads.Author')), ], ), migrations.CreateModel( name='Review', fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255)), ('content', django.contrib.postgres.fields.jsonb.JSONField(max_length=5000)), ('created_date', models.DateTimeField(auto_now_add=True)), ('updated_date', models.DateTimeField(auto_now=True)), - ('id', models.AutoField(primary_key=True, serialize=False)), ('star_rating', models.IntegerField(default=0)), ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), ('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')), @@ -80,26 +91,44 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Shelf', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), ('editable', models.BooleanField(default=True)), + ('shelf_type', models.CharField(default='custom', max_length=100)), ('created_date', models.DateTimeField(auto_now_add=True)), ('updated_date', models.DateTimeField(auto_now=True)), - ('books', models.ManyToManyField(to='fedireads.Book')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ShelfBook', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('added_date', models.DateTimeField(auto_now_add=True)), + ('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')), + ('shelf', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Shelf')), ], ), migrations.CreateModel( name='Work', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('openlibary_key', models.CharField(max_length=255)), ('data', django.contrib.postgres.fields.jsonb.JSONField()), ('added_date', models.DateTimeField(auto_now_add=True)), ('updated_date', models.DateTimeField(auto_now=True)), - ('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), ], ), + migrations.AddField( + model_name='shelf', + name='books', + field=models.ManyToManyField(through='fedireads.ShelfBook', to='fedireads.Book'), + ), + migrations.AddField( + model_name='shelf', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), migrations.AddField( model_name='book', name='works', diff --git a/fedireads/models.py b/fedireads/models.py index 76d9630e6..b32a0621e 100644 --- a/fedireads/models.py +++ b/fedireads/models.py @@ -1,5 +1,6 @@ ''' database schema for the whole dang thing ''' from django.db import models +from django.dispatch import receiver from django.contrib.auth.models import AbstractUser from django.contrib.postgres.fields import JSONField from Crypto.PublicKey import RSA @@ -28,10 +29,27 @@ class User(AbstractUser): super().save(*args, **kwargs) +@receiver(models.signals.post_save, sender=User) +def execute_after_save(sender, instance, created, *args, **kwargs): + if not created: + return + shelves = [{ + 'name': 'To Read', + 'type': 'to-read', + }, { + 'name': 'Currently Reading', + 'type': 'reading', + }, { + 'name': 'Read', + 'type': 'read', + }] + + for shelf in shelves: + Shelf(name=shelf['name'], shelf_type=shelf['type'], user=instance, editable=False).save() + class Message(models.Model): ''' any kind of user post, incl. reviews, replies, and status updates ''' - id = models.AutoField(primary_key=True) author = models.ForeignKey('User', on_delete=models.PROTECT) name = models.CharField(max_length=255) content = JSONField(max_length=5000) @@ -43,35 +61,52 @@ class Message(models.Model): class Review(Message): - id = models.AutoField(primary_key=True) book = models.ForeignKey('Book', on_delete=models.PROTECT) star_rating = models.IntegerField(default=0) class Shelf(models.Model): - id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) user = models.ForeignKey('User', on_delete=models.PROTECT) editable = models.BooleanField(default=True) - books = models.ManyToManyField('Book', symmetrical=False) + shelf_type = models.CharField(default='custom', max_length=100) + books = models.ManyToManyField( + 'Book', + symmetrical=False, + through='ShelfBook', + through_fields=('shelf', 'book') + ) created_date = models.DateTimeField(auto_now_add=True) updated_date = models.DateTimeField(auto_now=True) +class ShelfBook(models.Model): + # many to many join table for books and shelves + book = models.ForeignKey('Book', on_delete=models.PROTECT) + shelf = models.ForeignKey('Shelf', on_delete=models.PROTECT) + added_by = models.ForeignKey('User', blank=True, null=True, on_delete=models.PROTECT) + added_date = models.DateTimeField(auto_now_add=True) + + class Book(models.Model): ''' a non-canonical copy from open library ''' - id = models.AutoField(primary_key=True) openlibary_key = models.CharField(max_length=255) data = JSONField() works = models.ManyToManyField('Work') + authors = models.ManyToManyField('Author') added_by = models.ForeignKey('User', on_delete=models.PROTECT, blank=True, null=True) added_date = models.DateTimeField(auto_now_add=True) updated_date = models.DateTimeField(auto_now=True) class Work(models.Model): - id = models.AutoField(primary_key=True) openlibary_key = models.CharField(max_length=255) data = JSONField() - added_by = models.ForeignKey('User', on_delete=models.PROTECT, blank=True, null=True) added_date = models.DateTimeField(auto_now_add=True) updated_date = models.DateTimeField(auto_now=True) + +class Author(models.Model): + openlibary_key = models.CharField(max_length=255) + data = JSONField() + added_date = models.DateTimeField(auto_now_add=True) + updated_date = models.DateTimeField(auto_now=True) + diff --git a/fedireads/openlibrary.py b/fedireads/openlibrary.py index 3953da2d4..66fc20bbf 100644 --- a/fedireads/openlibrary.py +++ b/fedireads/openlibrary.py @@ -2,7 +2,7 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest from django.core.exceptions import ObjectDoesNotExist from django.core import serializers -from fedireads.models import Book, Work +from fedireads.models import Author, Book, Work import requests openlibrary_url = 'https://openlibrary.org' @@ -22,15 +22,28 @@ def get_book(request, olkey): for work_id in data['works']: work_id = work_id['key'] book.works.add(get_or_create_work(work_id)) + for author_id in data['authors']: + author_id = author_id['key'] + book.authors.add(get_or_create_author(author_id)) return HttpResponse(serializers.serialize('json', [book])) def get_or_create_work(olkey): try: work = Work.objects.get(openlibary_key=olkey) except ObjectDoesNotExist: - response = requests.get(openlibrary_url + '/work/' + olkey +'.json') + response = requests.get(openlibrary_url + olkey + '.json') data = response.json() work = Work(openlibary_key=olkey, data=data) work.save() return work +def get_or_create_author(olkey): + try: + author = Author.objects.get(openlibary_key=olkey) + except ObjectDoesNotExist: + response = requests.get(openlibrary_url + olkey + '.json') + data = response.json() + author = Author(openlibary_key=olkey, data=data) + author.save() + return author + diff --git a/fedireads/settings.py b/fedireads/settings.py index 4ba4a2ba1..c08cb7c2d 100644 --- a/fedireads/settings.py +++ b/fedireads/settings.py @@ -55,7 +55,7 @@ ROOT_URLCONF = 'fedireads.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': ['templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -68,6 +68,7 @@ TEMPLATES = [ }, ] + WSGI_APPLICATION = 'fedireads.wsgi.application' @@ -85,6 +86,7 @@ DATABASES = { } } +LOGIN_URL = 'login/' AUTH_USER_MODEL = 'fedireads.User' # Password validation diff --git a/fedireads/static/format.css b/fedireads/static/format.css new file mode 100644 index 000000000..7e0ea6c51 --- /dev/null +++ b/fedireads/static/format.css @@ -0,0 +1,69 @@ +* { + margin: 0; + padding: 0; + line-height: 1.3em; + overflow: auto; +} + +body > * > * { + margin: 0 auto; + padding: 1rem; + max-width: 75rem; + min-width: 30rem; +} + +#top-bar { + height: 4rem; + border-bottom: 1px solid #aaa; + box-shadow: 0 0.5em 0.5em -0.6em #666; + margin-bottom: 1em; + overflow: auto; +} + +#branding { + font-size: 2em; +} + +header > div:first-child { + float: left; +} + +header > div:last-child { + float: right; +} + +#sidebar { + width: 30%; + float: left; +} + +.user-pic { + width: 2em; + height: auto; + border-radius: 50%; + vertical-align: middle; +} + +.book-preview { + overflow: auto; + margin-bottom: 1em; +} + +.book-preview img { + float: left; + margin-right: 0.5em; +} + +.update { + border: 1px solid #333; + border-radius: 0.2rem; + margin-bottom: 1em; +} + +.update > * { + padding: 1em; +} + +.interact { + background-color: #eee; +} diff --git a/fedireads/static/images/med.jpg b/fedireads/static/images/med.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c275cd1c85dce6988f39a34f6fb2ad1a4aba7662 GIT binary patch literal 10937 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 XS;q9@zlzB({!JnIp8_-Ra_+wYDD(on literal 0 HcmV?d00001 diff --git a/fedireads/static/images/profile.jpg b/fedireads/static/images/profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f150ceabebf5e183818405f854f6de336d063068 GIT binary patch literal 34916 zcmbrlWpEua(=K?-%*@Qp%*@Qp_Az5(j+vR6nHge;nPbKr$IR^5j@j$H@9zDow(kA0 zwLNvDr_|~msi&q#YW4Y8``8Ac%1g^h1Hixl0I<&s@UaVCC?_FdqN=VUEvG2;9|m}$ zgN36zBr5>m=;Yz1E+bB=tEW#2vj;!`zyQDj_yArr3wIYWRaHg6|B)V6{%HpQ7McF( z`agF2-!l*_t=ug>2MGJg=CyEf^Z3M~pIG^ehs!^l_=zzsY|Sh`ap@;!bo(6OC!YJ) zZvJ1q{}0>z7eD^PZkp;60021RCnmM|FJ}CQ&Hs!4&4tv$*3I#=kHaUXbae3i9K(O; z-%Jp#oU}APYr_Bh+yUwU8Gtx|6krbU1lR!_03HCQ&)VtJX8(IR_W#H!1Dro)EIvyY zfH%PHQ^E#d`zg!#+3E>!2Uva9)}QTGpO?dDk5Bq%`+qe6@IN+nx90e#?{mtC@&EwT z-p9uudH?_>2LO1B`1p7!{`hz+0RSM@0DvE<|0D05`f1L~&-TRsX`{>o08l~zfcC!s zX)`Ya06IQxjQiWg%+2gS_ks8+$D9!)?S=j&p_2>Oi3_pGWBmhtlkdTlNP@e@96cjWJA}q`&p&%f@A)=w6qobjq zp5wt`vyFazw*)yz=W_;Fs zaFFmNyuEydr5o3?mM>|tWO7-%Fud~{!`JzrV{zlZ2I&TR`DQT7RnrQ;7iKGXYvi)b z>Pq@+TiVa$7)D==F+1m!<;jRs#`)JGm1}Ir6jTmS`wQo-3+dn#H=+#~M3mm02ioPy zVV2xUVBg^f$B4nsuIAB~#puGOE!syMR(zpW4bAkCJHFi3_&p~2Q>Iz2NjV)FU!i{C zN5jHoCrumWrDFIE^)D%CXWMnX!wZm_XQA5F&2!IL?C|9(VgZ}i5ZT5zUjSVP2^doK zHY_!Gv-8#KJv-U)6KDycKzGdp-sKMHzlT$^<`8PkR63^~TV!dRVE0kE8fP~@NmGtk3N z6v&Nwh@rEpU$`OTG*DTCa7%cVD2WH|h9w z_qLUKg6oD^8@J)zH6g$@Z7U{O*|yxRzXcI#-u#tpCXXLgZ0b$64P}Owy^)poF72=1 zNWPOAxt(*6h;1^puc2I$(2a7W?v=6TW=-223ZZA|!j73sc2i!8{k?@ch}`V*@&3bcD5AQd xZ=@u}2V$>Ihm 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 literal 0 HcmV?d00001 diff --git a/fedireads/templates/feed.html b/fedireads/templates/feed.html new file mode 100644 index 000000000..1d0c7f458 --- /dev/null +++ b/fedireads/templates/feed.html @@ -0,0 +1,51 @@ +{% extends 'layout.html' %} +{% block content %} +

+ +
+
+
+ + Mouse is currently reading +
+
+ +

Moby Dick

+

by Herman Melville

+

"Command the murderous chalices! Drink ye harpooners! Drink and swear, ye men that man the deathful whaleboat's bow -- Death to Moby Dick!" So Captain Ahab binds his crew to fulfil his obsession -- the destruction of the great white whale. Under his lordly but maniacal command the Pequod's commercial mission is perverted to one of vengeance...

+
+
+ ⭐️ Like + 💬 +
+
+
+ + Mouse is currently reading +
+ +

Moby Dick

+

by Herman Melville

+

"Command the murderous chalices! Drink ye harpooners! Drink and swear, ye men that man the deathful whaleboat's bow -- Death to Moby Dick!" So Captain Ahab binds his crew to fulfil his obsession -- the destruction of the great white whale. Under his lordly but maniacal command the Pequod's commercial mission is perverted to one of vengeance...

+
+
+ ⭐️ Like + 💬 +
+
+
+{% endblock %} diff --git a/fedireads/templates/layout.html b/fedireads/templates/layout.html new file mode 100644 index 000000000..c1329102a --- /dev/null +++ b/fedireads/templates/layout.html @@ -0,0 +1,60 @@ + + + + FediReads + + + + + + + + + + + + + + + + + +
+
+
📚FediReads
+
+
+ {% if user.is_authenticated %} +
+ Welcome, {{ user.username }} + +
+ {% else %} +
+ + + +
+ {% endif %} +
+ +
+
+
+ +
+
+ {% block content %} + {% endblock %} +
+ +
+ + + + diff --git a/fedireads/templates/login.html b/fedireads/templates/login.html new file mode 100644 index 000000000..fe6a319d9 --- /dev/null +++ b/fedireads/templates/login.html @@ -0,0 +1,8 @@ +{% extends 'layout.html' %} +{% block content %} +
+ + + +
+{% endblock %} diff --git a/fedireads/urls.py b/fedireads/urls.py index e4a94ebda..9c9644823 100644 --- a/fedireads/urls.py +++ b/fedireads/urls.py @@ -19,6 +19,9 @@ from fedireads import activitystream, openlibrary, views urlpatterns = [ path('admin/', admin.site.urls), + path('', views.home), + path('login/', views.user_login), + path('logout/', views.user_logout), path('api/book/', openlibrary.get_book), path('webfinger/', activitystream.webfinger), ] diff --git a/fedireads/views.py b/fedireads/views.py index 1fbaa8073..7b2e360c2 100644 --- a/fedireads/views.py +++ b/fedireads/views.py @@ -1,11 +1,39 @@ +''' application views/pages ''' from django.contrib.auth.decorators import login_required +from django.contrib.auth import authenticate, login, logout +from django.shortcuts import redirect +from django.template.response import TemplateResponse +from django.views.decorators.csrf import csrf_exempt +from fedireads.models import Shelf @login_required -def account_page(request): - return 'hi' +def home(request): + ''' user feed ''' + shelves = Shelf.objects.filter(user=request.user.id) + data = { + 'user': request.user, + 'shelves': shelves, + } + return TemplateResponse(request, 'feed.html', data) -def webfinger(request): - return 'hello' +@csrf_exempt +def user_login(request): + ''' authentication ''' + # send user to the login page + if request.method == 'GET': + return TemplateResponse(request, 'login.html') -def api(request): - return 'hey' + # authenticate user + username = request.POST['username'] + password = request.POST['password'] + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + return redirect(request.GET.get('next', '/')) + return TemplateResponse(request, 'login.html') + +@csrf_exempt +@login_required +def user_logout(request): + logout(request) + return redirect('/')