Repository: sissbruecker/linkding
Pull request #633 (2024-02-17 13:05:24)

Add k8s setup to community section
Hi there, just wanted to share my way of hosting linkding. Thanks for the software!


Patch:
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index 5e7a896..5bddff2 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -1,6 +1,6 @@
 name: linkding CI
 
-on: [push]
+on: [push, pull_request]
 
 jobs:
   unit_tests:
diff --git a/README.md b/README.md
index cdd520a..36b482f 100644
--- a/README.md
+++ b/README.md
@@ -212,6 +212,7 @@ This section lists community projects around using linkding, in alphabetical ord
 - [feed2linkding](https://codeberg.org/strubbl/feed2linkding) A commandline utility to add all web feed item links to linkding via API call. By [Strubbl](https://github.com/Strubbl)
 - [Helm Chart](https://charts.pascaliske.dev/charts/linkding/) Helm Chart for deploying linkding inside a Kubernetes cluster. By [pascaliske](https://github.com/pascaliske)
 - [iOS Shortcut using API and Tagging](https://gist.github.com/andrewdolphin/a7dff49505e588d940bec55132fab8ad) An iOS shortcut using the Linkding API (no extra logins required) that pulls previously used tags and allows tagging at the time of link creation.
+- [k8s + s3](https://github.com/jzck/linkding-k8s-s3) - Setup for hosting stateless linkding on k8s with sqlite replicated to s3. By [jzck](https://github.com/jzck)
 - [Linka!](https://github.com/cmsax/linka) Web app (also a PWA) for quickly searching & opening bookmarks in linkding, support multi keywords, exclude mode and other advance options. By [cmsax](https://github.com/cmsax)
 - [linkding-cli](https://github.com/bachya/linkding-cli) A command-line interface (CLI) to interact with the linkding REST API. Powered by [aiolinkding](https://github.com/bachya/aiolinkding). By [bachya](https://github.com/bachya)
 - [linkding-extension](https://github.com/jeroenpardon/linkding-extension) Chromium compatible extension that wraps the linkding bookmarklet. Tested with Chrome, Edge, Brave. By [jeroenpardon](https://github.com/jeroenpardon)
diff --git a/bookmarks/views/partials/contexts.py b/bookmarks/views/partials/contexts.py
index 998f038..700a74d 100644
--- a/bookmarks/views/partials/contexts.py
+++ b/bookmarks/views/partials/contexts.py
@@ -1,5 +1,6 @@
 import urllib.parse
 from typing import Set, List
+import re
 
 from django.core.handlers.wsgi import WSGIRequest
 from django.core.paginator import Paginator
@@ -11,13 +12,13 @@ from bookmarks import utils
 from bookmarks.models import (
     Bookmark,
     BookmarkSearch,
-    BookmarkSearchForm,
     User,
     UserProfile,
     Tag,
 )
 
 DEFAULT_PAGE_SIZE = 30
+CJK_RE = re.compile(r"[\u4e00-\u9fff]+")
 
 
 class BookmarkItem:
@@ -123,13 +124,13 @@ class BookmarkListContext:
         )
 
     def get_base_url(self):
-        raise Exception(f"Must be implemented by subclass")
+        raise Exception("Must be implemented by subclass")
 
     def get_base_action_url(self):
-        raise Exception(f"Must be implemented by subclass")
+        raise Exception("Must be implemented by subclass")
 
     def get_bookmark_query_set(self):
-        raise Exception(f"Must be implemented by subclass")
+        raise Exception("Must be implemented by subclass")
 
 
 class ActiveBookmarkListContext(BookmarkListContext):
@@ -178,23 +179,33 @@ class TagGroup:
         self.tags = []
         self.char = char
 
+    def __repr__(self):
+        return f"<{self.char} TagGroup>"
+
     @staticmethod
     def create_tag_groups(tags: Set[Tag]):
         # Ensure groups, as well as tags within groups, are ordered alphabetically
         sorted_tags = sorted(tags, key=lambda x: str.lower(x.name))
         group = None
         groups = []
+        cjk_used = False
+        cjk_group = TagGroup("Ideographic")
 
         # Group tags that start with a different character than the previous one
         for tag in sorted_tags:
             tag_char = tag.name[0].lower()
-
-            if not group or group.char != tag_char:
+            if CJK_RE.match(tag_char):
+                cjk_used = True
+                cjk_group.tags.append(tag)
+            elif not group or group.char != tag_char:
                 group = TagGroup(tag_char)
                 groups.append(group)
+                group.tags.append(tag)
+            else:
+                group.tags.append(tag)
 
-            group.tags.append(tag)
-
+        if cjk_used:
+            groups.append(cjk_group)
         return groups
 
 
@@ -224,7 +235,7 @@ class TagCloudContext:
         self.has_selected_tags = has_selected_tags
 
     def get_tag_query_set(self):
-        raise Exception(f"Must be implemented by subclass")
+        raise Exception("Must be implemented by subclass")
 
     def get_selected_tags(self, tags: List[Tag]):
         parsed_query = queries.parse_query_string(self.search.q)
diff --git a/requirements.dev.txt b/requirements.dev.txt
index c7bdcc8..7d4b749 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -12,7 +12,7 @@ click==8.1.7
     # via black
 coverage==7.4.1
     # via -r requirements.dev.in
-django==5.0.1
+django==5.0.2
     # via
     #   django-appconf
     #   django-debug-toolbar
diff --git a/requirements.txt b/requirements.txt
index 4e8081d..bea3c7e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -20,7 +20,7 @@ click==8.1.7
     # via waybackpy
 confusable-homoglyphs==3.2.0
     # via django-registration
-django==5.0.1
+django==5.0.2
     # via
     #   -r requirements.in
     #   django-registration


Test patch:
diff --git a/bookmarks/tests/test_tag_cloud_template.py b/bookmarks/tests/test_tag_cloud_template.py
index ca257a3..2ac61e0 100644
--- a/bookmarks/tests/test_tag_cloud_template.py
+++ b/bookmarks/tests/test_tag_cloud_template.py
@@ -39,7 +39,7 @@ class TagCloudTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
             group_element = group_elements[group_index]
             link_elements = group_element.select("a")
 
-            self.assertEqual(len(link_elements), len(tags))
+            self.assertEqual(len(link_elements), len(tags), tags)
 
             for tag_index, tag in enumerate(tags, start=0):
                 link_element = link_elements[tag_index]
@@ -50,6 +50,59 @@ class TagCloudTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
         link_elements = soup.select("p.selected-tags a")
         self.assertEqual(len(link_elements), count)
 
+    def test_cjk_using_single_group(self):
+        """
+        Ideographic characters will be using the same group
+        While other japanese and korean characters will have separate groups.
+        """
+        tags = [
+            self.setup_tag(name="Aardvark"),
+            self.setup_tag(name="Armadillo"),
+            self.setup_tag(name="あひる"),
+            self.setup_tag(name="あきらか"),
+            self.setup_tag(name="アヒル"),
+            self.setup_tag(name="アキラカ"),
+            self.setup_tag(name="ひる"),
+            self.setup_tag(name="アヒル"),
+            self.setup_tag(name="오리"),
+            self.setup_tag(name="물"),
+            self.setup_tag(name="家鴨"),
+            self.setup_tag(name="感じ"),
+        ]
+        self.setup_bookmark(tags=tags)
+        rendered_template = self.render_template()
+
+        self.assertTagGroups(
+            rendered_template,
+            [
+                [
+                    "Aardvark",
+                    "Armadillo",
+                ],
+                [
+                    "あきらか",
+                    "あひる",
+                ],
+                [
+                    "ひる",
+                ],
+                [
+                    "アキラカ",
+                    "アヒル",
+                ],
+                [
+                    "물",
+                ],
+                [
+                    "오리",
+                ],
+                [
+                    "家鴨",
+                    "感じ",
+                ],
+            ],
+        )
+
     def test_group_alphabetically(self):
         tags = [
             self.setup_tag(name="Cockatoo"),