From abd6cdc60ccbb21f1d9dce146ca22cb9679a27e8 Mon Sep 17 00:00:00 2001 From: Olivier Bado-Faustin <12731381+Badatos@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:31:20 +0100 Subject: [PATCH] Display a tag cloud with tags assigned to videos + Store tag list in CACHE + add a reindex_videos script to recreate ES video index --- pod/main/configuration.json | 36 ++++++++----- pod/main/static/css/pod.css | 29 ++++++++++ pod/main/templates/aside.html | 15 +++--- pod/urls.py | 4 +- pod/video/context_processors.py | 8 +++ .../management/commands/cache_video_data.py | 4 +- .../management/commands/reindex_videos.py | 54 +++++++++++++++++++ pod/video/templates/videos/filter_aside.html | 38 +++++++++---- pod/video/templatetags/video_tags.py | 19 +++---- pod/video/utils.py | 9 ++++ pod/video/views.py | 5 -- 11 files changed, 174 insertions(+), 47 deletions(-) create mode 100644 pod/video/management/commands/reindex_videos.py diff --git a/pod/main/configuration.json b/pod/main/configuration.json index 20b03f0fd5..dc8540008d 100644 --- a/pod/main/configuration.json +++ b/pod/main/configuration.json @@ -5541,7 +5541,7 @@ }, "settings": { "CAS": { - "default_value": "1.5.2", + "default_value": "1.5.3", "description": { "en": "", "fr": [ @@ -5553,7 +5553,7 @@ "pod_version_init": "3.1" }, "ModelTranslation": { - "default_value": "0.18.7", + "default_value": "0.19.11", "description": { "en": "", "fr": [ @@ -5566,7 +5566,7 @@ "pod_version_init": "3.1" }, "captcha": { - "default_value": "0.5.17", + "default_value": "0.6.0", "description": { "en": "", "fr": [ @@ -5619,7 +5619,7 @@ "pod_version_init": "3.1" }, "honeypot": { - "default_value": "1.0.3", + "default_value": "1.2.1", "description": { "en": "", "fr": [ @@ -5632,7 +5632,7 @@ "pod_version_init": "3.1" }, "mozilla_django_oidc": { - "default_value": "3.0.0", + "default_value": "4.0.1", "description": { "en": "", "fr": [ @@ -5644,7 +5644,7 @@ "pod_version_init": "3.1" }, "pwa": { - "default_value": "1.1.0", + "default_value": "2.0.1", "description": { "en": "", "fr": [ @@ -5676,11 +5676,11 @@ "pod_version_init": "3.4" }, "rest_framework": { - "default_value": "3.14.0", + "default_value": "3.15.2", "description": { "en": "", "fr": [ - "version 3.14.0 : mise en place de l’API rest pour l’application", + "mise en place de l’API rest pour l’application", "[django-rest-framework.org](https://www.django-rest-framework.org/)" ] }, @@ -5700,7 +5700,7 @@ "pod_version_init": "3.1" }, "sorl.thumbnail": { - "default_value": "12.9.0", + "default_value": "12.11.0", "description": { "en": "", "fr": [ @@ -5720,13 +5720,25 @@ "[django-tagging.readthedocs.io](https://django-tagging.readthedocs.io/en/develop/#settings)" ] }, - "pod_version_end": "", + "pod_version_end": "4.0.0", "pod_version_init": "3.1" + }, + "tagulous": { + "default_value": "2.1.0", + "description": { + "en": "", + "fr": [ + "Gestion des mots-clés associés à un objet Django", + "[django-tagulous.readthedocs.io](https://django-tagulous.readthedocs.io)" + ] + }, + "pod_version_end": "", + "pod_version_init": "4.0.0" } }, "title": { - "en": "", - "fr": "Information générale" + "en": "General information", + "fr": "Informations générales" } } } diff --git a/pod/main/static/css/pod.css b/pod/main/static/css/pod.css index c125a28371..374d99ba1b 100755 --- a/pod/main/static/css/pod.css +++ b/pod/main/static/css/pod.css @@ -65,6 +65,11 @@ --pod-alert: #fc8670; --pod-alert-dark: #b11030; + /* Tag cloud colors */ + --pod-tag-color1: 16, 131, 22; + --pod-tag-color2: 51, 51, 170; + --pod-tag-color3: 197, 53, 143; + /**** font family ****/ /* For better accessibility, avoid fonts where [1, i, L] or [O, 0] are the same. */ @@ -162,6 +167,30 @@ tr, margin-right: 0.5em; } +/** TAGs Cloud */ +.tag-cloud{ + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-evenly; +} + +.tag-cloud>a { + line-height: 1.5; +} + +.tag-cloud>*:nth-child(2n+1) { --bs-link-color-rgb: var(--pod-tag-color1); } +.tag-cloud>*:nth-child(3n+1) { --bs-link-color-rgb: var(--pod-tag-color2); } +.tag-cloud>*:nth-child(4n+1) { --bs-link-color-rgb: var(--pod-tag-color3); } + +.tag-1{font-size: 1em;} +.tag-2{font-size: 1.1em;} +.tag-3{font-size: 1.2em;} +.tag-4{font-size: 1.3em;} +.tag-5{font-size: 1.4em;} +.tag-6{font-size: 1.5em;} + + .pod-card--video a:not(.btn) { color: inherit; } diff --git a/pod/main/templates/aside.html b/pod/main/templates/aside.html index b455ec2364..0831ef99ad 100644 --- a/pod/main/templates/aside.html +++ b/pod/main/templates/aside.html @@ -1,5 +1,4 @@ -{% load i18n %} -{% load video_tags %} +{% load i18n video_tags %} {% spaceless %} {% if HIDE_SHARE == False %} @@ -24,7 +23,7 @@
- {% for tag in tag_cloud %}
-
+
+ {% for tag in TAGS %}
+
{{ tag.name }}
{% endfor %}
diff --git a/pod/urls.py b/pod/urls.py
index 3de81b5bdc..75abf501bb 100644
--- a/pod/urls.py
+++ b/pod/urls.py
@@ -45,8 +45,8 @@
urlpatterns = [
path("select2/", include("django_select2.urls")),
- re_path("robots.txt", robots_txt),
- re_path("info_pod.json", info_pod),
+ path("robots.txt", robots_txt),
+ path("info_pod.json", info_pod),
re_path(r"^admin/", admin.site.urls),
# Translation
path("i18n/", include("django.conf.urls.i18n")),
diff --git a/pod/video/context_processors.py b/pod/video/context_processors.py
index 86b045162c..30bfd69854 100644
--- a/pod/video/context_processors.py
+++ b/pod/video/context_processors.py
@@ -6,6 +6,8 @@
from pod.video.models import Discipline
from pod.video.models import Video
+from pod.video.utils import get_tag_cloud
+
from django.db.models import Count, Sum
from django.db.models import Q
from django.db.models import Exists
@@ -97,6 +99,11 @@ def context_video_data(request):
)
cache.set("TYPES", types, timeout=CACHE_VIDEO_DEFAULT_TIMEOUT)
+ tags = cache.get("TAGS")
+ if tags is None:
+ tags = get_tag_cloud()
+ cache.set("TAGS", tags, timeout=CACHE_VIDEO_DEFAULT_TIMEOUT)
+
disciplines = cache.get("DISCIPLINES")
if disciplines is None:
disciplines = (
@@ -132,4 +139,5 @@ def context_video_data(request):
"VIDEOS_COUNT": VIDEOS_COUNT,
"VIDEOS_DURATION": VIDEOS_DURATION,
"CHANNELS_PER_BATCH": CHANNELS_PER_BATCH,
+ "TAGS": tags
}
diff --git a/pod/video/management/commands/cache_video_data.py b/pod/video/management/commands/cache_video_data.py
index 22c5517200..760d4f0633 100644
--- a/pod/video/management/commands/cache_video_data.py
+++ b/pod/video/management/commands/cache_video_data.py
@@ -15,9 +15,9 @@ class Command(BaseCommand):
+ "types, discipline, video count and videos duration"
)
- def handle(self, *args, **options):
+ def handle(self, *args, **options) -> None:
"""Store video data in cache."""
- cache.delete_many(["DISCIPLINES", "VIDEOS_COUNT", "VIDEOS_DURATION", "TYPES"])
+ cache.delete_many(["DISCIPLINES", "VIDEOS_COUNT", "VIDEOS_DURATION", "TYPES", "TAGS"])
video_data = context_video_data(request=None)
msg = "Successfully store video data in cache"
for data in video_data:
diff --git a/pod/video/management/commands/reindex_videos.py b/pod/video/management/commands/reindex_videos.py
new file mode 100644
index 0000000000..c09665fdc6
--- /dev/null
+++ b/pod/video/management/commands/reindex_videos.py
@@ -0,0 +1,54 @@
+"""Script reindexing all videos (useful in case of loss of the ElasticSearch database)"""
+
+from django.core.management.base import BaseCommand
+from pod.video.models import Video
+from pod.video_search.models import index_video
+
+
+def reindex_all_videos(dry_run: bool) -> int:
+ """Reindex all videos."""
+ print("\nReindexing all videos...")
+ videos = Video.objects.all()
+ nb_videos = 0
+ for vid in videos:
+ print(".", end="")
+ if not dry_run:
+ index_video(vid)
+ nb_videos += 1
+ print("")
+ return nb_videos
+
+
+class Command(BaseCommand):
+ """Reindex all videos."""
+
+ help = "Reindex all videos (useful in case of loss of the ElasticSearch database)"
+
+ def add_arguments(self, parser) -> None:
+ """Allow arguments to be used with the command."""
+ parser.add_argument(
+ "--dry",
+ help="Simulate what would be reindexed.",
+ action="store_true",
+ default=False,
+ )
+
+ def handle(self, *args, **options) -> None:
+ """Handle the clean_video_files command call."""
+ if options["dry"]:
+ print("Simulation mode ('dry'). Nothing will be deleted.")
+ self.nb_reindexed = reindex_all_videos(options["dry"])
+
+ self.print_resume(options["dry"])
+
+ def print_resume(self, dry_run: bool) -> None:
+ """Print summary of reindexed objects."""
+
+ if dry_run:
+ print(
+ "[DRY RUN] %i video(s) would have been reindexed."
+ % (self.nb_reindexed)
+ )
+ else:
+ print("%i video(s) reindexed." % self.nb_reindexed)
+ print("Have a nice day ;)")
diff --git a/pod/video/templates/videos/filter_aside.html b/pod/video/templates/videos/filter_aside.html
index a29306a2e1..2da17edd67 100644
--- a/pod/video/templates/videos/filter_aside.html
+++ b/pod/video/templates/videos/filter_aside.html
@@ -1,6 +1,4 @@
-{% load i18n %}
-{% load video_tags %}
-{% load thumbnail %}
+{% load i18n video_tags thumbnail %}
{% spaceless %}
{% endif %}
{% if HIDE_TAGS == False %}
-
+ {% if TAGS|length > 0 %}
+
+ {% endif %}
{% endif %}
{% if HIDE_CURSUS == False %}