forked from mirrors/bookwyrm
Allow users to set privacy on imported reviews
or not import them at all. Fixes #252
This commit is contained in:
parent
45f39fab48
commit
0b0de12968
8 changed files with 77 additions and 18 deletions
|
@ -11,9 +11,13 @@ from bookwyrm.status import create_notification
|
||||||
MAX_ENTRIES = 500
|
MAX_ENTRIES = 500
|
||||||
|
|
||||||
|
|
||||||
def create_job(user, csv_file):
|
def create_job(user, csv_file, include_reviews, privacy):
|
||||||
''' check over a csv and creates a database entry for the job'''
|
''' check over a csv and creates a database entry for the job'''
|
||||||
job = ImportJob.objects.create(user=user)
|
job = ImportJob.objects.create(
|
||||||
|
user=user,
|
||||||
|
include_reviews=include_reviews,
|
||||||
|
privacy=privacy
|
||||||
|
)
|
||||||
for index, entry in enumerate(list(csv.DictReader(csv_file))[:MAX_ENTRIES]):
|
for index, entry in enumerate(list(csv.DictReader(csv_file))[:MAX_ENTRIES]):
|
||||||
if not all(x in entry for x in ('ISBN13', 'Title', 'Author')):
|
if not all(x in entry for x in ('ISBN13', 'Title', 'Author')):
|
||||||
raise ValueError("Author, title, and isbn must be in data.")
|
raise ValueError("Author, title, and isbn must be in data.")
|
||||||
|
@ -42,8 +46,10 @@ def import_data(job_id):
|
||||||
if item.book:
|
if item.book:
|
||||||
item.save()
|
item.save()
|
||||||
results.append(item)
|
results.append(item)
|
||||||
|
|
||||||
|
if job.include_reviews:
|
||||||
# shelves book and handles reviews
|
# shelves book and handles reviews
|
||||||
outgoing.handle_imported_book(job.user, item)
|
outgoing.handle_imported_book(job.user, item, job.privacy)
|
||||||
else:
|
else:
|
||||||
item.fail_reason = "Could not find a match for book"
|
item.fail_reason = "Could not find a match for book"
|
||||||
item.save()
|
item.save()
|
||||||
|
|
23
bookwyrm/migrations/0059_auto_20201030_1755.py
Normal file
23
bookwyrm/migrations/0059_auto_20201030_1755.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 3.0.7 on 2020-10-30 17:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bookwyrm', '0058_remove_importjob_import_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='importjob',
|
||||||
|
name='include_reviews',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='importjob',
|
||||||
|
name='privacy',
|
||||||
|
field=models.CharField(choices=[('public', 'Public'), ('unlisted', 'Unlisted'), ('followers', 'Followers'), ('direct', 'Direct')], default='public', max_length=255),
|
||||||
|
),
|
||||||
|
]
|
|
@ -14,6 +14,14 @@ from django.dispatch import receiver
|
||||||
from bookwyrm import activitypub
|
from bookwyrm import activitypub
|
||||||
from bookwyrm.settings import DOMAIN
|
from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
PrivacyLevels = models.TextChoices('Privacy', [
|
||||||
|
'public',
|
||||||
|
'unlisted',
|
||||||
|
'followers',
|
||||||
|
'direct'
|
||||||
|
])
|
||||||
|
|
||||||
class BookWyrmModel(models.Model):
|
class BookWyrmModel(models.Model):
|
||||||
''' shared fields '''
|
''' shared fields '''
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
@ -9,6 +9,8 @@ from bookwyrm import books_manager
|
||||||
from bookwyrm.connectors import ConnectorException
|
from bookwyrm.connectors import ConnectorException
|
||||||
from bookwyrm.models import ReadThrough, User, Book
|
from bookwyrm.models import ReadThrough, User, Book
|
||||||
from bookwyrm.utils.fields import JSONField
|
from bookwyrm.utils.fields import JSONField
|
||||||
|
from .base_model import PrivacyLevels
|
||||||
|
|
||||||
|
|
||||||
# Mapping goodreads -> bookwyrm shelf titles.
|
# Mapping goodreads -> bookwyrm shelf titles.
|
||||||
GOODREADS_SHELVES = {
|
GOODREADS_SHELVES = {
|
||||||
|
@ -40,6 +42,12 @@ class ImportJob(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
created_date = models.DateTimeField(default=timezone.now)
|
created_date = models.DateTimeField(default=timezone.now)
|
||||||
task_id = models.CharField(max_length=100, null=True)
|
task_id = models.CharField(max_length=100, null=True)
|
||||||
|
include_reviews = models.BooleanField(default=True)
|
||||||
|
privacy = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
default='public',
|
||||||
|
choices=PrivacyLevels.choices
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ImportItem(models.Model):
|
class ImportItem(models.Model):
|
||||||
|
|
|
@ -7,16 +7,9 @@ from model_utils.managers import InheritanceManager
|
||||||
|
|
||||||
from bookwyrm import activitypub
|
from bookwyrm import activitypub
|
||||||
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
|
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
|
||||||
from .base_model import ActivityMapping, BookWyrmModel
|
from .base_model import ActivityMapping, BookWyrmModel, PrivacyLevels
|
||||||
|
|
||||||
|
|
||||||
PrivacyLevels = models.TextChoices('Privacy', [
|
|
||||||
'public',
|
|
||||||
'unlisted',
|
|
||||||
'followers',
|
|
||||||
'direct'
|
|
||||||
])
|
|
||||||
|
|
||||||
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
''' any post, like a reply to a review, etc '''
|
''' any post, like a reply to a review, etc '''
|
||||||
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||||
|
|
|
@ -155,7 +155,7 @@ def handle_unshelve(user, book, shelf):
|
||||||
broadcast(user, activity)
|
broadcast(user, activity)
|
||||||
|
|
||||||
|
|
||||||
def handle_imported_book(user, item):
|
def handle_imported_book(user, item, privacy):
|
||||||
''' process a goodreads csv and then post about it '''
|
''' process a goodreads csv and then post about it '''
|
||||||
if isinstance(item.book, models.Work):
|
if isinstance(item.book, models.Work):
|
||||||
item.book = item.book.default_edition
|
item.book = item.book.default_edition
|
||||||
|
@ -171,7 +171,7 @@ def handle_imported_book(user, item):
|
||||||
shelf_book, created = models.ShelfBook.objects.get_or_create(
|
shelf_book, created = models.ShelfBook.objects.get_or_create(
|
||||||
book=item.book, shelf=desired_shelf, added_by=user)
|
book=item.book, shelf=desired_shelf, added_by=user)
|
||||||
if created:
|
if created:
|
||||||
broadcast(user, shelf_book.to_add_activity(user))
|
broadcast(user, shelf_book.to_add_activity(user), privacy=privacy)
|
||||||
|
|
||||||
# only add new read-throughs if the item isn't already shelved
|
# only add new read-throughs if the item isn't already shelved
|
||||||
for read in item.reads:
|
for read in item.reads:
|
||||||
|
@ -194,10 +194,11 @@ def handle_imported_book(user, item):
|
||||||
content=item.review,
|
content=item.review,
|
||||||
rating=item.rating,
|
rating=item.rating,
|
||||||
published_date=published_date_guess,
|
published_date=published_date_guess,
|
||||||
|
privacy=privacy,
|
||||||
)
|
)
|
||||||
# we don't need to send out pure activities because non-bookwyrm
|
# we don't need to send out pure activities because non-bookwyrm
|
||||||
# instances don't need this data
|
# instances don't need this data
|
||||||
broadcast(user, review.to_create_activity(user))
|
broadcast(user, review.to_create_activity(user), privacy=privacy)
|
||||||
|
|
||||||
|
|
||||||
def handle_delete_status(user, status):
|
def handle_delete_status(user, status):
|
||||||
|
|
|
@ -5,8 +5,24 @@
|
||||||
<h2 class="title">Import Books from GoodReads</h2>
|
<h2 class="title">Import Books from GoodReads</h2>
|
||||||
<form name="import" action="/import_data/" method="post" enctype="multipart/form-data">
|
<form name="import" action="/import_data/" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<div class="field">
|
||||||
{{ import_form.as_p }}
|
{{ import_form.as_p }}
|
||||||
<button class="button" type="submit">Import</button>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="include_reviews"><input type="checkbox" name="include_reviews" checked> Include reviews</label>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="privacy">Privacy setting for imported reviews</label>
|
||||||
|
<div class="select">
|
||||||
|
<select name="privacy">
|
||||||
|
<option value="public" selected>Public</option>
|
||||||
|
<option value="unlisted">Unlisted</option>
|
||||||
|
<option value="followers">Followers only</option>
|
||||||
|
<option value="direct">Private</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="button is-primary" type="submit">Import</button>
|
||||||
</form>
|
</form>
|
||||||
<p>
|
<p>
|
||||||
Imports are limited in size, and only the first {{ limit }} items will be imported.
|
Imports are limited in size, and only the first {{ limit }} items will be imported.
|
||||||
|
|
|
@ -491,12 +491,16 @@ def import_data(request):
|
||||||
''' ingest a goodreads csv '''
|
''' ingest a goodreads csv '''
|
||||||
form = forms.ImportForm(request.POST, request.FILES)
|
form = forms.ImportForm(request.POST, request.FILES)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
include_reviews = request.POST.get('include_reviews') == 'on'
|
||||||
|
privacy = request.POST.get('privacy')
|
||||||
try:
|
try:
|
||||||
job = goodreads_import.create_job(
|
job = goodreads_import.create_job(
|
||||||
request.user,
|
request.user,
|
||||||
TextIOWrapper(
|
TextIOWrapper(
|
||||||
request.FILES['csv_file'],
|
request.FILES['csv_file'],
|
||||||
encoding=request.encoding)
|
encoding=request.encoding),
|
||||||
|
include_reviews,
|
||||||
|
privacy,
|
||||||
)
|
)
|
||||||
except (UnicodeDecodeError, ValueError):
|
except (UnicodeDecodeError, ValueError):
|
||||||
return HttpResponseBadRequest('Not a valid csv file')
|
return HttpResponseBadRequest('Not a valid csv file')
|
||||||
|
|
Loading…
Reference in a new issue