diff --git a/bookmarks/services/tasks.py b/bookmarks/services/tasks.py
index 6d24ba2..e97f55c 100644
--- a/bookmarks/services/tasks.py
+++ b/bookmarks/services/tasks.py
@@ -1,6 +1,7 @@
import functools
import logging
import os
+from typing import List
import waybackpy
from django.conf import settings
@@ -228,6 +229,26 @@ def create_html_snapshot(bookmark: Bookmark):
if not is_html_snapshot_feature_active():
return
+ asset = _create_snapshot_asset(bookmark)
+ asset.save()
+
+
+def create_html_snapshots(bookmark_list: List[Bookmark]):
+ if not is_html_snapshot_feature_active():
+ return
+
+ assets_to_create = []
+ for bookmark in bookmark_list:
+ asset = _create_snapshot_asset(bookmark)
+ assets_to_create.append(asset)
+
+ BookmarkAsset.objects.bulk_create(assets_to_create)
+
+
+MAX_SNAPSHOT_FILENAME_LENGTH = 192
+
+
+def _create_snapshot_asset(bookmark: Bookmark) -> BookmarkAsset:
timestamp = formats.date_format(timezone.now(), "SHORT_DATE_FORMAT")
asset = BookmarkAsset(
bookmark=bookmark,
@@ -236,10 +257,7 @@ def create_html_snapshot(bookmark: Bookmark):
display_name=f"HTML snapshot from {timestamp}",
status=BookmarkAsset.STATUS_PENDING,
)
- asset.save()
-
-
-MAX_SNAPSHOT_FILENAME_LENGTH = 192
+ return asset
def _generate_snapshot_filename(asset: BookmarkAsset) -> str:
@@ -305,3 +323,23 @@ def _create_html_snapshot_task(asset_id: int):
)
asset.status = BookmarkAsset.STATUS_FAILURE
asset.save()
+
+
+def create_missing_html_snapshots(user: User) -> int:
+ if not is_html_snapshot_feature_active():
+ return 0
+
+ bookmarks_without_snapshots = Bookmark.objects.filter(owner=user).exclude(
+ bookmarkasset__asset_type=BookmarkAsset.TYPE_SNAPSHOT,
+ bookmarkasset__status__in=[
+ BookmarkAsset.STATUS_PENDING,
+ BookmarkAsset.STATUS_COMPLETE,
+ ],
+ )
+ bookmarks_without_snapshots |= Bookmark.objects.filter(owner=user).exclude(
+ bookmarkasset__asset_type=BookmarkAsset.TYPE_SNAPSHOT
+ )
+
+ create_html_snapshots(list(bookmarks_without_snapshots))
+
+ return bookmarks_without_snapshots.count()
diff --git a/bookmarks/templates/settings/general.html b/bookmarks/templates/settings/general.html
index f495869..acec66d 100644
--- a/bookmarks/templates/settings/general.html
+++ b/bookmarks/templates/settings/general.html
@@ -8,6 +8,12 @@
{# Profile section #}
Change password
@@ -120,13 +126,6 @@
{% if request.user_profile.enable_favicons and enable_refresh_favicons %}
{% endif %}
- {% if refresh_favicons_success_message %}
-
- {{ refresh_favicons_success_message }}
-
- {{ update_profile_success_message }}
- Profile
- {{ import_success_message }} -
-- {{ import_errors_message }} -
-Profile updated
- """, - html, - ) + self.assertSuccessMessage(html, "Profile updated") def test_update_profile_should_not_be_called_without_respective_form_action(self): form_data = { @@ -156,13 +169,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): self.assertEqual(response.status_code, 200) self.assertEqual(self.user.profile.theme, UserProfile.THEME_AUTO) - self.assertInHTML( - """ -Profile updated
- """, - html, - count=0, - ) + self.assertSuccessMessage(html, "Profile updated", count=0) def test_enable_favicons_should_schedule_icon_update(self): with patch.object( @@ -210,13 +217,8 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): html = response.content.decode() mock_schedule_refresh_favicons.assert_called_once() - self.assertInHTML( - """ -- Scheduled favicon update. This may take a while... -
- """, - html, + self.assertSuccessMessage( + html, "Scheduled favicon update. This may take a while..." ) def test_refresh_favicons_should_not_be_called_without_respective_form_action(self): @@ -230,14 +232,8 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): html = response.content.decode() mock_schedule_refresh_favicons.assert_not_called() - self.assertInHTML( - """ -- Scheduled favicon update. This may take a while... -
- """, - html, - count=0, + self.assertSuccessMessage( + html, "Scheduled favicon update. This may take a while...", count=0 ) def test_refresh_favicons_should_be_visible_when_favicons_enabled_in_profile(self): @@ -365,3 +361,57 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): with patch.object(requests, "get", return_value=latest_version_response_mock): version_info = get_version_info(random.random()) self.assertEqual(version_info, app_version) + + @override_settings(LD_ENABLE_SNAPSHOTS=True) + def test_create_missing_html_snapshots(self): + with patch.object( + tasks, "create_missing_html_snapshots" + ) as mock_create_missing_html_snapshots: + mock_create_missing_html_snapshots.return_value = 5 + form_data = { + "create_missing_html_snapshots": "", + } + response = self.client.post( + reverse("bookmarks:settings.general"), form_data + ) + html = response.content.decode() + + mock_create_missing_html_snapshots.assert_called_once() + self.assertSuccessMessage( + html, "Queued 5 missing snapshots. This may take a while..." + ) + + @override_settings(LD_ENABLE_SNAPSHOTS=True) + def test_create_missing_html_snapshots_no_missing_snapshots(self): + with patch.object( + tasks, "create_missing_html_snapshots" + ) as mock_create_missing_html_snapshots: + mock_create_missing_html_snapshots.return_value = 0 + form_data = { + "create_missing_html_snapshots": "", + } + response = self.client.post( + reverse("bookmarks:settings.general"), form_data + ) + html = response.content.decode() + + mock_create_missing_html_snapshots.assert_called_once() + self.assertSuccessMessage(html, "No missing snapshots found.") + + def test_create_missing_html_snapshots_should_not_be_called_without_respective_form_action( + self, + ): + with patch.object( + tasks, "create_missing_html_snapshots" + ) as mock_create_missing_html_snapshots: + mock_create_missing_html_snapshots.return_value = 5 + form_data = {} + response = self.client.post( + reverse("bookmarks:settings.general"), form_data + ) + html = response.content.decode() + + mock_create_missing_html_snapshots.assert_not_called() + self.assertSuccessMessage( + html, "Queued 5 missing snapshots. This may take a while...", count=0 + ) diff --git a/bookmarks/tests/test_settings_import_view.py b/bookmarks/tests/test_settings_import_view.py index 06b6f02..eee9ad4 100644 --- a/bookmarks/tests/test_settings_import_view.py +++ b/bookmarks/tests/test_settings_import_view.py @@ -11,19 +11,27 @@ class SettingsImportViewTestCase(TestCase, BookmarkFactoryMixin): user = self.get_or_create_test_user() self.client.force_login(user) - def assertFormSuccessHint(self, response, text: str): - self.assertContains(response, '