mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-22 03:13:02 +00:00
Improve performance of the export bookmark service.
Avoid temporary copies in memory of the Bookmark and Tag instances by returning an iterable from services.exporter.export_netscape_html.
This commit is contained in:
parent
c3149409b0
commit
ced4968522
4 changed files with 34 additions and 36 deletions
|
@ -1,33 +1,30 @@
|
|||
import html
|
||||
from typing import List
|
||||
import itertools
|
||||
from typing import Iterable
|
||||
|
||||
from bookmarks.models import Bookmark
|
||||
|
||||
BookmarkDocument = List[str]
|
||||
|
||||
def export_netscape_html(bookmarks: Iterable[Bookmark]):
|
||||
def _append_bookmarks():
|
||||
for bookmark in bookmarks:
|
||||
yield from append_bookmark(bookmark)
|
||||
|
||||
return itertools.chain(append_header(), append_list_start(), _append_bookmarks(), append_list_end())
|
||||
|
||||
|
||||
def export_netscape_html(bookmarks: List[Bookmark]):
|
||||
doc = []
|
||||
append_header(doc)
|
||||
append_list_start(doc)
|
||||
[append_bookmark(doc, bookmark) for bookmark in bookmarks]
|
||||
append_list_end(doc)
|
||||
|
||||
return "\n\r".join(doc)
|
||||
def append_header():
|
||||
yield "<!DOCTYPE NETSCAPE-Bookmark-file-1>"
|
||||
yield '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">'
|
||||
yield "<TITLE>Bookmarks</TITLE>"
|
||||
yield "<H1>Bookmarks</H1>"
|
||||
|
||||
|
||||
def append_header(doc: BookmarkDocument):
|
||||
doc.append("<!DOCTYPE NETSCAPE-Bookmark-file-1>")
|
||||
doc.append('<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">')
|
||||
doc.append("<TITLE>Bookmarks</TITLE>")
|
||||
doc.append("<H1>Bookmarks</H1>")
|
||||
def append_list_start():
|
||||
yield "<DL><p>"
|
||||
|
||||
|
||||
def append_list_start(doc: BookmarkDocument):
|
||||
doc.append("<DL><p>")
|
||||
|
||||
|
||||
def append_bookmark(doc: BookmarkDocument, bookmark: Bookmark):
|
||||
def append_bookmark(bookmark: Bookmark):
|
||||
url = bookmark.url
|
||||
title = html.escape(bookmark.resolved_title or "")
|
||||
desc = html.escape(bookmark.resolved_description or "")
|
||||
|
@ -42,13 +39,12 @@ def append_bookmark(doc: BookmarkDocument, bookmark: Bookmark):
|
|||
added = int(bookmark.date_added.timestamp())
|
||||
modified = int(bookmark.date_modified.timestamp())
|
||||
|
||||
doc.append(
|
||||
f'<DT><A HREF="{url}" ADD_DATE="{added}" LAST_MODIFIED="{modified}" PRIVATE="{private}" TOREAD="{toread}" TAGS="{tags}">{title}</A>'
|
||||
)
|
||||
yield f'<DT><A HREF="{url}" ADD_DATE="{added}" LAST_MODIFIED="{modified}" PRIVATE="{private}" TOREAD="{toread}" TAGS="{tags}">{title}</A>'
|
||||
|
||||
|
||||
if desc:
|
||||
doc.append(f"<DD>{desc}")
|
||||
yield f"<DD>{desc}"
|
||||
|
||||
|
||||
def append_list_end(doc: BookmarkDocument):
|
||||
doc.append("</DL><p>")
|
||||
def append_list_end():
|
||||
yield "</DL><p>"
|
||||
|
|
|
@ -74,7 +74,7 @@ class ExporterTestCase(TestCase, BookmarkFactoryMixin):
|
|||
is_archived=True,
|
||||
),
|
||||
]
|
||||
html = exporter.export_netscape_html(bookmarks)
|
||||
html = "\r\n".join(exporter.export_netscape_html(bookmarks))
|
||||
|
||||
lines = [
|
||||
'<DT><A HREF="https://example.com/1" ADD_DATE="1" LAST_MODIFIED="11" PRIVATE="1" TOREAD="0" TAGS="">Title 1</A>',
|
||||
|
@ -89,7 +89,7 @@ class ExporterTestCase(TestCase, BookmarkFactoryMixin):
|
|||
'<DT><A HREF="https://example.com/7" ADD_DATE="7" LAST_MODIFIED="77" PRIVATE="1" TOREAD="0" TAGS="linkding:archived">Title 7</A>',
|
||||
'<DT><A HREF="https://example.com/8" ADD_DATE="8" LAST_MODIFIED="88" PRIVATE="1" TOREAD="0" TAGS="tag4,tag5,linkding:archived">Title 8</A>',
|
||||
]
|
||||
self.assertIn("\n\r".join(lines), html)
|
||||
self.assertIn("\r\n".join(lines), html)
|
||||
|
||||
def test_escape_html(self):
|
||||
bookmark = self.setup_bookmark(
|
||||
|
@ -97,7 +97,7 @@ class ExporterTestCase(TestCase, BookmarkFactoryMixin):
|
|||
description="The <style> HTML element contains style information for a document, or part of a document.",
|
||||
notes="Interesting notes about the <style> HTML element.",
|
||||
)
|
||||
html = exporter.export_netscape_html([bookmark])
|
||||
html = "\r\n".join(exporter.export_netscape_html([bookmark]))
|
||||
|
||||
self.assertIn("<style>: The Style Information element", html)
|
||||
self.assertIn(
|
||||
|
@ -111,4 +111,4 @@ class ExporterTestCase(TestCase, BookmarkFactoryMixin):
|
|||
bookmark.title = ""
|
||||
bookmark.description = ""
|
||||
bookmark.save()
|
||||
exporter.export_netscape_html([bookmark])
|
||||
"\r\n".join(exporter.export_netscape_html([bookmark]))
|
||||
|
|
|
@ -7,7 +7,6 @@ from django.conf import settings as django_settings
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import prefetch_related_objects
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
|
@ -233,14 +232,17 @@ def bookmark_import(request):
|
|||
def bookmark_export(request):
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
bookmarks = Bookmark.objects.filter(owner=request.user)
|
||||
# Prefetch tags to prevent n+1 queries
|
||||
prefetch_related_objects(bookmarks, "tags")
|
||||
bookmarks = Bookmark.objects.filter(owner=request.user).prefetch_related("tags")
|
||||
file_content = exporter.export_netscape_html(bookmarks)
|
||||
def _newline_appended():
|
||||
for line in file_content:
|
||||
yield line
|
||||
yield "\r\n"
|
||||
|
||||
response = HttpResponse(content_type="text/plain; charset=UTF-8")
|
||||
response["Content-Disposition"] = 'attachment; filename="bookmarks.html"'
|
||||
response.write(file_content)
|
||||
response.writelines(_newline_appended())
|
||||
|
||||
return response
|
||||
except:
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "linkding",
|
||||
"version": "1.35.0",
|
||||
"version": "1.36.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "linkding",
|
||||
"version": "1.35.0",
|
||||
"version": "1.36.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@hotwired/turbo": "^8.0.6",
|
||||
|
|
Loading…
Reference in a new issue