openlibrary search

This commit is contained in:
Mouse Reeve 2020-01-29 01:05:27 -08:00
parent dd478a3587
commit c5d0e02166
14 changed files with 91 additions and 63 deletions

43
fedireads/forms.py Normal file
View file

@ -0,0 +1,43 @@
''' usin django model forms '''
from django.forms import ModelForm, PasswordInput
from fedireads import models
class LoginForm(ModelForm):
class Meta:
model = models.User
fields = ['username', 'password']
help_texts = {f: None for f in fields}
widgets = {
'password': PasswordInput(),
}
class RegisterForm(ModelForm):
class Meta:
model = models.User
fields = ['username', 'email', 'password']
help_texts = {f: None for f in fields}
widgets = {
'password': PasswordInput(),
}
class ReviewForm(ModelForm):
class Meta:
model = models.Review
fields = ['name', 'review_content', 'rating']
help_texts = {f: None for f in fields}
labels = {
'name': 'Title',
'review_content': 'Review',
'rating': 'Rating (out of 5)',
}
class EditUserForm(ModelForm):
class Meta:
model = models.User
fields = ['avatar', 'name', 'summary']
help_texts = {f: None for f in fields}

View file

@ -193,7 +193,6 @@ def handle_incoming_create(activity):
content=activity, content=activity,
activity_type='Article', activity_type='Article',
book=book, book=book,
work=book.works.first(),
name=activity['object']['name'], name=activity['object']['name'],
rating=activity['object']['rating'], rating=activity['object']['rating'],
review_content=activity['objet']['content'], review_content=activity['objet']['content'],

View file

@ -1,4 +1,4 @@
# Generated by Django 3.0.2 on 2020-01-29 07:36 # Generated by Django 3.0.2 on 2020-01-29 09:04
from django.conf import settings from django.conf import settings
import django.contrib.auth.models import django.contrib.auth.models
@ -109,16 +109,6 @@ class Migration(migrations.Migration):
('updated_date', models.DateTimeField(auto_now=True)), ('updated_date', models.DateTimeField(auto_now=True)),
], ],
), ),
migrations.CreateModel(
name='Work',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('openlibrary_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( migrations.CreateModel(
name='Note', name='Note',
fields=[ fields=[
@ -154,11 +144,6 @@ class Migration(migrations.Migration):
name='shelves', name='shelves',
field=models.ManyToManyField(through='fedireads.ShelfBook', to='fedireads.Shelf'), field=models.ManyToManyField(through='fedireads.ShelfBook', to='fedireads.Shelf'),
), ),
migrations.AddField(
model_name='book',
name='works',
field=models.ManyToManyField(to='fedireads.Work'),
),
migrations.CreateModel( migrations.CreateModel(
name='ShelveActivity', name='ShelveActivity',
fields=[ fields=[
@ -180,7 +165,6 @@ class Migration(migrations.Migration):
('rating', models.IntegerField(default=0)), ('rating', models.IntegerField(default=0)),
('review_content', models.TextField()), ('review_content', models.TextField()),
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')), ('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')),
('work', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Work')),
], ],
bases=('fedireads.activity',), bases=('fedireads.activity',),
), ),

View file

@ -133,7 +133,6 @@ class FollowActivity(Activity):
class Review(Activity): class Review(Activity):
''' a book review ''' ''' a book review '''
book = models.ForeignKey('Book', on_delete=models.PROTECT) book = models.ForeignKey('Book', on_delete=models.PROTECT)
work = models.ForeignKey('Work', on_delete=models.PROTECT)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
# TODO: validation # TODO: validation
rating = models.IntegerField(default=0) rating = models.IntegerField(default=0)
@ -200,11 +199,10 @@ class ShelfBook(models.Model):
class Book(models.Model): class Book(models.Model):
''' a non-canonical copy from open library ''' ''' a non-canonical copy of a work (not book) from open library '''
activitypub_id = models.CharField(max_length=255) activitypub_id = models.CharField(max_length=255)
openlibrary_key = models.CharField(max_length=255, unique=True) openlibrary_key = models.CharField(max_length=255, unique=True)
data = JSONField() data = JSONField()
works = models.ManyToManyField('Work')
authors = models.ManyToManyField('Author') authors = models.ManyToManyField('Author')
cover = models.ImageField(upload_to='covers/', blank=True, null=True) cover = models.ImageField(upload_to='covers/', blank=True, null=True)
shelves = models.ManyToManyField( shelves = models.ManyToManyField(
@ -227,14 +225,6 @@ class Book(models.Model):
super().save(*args, **kwargs) super().save(*args, **kwargs)
class Work(models.Model):
''' encompassses all editions of a book '''
openlibrary_key = models.CharField(max_length=255)
data = JSONField()
added_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
class Author(models.Model): class Author(models.Model):
openlibrary_key = models.CharField(max_length=255) openlibrary_key = models.CharField(max_length=255)
data = JSONField() data = JSONField()

View file

@ -3,10 +3,27 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
import requests import requests
from fedireads.models import Author, Book, Work from fedireads.models import Author, Book
from fedireads.settings import OL_URL from fedireads.settings import OL_URL
def book_search(query):
''' look up a book '''
response = requests.get('%s/search.json' % OL_URL, params={'q': query})
if not response.ok:
response.raise_for_status()
data = response.json()
results = []
for doc in data['docs'][:5]:
key = doc['key'].split('/')[-1]
results.append({
'title': doc['title'],
'olkey': key,
'year': doc['first_publish_year'],
'author': doc['author_name'][0],
})
return results
def get_or_create_book(olkey, user=None, update=False): def get_or_create_book(olkey, user=None, update=False):
''' add a book ''' ''' add a book '''
# TODO: check if this is a valid open library key, and a book # TODO: check if this is a valid open library key, and a book
@ -36,13 +53,9 @@ def get_or_create_book(olkey, user=None, update=False):
# great, we can update our book. # great, we can update our book.
book.save() book.save()
# we also need to know the author and works related to this book. # we also need to know the author get the cover
for work_id in data['works']: for author_blob in data['authors']:
work_id = work_id['key'] author_id = author_blob['author']['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)) book.authors.add(get_or_create_author(author_id))
if len(data['covers']): if len(data['covers']):
@ -62,20 +75,6 @@ def get_cover(cover_id):
return [image_name, image_content] return [image_name, image_content]
def get_or_create_work(olkey):
''' load em up '''
# TODO: validate that this is a work key
# TODO: error handling
try:
work = Work.objects.get(openlibrary_key=olkey)
except ObjectDoesNotExist:
response = requests.get(OL_URL + olkey + '.json')
data = response.json()
work = Work(openlibrary_key=olkey, data=data)
work.save()
return work
def get_or_create_author(olkey): def get_or_create_author(olkey):
''' load that author ''' ''' load that author '''
# TODO: validate that this is an author key # TODO: validate that this is an author key

View file

@ -188,7 +188,6 @@ def handle_review(user, book, name, content, rating):
content=activity, content=activity,
activity_type='Article', activity_type='Article',
book=book, book=book,
work=book.works.first(),
name=name, name=name,
rating=rating, rating=rating,
review_content=content, review_content=content,

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -0,0 +1,13 @@
{% extends 'layout.html' %}
{% block content %}
<div id="content">
<div>
<h1>Search results</h1>
{% for result in results %}
<div>
<a href="/work/{{ result.olkey }}">{{ result.title }}</a> by {{ result.author }} ({{ result.year }})
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View file

@ -82,6 +82,7 @@
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a> <a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
by by
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a> <a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
<blockquote>{{ book.data.description }}</blockquote>
</p> </p>
</div> </div>
{% elif activity.fedireads_type == 'Review' %} {% elif activity.fedireads_type == 'Review' %}
@ -94,6 +95,7 @@
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a> <a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
by by
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a> <a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
<blockquote>{{ book.data.description }}</blockquote>
</p> </p>
<h3>{{ activity.name }}</h3> <h3>{{ activity.name }}</h3>

View file

@ -23,7 +23,7 @@ urlpatterns = [
path('logout/', views.user_logout), path('logout/', views.user_logout),
path('user/<str:username>', views.user_profile), path('user/<str:username>', views.user_profile),
path('user/<str:username>/edit/', views.user_profile_edit), path('user/<str:username>/edit/', views.user_profile_edit),
path('book/<str:book_identifier>', views.book_page), path('work/<str:book_identifier>', views.book_page),
# internal action endpoints # internal action endpoints
path('review/', views.review), path('review/', views.review),

View file

@ -26,7 +26,7 @@ def home(request):
user_books = models.Book.objects.filter(shelves__user=request.user).all() user_books = models.Book.objects.filter(shelves__user=request.user).all()
recent_books = models.Book.objects.order_by( recent_books = models.Book.objects.order_by(
'added_date' 'added_date'
)[:10] )[:5]
following = models.User.objects.filter( following = models.User.objects.filter(
Q(followers=request.user) | Q(id=request.user.id) Q(followers=request.user) | Q(id=request.user.id)
@ -163,10 +163,8 @@ def edit_profile(request):
@login_required @login_required
def book_page(request, book_identifier): def book_page(request, book_identifier):
''' info about a book ''' ''' info about a book '''
book = openlibrary.get_or_create_book('/book/' + book_identifier) book = openlibrary.get_or_create_book('/work/' + book_identifier)
reviews = models.Review.objects.filter( reviews = models.Review.objects.filter(book=book)
Q(work=book.works.first()) | Q(book=book)
)
rating = reviews.aggregate(Avg('rating')) rating = reviews.aggregate(Avg('rating'))
review_form = forms.ReviewForm() review_form = forms.ReviewForm()
data = { data = {
@ -244,9 +242,10 @@ def search(request):
query = request.GET.get('q') query = request.GET.get('q')
if re.match(r'\w+@\w+.\w+', query): if re.match(r'\w+@\w+.\w+', query):
results = [api.handle_account_search(query)] results = [api.handle_account_search(query)]
template = 'user_results.html'
else: else:
# TODO: book search results = openlibrary.book_search(query)
results = [] template = 'book_results.html'
return TemplateResponse(request, 'results.html', {'results': results}) return TemplateResponse(request, template, {'results': results})

View file

@ -12,6 +12,6 @@ echo "from fedireads.models import User
User.objects.create_user('rat', 'rat@rat.com', 'ratword') User.objects.create_user('rat', 'rat@rat.com', 'ratword')
User.objects.get(id=1).followers.add(User.objects.get(id=2))" | python manage.py shell User.objects.get(id=1).followers.add(User.objects.get(id=2))" | python manage.py shell
echo "from fedireads.openlibrary import get_or_create_book echo "from fedireads.openlibrary import get_or_create_book
get_or_create_book('/book/OL13549170M') get_or_create_book('/work/OL1715344W')
get_or_create_book('/book/OL24738110M')" | python manage.py shell get_or_create_book('/work/OL102749W')" | python manage.py shell
python manage.py runserver python manage.py runserver