Merge pull request #3055 from hughrun/user-migrate

formatting and linting fixes
This commit is contained in:
Hugh Rundle 2023-10-22 16:56:20 +11:00 committed by GitHub
commit 0c846ca31f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 91 additions and 63 deletions

View file

@ -1,14 +1,15 @@
"""Import data from Bookwyrm export files"""
from bookwyrm import settings
from bookwyrm.models.bookwyrm_import_job import BookwyrmImportJob
class BookwyrmImporter:
"""Import a Bookwyrm User export JSON file.
"""Import a Bookwyrm User export file.
This is kind of a combination of an importer and a connector.
"""
def process_import(self, user, archive_file, settings):
def process_import(
self, user, archive_file, settings
): # pylint: disable=no-self-use
"""import user data from a Bookwyrm export file"""
required = [k for k in settings if settings.get(k) == "on"]

View file

@ -1,4 +1,7 @@
"""Export user account to tar.gz file for import into another Bookwyrm instance"""
import logging
from uuid import uuid4
from django.db.models import FileField
from django.db.models import Q
@ -6,10 +9,9 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.core.files.base import ContentFile
from bookwyrm import models
from bookwyrm.models.job import ParentJob, ParentTask
from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app, IMPORTS
from bookwyrm.models.job import ParentJob, ParentTask, SubTask, create_child_job
from uuid import uuid4
from bookwyrm.utils.tar import BookwyrmTarFile
logger = logging.getLogger(__name__)
@ -49,21 +51,22 @@ def start_export_task(**kwargs):
job.save(update_fields=["export_data"])
def tar_export(json_data: str, user, f):
f.open("wb")
with BookwyrmTarFile.open(mode="w:gz", fileobj=f) as tar:
def tar_export(json_data: str, user, file):
"""wrap the export information in a tar file"""
file.open("wb")
with BookwyrmTarFile.open(mode="w:gz", fileobj=file) as tar:
tar.write_bytes(json_data.encode("utf-8"))
# Add avatar image if present
if getattr(user, "avatar", False):
tar.add_image(user.avatar, filename="avatar")
editions, books = get_books_for_user(user)
editions = get_books_for_user(user)
for book in editions:
if getattr(book, "cover", False):
tar.add_image(book.cover)
f.close()
file.close()
def json_export(user):
@ -91,18 +94,19 @@ def json_export(user):
# reading goals
reading_goals = models.AnnualGoal.objects.filter(user=user).distinct()
goals_list = []
# TODO: either error checking should be more sophisticated or maybe we don't need this try/except
try:
for goal in reading_goals:
goals_list.append(
{"goal": goal.goal, "year": goal.year, "privacy": goal.privacy}
)
except Exception:
except Exception: # pylint: disable=broad-except
pass
try:
readthroughs = models.ReadThrough.objects.filter(user=user).distinct().values()
readthroughs = list(readthroughs)
except Exception as e:
except Exception: # pylint: disable=broad-except
readthroughs = []
# books

View file

@ -1,3 +1,5 @@
"""Import a user from another Bookwyrm instance"""
from functools import reduce
import json
import logging
@ -11,13 +13,7 @@ from django.contrib.postgres.fields import ArrayField as DjangoArrayField
from bookwyrm import activitypub
from bookwyrm import models
from bookwyrm.tasks import app, IMPORTS
from bookwyrm.models.job import (
ParentJob,
ParentTask,
ChildJob,
SubTask,
create_child_job,
)
from bookwyrm.models.job import ParentJob, ParentTask, SubTask
from bookwyrm.utils.tar import BookwyrmTarFile
logger = logging.getLogger(__name__)
@ -161,8 +157,10 @@ def get_or_create_edition(book_data, tar):
if cover_path:
tar.write_image_to_file(cover_path, new_book.cover)
# NOTE: clean_values removes "last_edited_by" because it's a user ID from the old database
# if this is required, bookwyrm_export_job will need to bring in the user who edited it.
# NOTE: clean_values removes "last_edited_by"
# because it's a user ID from the old database
# if this is required, bookwyrm_export_job will
# need to bring in the user who edited it.
# create parent
work = models.Work.objects.create(title=book["title"])
@ -197,7 +195,7 @@ def clean_values(data):
return new_data
def find_existing(cls, data, user):
def find_existing(cls, data):
"""Given a book or author, find any existing model instances"""
identifiers = [
@ -248,27 +246,31 @@ def upsert_readthroughs(data, user, book_id):
"""Take a JSON string of readthroughs, find or create the
instances in the database and return a list of saved instances"""
for rt in data:
for read_thru in data:
start_date = (
parse_datetime(rt["start_date"]) if rt["start_date"] is not None else None
parse_datetime(read_thru["start_date"])
if read_thru["start_date"] is not None
else None
)
finish_date = (
parse_datetime(rt["finish_date"]) if rt["finish_date"] is not None else None
parse_datetime(read_thru["finish_date"])
if read_thru["finish_date"] is not None
else None
)
stopped_date = (
parse_datetime(rt["stopped_date"])
if rt["stopped_date"] is not None
parse_datetime(read_thru["stopped_date"])
if read_thru["stopped_date"] is not None
else None
)
readthrough = {
"user": user,
"book": models.Edition.objects.get(id=book_id),
"progress": rt["progress"],
"progress_mode": rt["progress_mode"],
"progress": read_thru["progress"],
"progress_mode": read_thru["progress_mode"],
"start_date": start_date,
"finish_date": finish_date,
"stopped_date": stopped_date,
"is_active": rt["is_active"],
"is_active": read_thru["is_active"],
}
existing = models.ReadThrough.objects.filter(**readthrough).exists()
@ -311,7 +313,8 @@ def get_or_create_statuses(user, cls, data, book_id):
def upsert_lists(user, lists, items, book_id):
"""Take a list and ListItems as JSON and create DB entries if they don't already exist"""
"""Take a list and ListItems as JSON and
create DB entries if they don't already exist"""
book = models.Edition.objects.get(id=book_id)
@ -408,7 +411,7 @@ def update_user_settings(user, data):
@app.task(queue=IMPORTS, base=SubTask)
def update_user_settings_task(job_id, child_id):
def update_user_settings_task(job_id):
"""wrapper task for user's settings import"""
parent_job = BookwyrmImportJob.objects.get(id=job_id)
@ -433,7 +436,7 @@ def update_goals(user, data):
@app.task(queue=IMPORTS, base=SubTask)
def update_goals_task(job_id, child_id):
def update_goals_task(job_id):
"""wrapper task for user's goals import"""
parent_job = BookwyrmImportJob.objects.get(id=job_id)
@ -450,7 +453,7 @@ def upsert_saved_lists(user, values):
@app.task(queue=IMPORTS, base=SubTask)
def upsert_saved_lists_task(job_id, child_id):
def upsert_saved_lists_task(job_id):
"""wrapper task for user's saved lists import"""
parent_job = BookwyrmImportJob.objects.get(id=job_id)
@ -477,7 +480,7 @@ def upsert_follows(user, values):
@app.task(queue=IMPORTS, base=SubTask)
def upsert_follows_task(job_id, child_id):
def upsert_follows_task(job_id):
"""wrapper task for user's follows import"""
parent_job = BookwyrmImportJob.objects.get(id=job_id)
@ -504,7 +507,7 @@ def upsert_user_blocks(user, user_ids):
@app.task(queue=IMPORTS, base=SubTask)
def upsert_user_blocks_task(job_id, child_id):
def upsert_user_blocks_task(job_id):
"""wrapper task for user's blocks import"""
parent_job = BookwyrmImportJob.objects.get(id=job_id)

View file

@ -31,6 +31,8 @@ class Job(models.Model):
)
class Meta:
"""Make it abstract"""
abstract = True
def complete_job(self):
@ -119,7 +121,7 @@ class ParentJob(Job):
if not self.complete and self.has_completed:
self.complete_job()
def __terminate_job(self):
def __terminate_job(self): # pylint: disable=unused-private-member
"""Tell workers to ignore and not execute this task
& pending child tasks. Extend.
"""
@ -183,7 +185,9 @@ class ParentTask(app.Task):
Usage e.g. @app.task(base=ParentTask)
"""
def before_start(self, task_id, args, kwargs):
def before_start(
self, task_id, args, kwargs
): # pylint: disable=no-self-use, unused-argument
"""Handler called before the task starts. Override.
Prepare ParentJob before the task starts.
@ -208,7 +212,9 @@ class ParentTask(app.Task):
if kwargs["no_children"]:
job.set_status(ChildJob.Status.ACTIVE)
def on_success(self, retval, task_id, args, kwargs):
def on_success(
self, retval, task_id, args, kwargs
): # pylint: disable=no-self-use, unused-argument
"""Run by the worker if the task executes successfully. Override.
Update ParentJob on Task complete.
@ -241,7 +247,9 @@ class SubTask(app.Task):
Usage e.g. @app.task(base=SubTask)
"""
def before_start(self, task_id, args, kwargs):
def before_start(
self, task_id, args, kwargs
): # pylint: disable=no-self-use, unused-argument
"""Handler called before the task starts. Override.
Prepare ChildJob before the task starts.
@ -263,7 +271,9 @@ class SubTask(app.Task):
child_job.save(update_fields=["task_id"])
child_job.set_status(ChildJob.Status.ACTIVE)
def on_success(self, retval, task_id, args, kwargs):
def on_success(
self, retval, task_id, args, kwargs
): # pylint: disable=no-self-use, unused-argument
"""Run by the worker if the task executes successfully. Override.
Notify ChildJob of task completion.

View file

@ -1,6 +1,7 @@
""" alert a user to activity """
from django.db import models, transaction
from django.dispatch import receiver
from bookwyrm.models.bookwyrm_export_job import BookwyrmExportJob
from .base_model import BookWyrmModel
from . import (
Boost,
@ -10,7 +11,6 @@ from . import (
BookwyrmImportJob,
LinkDomain,
)
from bookwyrm.models.bookwyrm_export_job import BookwyrmExportJob
from . import ListItem, Report, Status, User, UserFollowRequest

View file

@ -141,7 +141,7 @@
<div class="table-container block content">
<table class="table is-striped is-fullwidth">
<tr>
{% url 'settings-imports' status as url %}
{% url 'settings-imports' status as url %}
<th>
{% trans "ID" %}
</th>
@ -231,7 +231,7 @@
<div class="table-container block content">
<table class="table is-striped is-fullwidth">
<tr>
{% url 'settings-imports' status as url %}
{% url 'settings-imports' status as url %}
<th>
{% trans "ID" %}
</th>
@ -299,5 +299,5 @@
</div>
{% include 'snippets/pagination.html' with page=user_imports path=request.path %}
{% endblock %}
</div>
{% endblock %}

View file

@ -1,3 +1,4 @@
"""test bookwyrm user export functions"""
import datetime
import time
import json
@ -110,7 +111,7 @@ class BookwyrmExport(TestCase):
)
# add to list
item = models.ListItem.objects.create(
models.ListItem.objects.create(
book_list=self.list,
user=self.local_user,
book=self.edition,
@ -226,7 +227,7 @@ class BookwyrmExport(TestCase):
json_data["books"][0]["quotes"][0]["quote"], "A rose by any other name"
)
def test_tar_export(self):
def test_tar_export(self): # pylint: disable=unnecessary-pass
"""test the tar export function"""
# TODO

View file

@ -5,14 +5,12 @@ import pathlib
from unittest.mock import patch
from django.db.models import Q
from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.test import TestCase
from bookwyrm import models
from bookwyrm.settings import DOMAIN
from bookwyrm.utils.tar import BookwyrmTarFile
import bookwyrm.models.bookwyrm_import_job as bookwyrm_import_job
from bookwyrm.models import bookwyrm_import_job
class BookwyrmImport(TestCase):
@ -246,7 +244,9 @@ class BookwyrmImport(TestCase):
self.assertEqual(author.name, "James C. Scott")
def test_get_or_create_edition_existing(self):
"""Test take a JSON string of books and editions, find or create the editions in the database and return a list of edition instances"""
"""Test take a JSON string of books and editions,
find or create the editions in the database and
return a list of edition instances"""
self.assertEqual(models.Edition.objects.count(), 1)
self.assertEqual(models.Edition.objects.count(), 1)
@ -258,7 +258,9 @@ class BookwyrmImport(TestCase):
self.assertEqual(models.Edition.objects.count(), 1)
def test_get_or_create_edition_not_existing(self):
"""Test take a JSON string of books and editions, find or create the editions in the database and return a list of edition instances"""
"""Test take a JSON string of books and editions,
find or create the editions in the database and
return a list of edition instances"""
self.assertEqual(models.Edition.objects.count(), 1)
@ -441,7 +443,8 @@ class BookwyrmImport(TestCase):
)
def test_upsert_list_existing(self):
"""Take a list and ListItems as JSON and create DB entries if they don't already exist"""
"""Take a list and ListItems as JSON and create DB entries
if they don't already exist"""
book_data = self.import_data["books"][0]
@ -456,7 +459,7 @@ class BookwyrmImport(TestCase):
name="my list of books", user=self.local_user
)
list_item = models.ListItem.objects.create(
models.ListItem.objects.create(
book=self.book, book_list=book_list, user=self.local_user, order=1
)
@ -489,7 +492,8 @@ class BookwyrmImport(TestCase):
)
def test_upsert_list_not_existing(self):
"""Take a list and ListItems as JSON and create DB entries if they don't already exist"""
"""Take a list and ListItems as JSON and create DB entries
if they don't already exist"""
book_data = self.import_data["books"][0]

View file

@ -1,5 +1,4 @@
""" test for app action functionality """
from collections import namedtuple
""" test for user export app functionality """
from unittest.mock import patch
from django.http import HttpResponse

View file

@ -1,12 +1,15 @@
"""manage tar files for user exports"""
import io
import tarfile
from uuid import uuid4
from django.core.files import File
import tarfile
import io
class BookwyrmTarFile(tarfile.TarFile):
def write_bytes(self, data: bytes, filename="archive.json"):
"""Add a file containing :data: bytestring with name :filename: to the archive"""
"""Create tar files for user exports"""
def write_bytes(self, data: bytes):
"""Add a file containing bytes to the archive"""
buffer = io.BytesIO(data)
info = tarfile.TarInfo("archive.json")
info.size = len(data)
@ -30,10 +33,12 @@ class BookwyrmTarFile(tarfile.TarFile):
self.addfile(info, fileobj=image)
def read(self, filename):
"""read data from the tar"""
with self.extractfile(filename) as reader:
return reader.read()
def write_image_to_file(self, filename, file_field):
"""add an image to the tar"""
extension = filename.rsplit(".")[-1]
with self.extractfile(filename) as reader:
filename = f"{str(uuid4())}.{extension}"

View file

@ -13,7 +13,7 @@ from django.views import View
from django.utils.decorators import method_decorator
from django.shortcuts import redirect
from bookwyrm import models, settings
from bookwyrm import models
from bookwyrm.models.bookwyrm_export_job import BookwyrmExportJob
from bookwyrm.settings import PAGE_LENGTH
@ -135,10 +135,11 @@ class ExportUser(View):
@method_decorator(login_required, name="dispatch")
class ExportArchive(View):
class ExportArchive(View): # pylint: disable=line-too-long
"""Serve the archive file"""
def get(self, request, archive_id):
"""download user export file"""
export = BookwyrmExportJob.objects.get(task_id=archive_id, user=request.user)
return HttpResponse(
export.export_data,