+ + {% trans 'Import marks, reviews and notes from CSV' %} + + {% csrf_token %} + + {% trans 'Upload a ZIP file containing CSV files exported from NeoDB.' %} + {% trans 'Existing marks and reviews with newer dates will be preserved.' %} + + + + + {% trans 'Visibility' %}: + + + + {% trans 'Public' %} + + + + {% trans 'Followers Only' %} + + + + {% trans 'Mentioned Only' %} + + + + + {% if csv_import_task %} + + {% trans 'Last import started' %}: {{ csv_import_task.created_time }} + {% trans 'Status' %}: {{ csv_import_task.get_state_display }}。 + + {{ csv_import_task.message }} + {% if csv_import_task.metadata.failed_items %} + {% trans 'Failed items' %}: + + {% for item in csv_import_task.metadata.failed_items %}{{item}} {% endfor %} + {% endif %} + {% endif %} + + + +
{% trans 'Export Data' %} diff --git a/users/urls.py b/users/urls.py index 2aa9878e..689f478e 100644 --- a/users/urls.py +++ b/users/urls.py @@ -15,6 +15,7 @@ urlpatterns = [ path("data/import/douban", import_douban, name="import_douban"), path("data/import/letterboxd", import_letterboxd, name="import_letterboxd"), path("data/import/opml", import_opml, name="import_opml"), + path("data/import/csv", import_csv, name="import_csv"), path("data/export/reviews", export_reviews, name="export_reviews"), path("data/export/marks", export_marks, name="export_marks"), path("data/export/csv", export_csv, name="export_csv"), diff --git a/users/views/data.py b/users/views/data.py index c13aec67..f8ae1be0 100644 --- a/users/views/data.py +++ b/users/views/data.py @@ -14,6 +14,7 @@ from django.utils.translation import gettext as _ from common.utils import GenerateDateUUIDMediaFilePath from journal.exporters import CsvExporter, DoufenExporter, NdjsonExporter from journal.importers import ( + CsvImporter, DoubanImporter, GoodreadsImporter, LetterboxdImporter, @@ -98,6 +99,7 @@ def data(request): "import_task": DoubanImporter.latest_task(request.user), "export_task": DoufenExporter.latest_task(request.user), "csv_export_task": CsvExporter.latest_task(request.user), + "csv_import_task": CsvImporter.latest_task(request.user), "ndjson_export_task": NdjsonExporter.latest_task(request.user), "letterboxd_task": LetterboxdImporter.latest_task(request.user), "goodreads_task": GoodreadsImporter.latest_task(request.user), @@ -319,3 +321,29 @@ def import_opml(request): else: messages.add_message(request, messages.ERROR, _("Invalid file.")) return redirect(reverse("users:data")) + + +@login_required +def import_csv(request): + if request.method == "POST": + f = ( + settings.MEDIA_ROOT + + "/" + + GenerateDateUUIDMediaFilePath("x.zip", settings.SYNC_FILE_PATH_ROOT) + ) + os.makedirs(os.path.dirname(f), exist_ok=True) + with open(f, "wb+") as destination: + for chunk in request.FILES["file"].chunks(): + destination.write(chunk) + if not CsvImporter.validate_file(f): + messages.add_message(request, messages.ERROR, _("Invalid file.")) + return redirect(reverse("users:data")) + CsvImporter.create( + request.user, + visibility=int(request.POST.get("visibility", 0)), + file=f, + ).enqueue() + messages.add_message( + request, messages.INFO, _("File is uploaded and will be imported soon.") + ) + return redirect(reverse("users:data"))