import csv ui

This commit is contained in:
mein Name 2025-03-03 16:36:18 -05:00 committed by Henri Dickson
parent 5911d422f5
commit 1d7816d9d7
4 changed files with 91 additions and 1 deletions

View file

@ -333,7 +333,8 @@ class CsvImporter(Task):
success = import_function(row)
self.progress(success)
def validate_file(self, filename: str) -> bool:
@classmethod
def validate_file(cls, filename: str) -> bool:
"""Validate that the given file is a valid CSV export ZIP file.
Args:

View file

@ -213,6 +213,66 @@
</form>
</details>
</article>
<article>
<details>
<summary>{% trans 'Import marks, reviews and notes from CSV' %}</summary>
<form action="{% url 'users:import_csv' %}"
method="post"
enctype="multipart/form-data">
{% csrf_token %}
<ul>
<li>{% trans 'Upload a ZIP file containing CSV files exported from NeoDB.' %}</li>
<li>{% trans 'Existing marks and reviews with newer dates will be preserved.' %}</li>
</ul>
<br>
<input type="file" name="file" required accept=".zip">
<p>
{% trans 'Visibility' %}:
<br>
<label for="csv_visibility_0">
<input type="radio"
name="visibility"
value="0"
required=""
id="csv_visibility_0"
checked>
{% trans 'Public' %}
</label>
<label for="csv_visibility_1">
<input type="radio"
name="visibility"
value="1"
required=""
id="csv_visibility_1">
{% trans 'Followers Only' %}
</label>
<label for="csv_visibility_2">
<input type="radio"
name="visibility"
value="2"
required=""
id="csv_visibility_2">
{% trans 'Mentioned Only' %}
</label>
</p>
<input type="submit" value="{% trans 'Import' %}" />
<small>
{% if csv_import_task %}
<br>
{% trans 'Last import started' %}: {{ csv_import_task.created_time }}
{% trans 'Status' %}: {{ csv_import_task.get_state_display }}。
<br>
{{ csv_import_task.message }}
{% if csv_import_task.metadata.failed_items %}
{% trans 'Failed items' %}:
<br>
<textarea readonly>{% for item in csv_import_task.metadata.failed_items %}{{item}}&#10;{% endfor %}</textarea>
{% endif %}
{% endif %}
</small>
</form>
</details>
</article>
<article>
<details>
<summary>{% trans 'Export Data' %}</summary>

View file

@ -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"),

View file

@ -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"))