From e066063dbabf2b1f117a096b352b476511e0459c Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 26 Feb 2025 21:25:49 -0600 Subject: [PATCH 01/12] Initial structure added --- sde_collections/urls.py | 35 +++++ sde_collections/views.py | 121 ++++++++++++++++++ .../sde_collections/affected_urls.html | 0 3 files changed, 156 insertions(+) create mode 100644 sde_indexing_helper/templates/sde_collections/affected_urls.html diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 9ee77759..477abaf6 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -16,6 +16,21 @@ router.register(r"document-type-patterns", views.DocumentTypePatternViewSet) router.register(r"division-patterns", views.DivisionPatternViewSet) router.register(r"environmental-justice", EnvironmentalJusticeRowViewSet) +router.register( + r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, basename="include-pattern-affected-urls" +) +router.register( + r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, basename="exclude-pattern-affected-urls" +) +router.register( + r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" +) +router.register( + r"documenttype-pattern-affected-urls", + views.DocumentTypePatternAffectedURLsViewSet, + basename="documenttype-pattern-affected-urls", +) + app_name = "sde_collections" @@ -67,4 +82,24 @@ name="candidate-url-api", ), path("titles-and-errors/", views.TitlesAndErrorsView.as_view(), name="titles-and-errors-list"), + # path( + # "exclude-pattern//", + # view=views.ExcludePatternAffectedURLsListView.as_view(), + # name="affected_urls", + # ), + # path( + # "include-pattern//", + # view=views.IncludePatternAffectedURLsListView.as_view(), + # name="affected_urls", + # ), + # path( + # "title-pattern//", + # view=views.TitlePatternAffectedURLsListView.as_view(), + # name="affected_urls", + # ), + # path( + # "document-type-pattern//", + # view=views.DocumentTypePatternAffectedURLsListView.as_view(), + # name="affected_urls", + # ), ] diff --git a/sde_collections/views.py b/sde_collections/views.py index fb268170..69c09089 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -632,3 +632,124 @@ def get(self, request, *args, **kwargs): "resolved_title_errors": resolved_title_errors, } return render(request, "sde_collections/titles_and_errors_list.html", context) + + +# class BaseAffectedURLsListView(LoginRequiredMixin, ListView): +# """ +# Base view for displaying a list of URLs affected by a match pattern +# """ + +# model = DeltaUrl +# template_name = "sde_collections/affected_urls.html" +# context_object_name = "affected_urls" +# pattern_model = None +# pattern_type = None + +# def get_queryset(self): +# self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) +# queryset = self.pattern.matched_urls() +# return queryset + +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# context["pattern"] = self.pattern +# context["pattern_id"] = self.kwargs["id"] +# context["url_count"] = self.get_queryset().count() +# context["collection"] = self.pattern.collection +# context["pattern_type"] = self.pattern_type +# return context + + +# class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): +# pattern_model = ExcludePattern +# pattern_type = "Exclude" + +# def get_queryset(self): +# self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) +# queryset = self.pattern.matched_urls() + +# # Subquery to get the match_pattern and id of the IncludePattern +# include_pattern_subquery = IncludePattern.objects.filter(candidate_urls=models.OuterRef("pk")).values( +# "match_pattern", "id" +# )[:1] + +# # Annotate with inclusion status, match_pattern, and id of the IncludePattern +# queryset = queryset.annotate( +# included=models.Exists(include_pattern_subquery), +# included_by_pattern=models.Subquery( +# include_pattern_subquery.values("match_pattern"), output_field=models.CharField() +# ), +# match_pattern_id=models.Subquery(include_pattern_subquery.values("id"), +# output_field=models.IntegerField()), +# ) + +# return queryset + + +# class IncludePatternAffectedURLsListView(BaseAffectedURLsListView): +# pattern_model = IncludePattern +# pattern_type = "Include" + + +# class TitlePatternAffectedURLsListView(BaseAffectedURLsListView): +# pattern_model = TitlePattern +# pattern_type = "Title" + + +# class DocumentTypePatternAffectedURLsListView(BaseAffectedURLsListView): +# pattern_model = DocumentTypePattern +# pattern_type = "Document Type" + +# class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): +# queryset = CandidateURL.objects.all() +# serializer_class = AffectedURLSerializer +# pattern_model = None +# pattern_type = None + +# def get_queryset(self): +# pattern_id = self.request.GET.get("pattern_id") +# self.pattern = self.pattern_model.objects.get(id=pattern_id) +# queryset = self.pattern.matched_urls() +# return queryset + + +# class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): +# pattern_model = IncludePattern +# pattern_type = "Include" + + +# class ExcludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): +# pattern_model = ExcludePattern +# pattern_type = "Exclude" + +# def get_queryset(self): +# pattern_id = self.request.GET.get("pattern_id") +# self.pattern = self.pattern_model.objects.get(id=pattern_id) +# queryset = self.pattern.matched_urls() + +# # Subquery to get the match_pattern and id of the IncludePattern +# include_pattern_subquery = IncludePattern.objects.filter(candidate_urls=models.OuterRef("pk")).values( +# "match_pattern", "id" +# )[:1] + +# # Annotate with inclusion status, match_pattern, and id of the IncludePattern +# queryset = queryset.annotate( +# included=models.Exists(include_pattern_subquery), +# included_by_pattern=models.Subquery( +# include_pattern_subquery.values("match_pattern"), output_field=models.CharField() +# ), +# match_pattern_id=models.Subquery(include_pattern_subquery.values("id"), +# output_field=models.IntegerField()), +# ) + +# return queryset + + +# class TitlePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): +# pattern_model = TitlePattern +# pattern_type = "Title" + + +# class DocumentTypePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): +# pattern_model = DocumentTypePattern +# pattern_type = "Document Type" diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html new file mode 100644 index 00000000..e69de29b From edc46d5a40af9177b062cdf49caaf95f5137fdc8 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 27 Feb 2025 01:52:07 -0600 Subject: [PATCH 02/12] Add HTML/CSS/JS files for Affected URLs --- sde_collections/urls.py | 70 ++++++------ sde_collections/views.py | 89 +++++++--------- .../static/css/affected_urls.css | 0 .../static/js/affected_urls.js | 0 .../static/js/delta_url_list.js | 53 ++++++++++ .../sde_collections/affected_urls.html | 100 ++++++++++++++++++ 6 files changed, 225 insertions(+), 87 deletions(-) create mode 100644 sde_indexing_helper/static/css/affected_urls.css create mode 100644 sde_indexing_helper/static/js/affected_urls.js diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 477abaf6..80243501 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -16,20 +16,22 @@ router.register(r"document-type-patterns", views.DocumentTypePatternViewSet) router.register(r"division-patterns", views.DivisionPatternViewSet) router.register(r"environmental-justice", EnvironmentalJusticeRowViewSet) -router.register( - r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, basename="include-pattern-affected-urls" -) -router.register( - r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, basename="exclude-pattern-affected-urls" -) -router.register( - r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" -) -router.register( - r"documenttype-pattern-affected-urls", - views.DocumentTypePatternAffectedURLsViewSet, - basename="documenttype-pattern-affected-urls", -) +# router.register( +# r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, +# basename="include-pattern-affected-urls" +# ) +# router.register( +# r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, +# basename="exclude-pattern-affected-urls" +# ) +# router.register( +# r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" +# ) +# router.register( +# r"documenttype-pattern-affected-urls", +# views.DocumentTypePatternAffectedURLsViewSet, +# basename="documenttype-pattern-affected-urls", +# ) app_name = "sde_collections" @@ -82,24 +84,24 @@ name="candidate-url-api", ), path("titles-and-errors/", views.TitlesAndErrorsView.as_view(), name="titles-and-errors-list"), - # path( - # "exclude-pattern//", - # view=views.ExcludePatternAffectedURLsListView.as_view(), - # name="affected_urls", - # ), - # path( - # "include-pattern//", - # view=views.IncludePatternAffectedURLsListView.as_view(), - # name="affected_urls", - # ), - # path( - # "title-pattern//", - # view=views.TitlePatternAffectedURLsListView.as_view(), - # name="affected_urls", - # ), - # path( - # "document-type-pattern//", - # view=views.DocumentTypePatternAffectedURLsListView.as_view(), - # name="affected_urls", - # ), + path( + "exclude-pattern//-urls", + view=views.ExcludePatternAffectedURLsListView.as_view(), + name="exclude_pattern_urls", + ), + path( + "include-pattern//-urls", + view=views.IncludePatternAffectedURLsListView.as_view(), + name="include_pattern_urls", + ), + path( + "title-pattern//-urls", + view=views.TitlePatternAffectedURLsListView.as_view(), + name="title_pattern_urls", + ), + path( + "document-type-pattern//-urls", + view=views.DocumentTypePatternAffectedURLsListView.as_view(), + name="document_type_pattern_urls", + ), ] diff --git a/sde_collections/views.py b/sde_collections/views.py index 69c09089..383ee996 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -634,71 +634,54 @@ def get(self, request, *args, **kwargs): return render(request, "sde_collections/titles_and_errors_list.html", context) -# class BaseAffectedURLsListView(LoginRequiredMixin, ListView): -# """ -# Base view for displaying a list of URLs affected by a match pattern -# """ - -# model = DeltaUrl -# template_name = "sde_collections/affected_urls.html" -# context_object_name = "affected_urls" -# pattern_model = None -# pattern_type = None - -# def get_queryset(self): -# self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) -# queryset = self.pattern.matched_urls() -# return queryset - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) -# context["pattern"] = self.pattern -# context["pattern_id"] = self.kwargs["id"] -# context["url_count"] = self.get_queryset().count() -# context["collection"] = self.pattern.collection -# context["pattern_type"] = self.pattern_type -# return context +class BaseAffectedURLsListView(LoginRequiredMixin, ListView): + """ + Base view for displaying a list of URLs affected by a match pattern + """ + template_name = "sde_collections/affected_urls.html" + context_object_name = "affected_urls" + pattern_model = None + pattern_type = None -# class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): -# pattern_model = ExcludePattern -# pattern_type = "Exclude" + def get_queryset(self): + self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) + if self.kwargs["url_type"] == "delta": + queryset = self.pattern.get_matching_delta_urls() + elif self.kwargs["url_type"] == "curated": + queryset = self.pattern.get_matching_curated_urls() + return queryset -# def get_queryset(self): -# self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) -# queryset = self.pattern.matched_urls() + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["pattern"] = self.pattern + context["pattern_id"] = self.kwargs["id"] + context["url_count"] = self.get_queryset().count() + context["collection"] = self.pattern.collection + context["pattern_type"] = self.pattern_type + context["url_type"] = self.kwargs["url_type"] + return context -# # Subquery to get the match_pattern and id of the IncludePattern -# include_pattern_subquery = IncludePattern.objects.filter(candidate_urls=models.OuterRef("pk")).values( -# "match_pattern", "id" -# )[:1] -# # Annotate with inclusion status, match_pattern, and id of the IncludePattern -# queryset = queryset.annotate( -# included=models.Exists(include_pattern_subquery), -# included_by_pattern=models.Subquery( -# include_pattern_subquery.values("match_pattern"), output_field=models.CharField() -# ), -# match_pattern_id=models.Subquery(include_pattern_subquery.values("id"), -# output_field=models.IntegerField()), -# ) +class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): + pattern_model = DeltaExcludePattern + pattern_type = "Exclude" -# return queryset +class IncludePatternAffectedURLsListView(BaseAffectedURLsListView): + pattern_model = DeltaIncludePattern + pattern_type = "Include" -# class IncludePatternAffectedURLsListView(BaseAffectedURLsListView): -# pattern_model = IncludePattern -# pattern_type = "Include" +class TitlePatternAffectedURLsListView(BaseAffectedURLsListView): + pattern_model = DeltaTitlePattern + pattern_type = "Title" -# class TitlePatternAffectedURLsListView(BaseAffectedURLsListView): -# pattern_model = TitlePattern -# pattern_type = "Title" +class DocumentTypePatternAffectedURLsListView(BaseAffectedURLsListView): + pattern_model = DeltaDocumentTypePattern + pattern_type = "Document Type" -# class DocumentTypePatternAffectedURLsListView(BaseAffectedURLsListView): -# pattern_model = DocumentTypePattern -# pattern_type = "Document Type" # class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): # queryset = CandidateURL.objects.all() diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css new file mode 100644 index 00000000..e69de29b diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js new file mode 100644 index 00000000..e69de29b diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index 33e7850d..67891544 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -103,6 +103,19 @@ function modalContents(tableName) { }); } +function renderCountWithViewButton(count, buttonClass, rowId) { + return ` +
+ + ${count} + + +
+ `; +} + function initializeDataTable() { var true_icon = 'check'; var false_icon = 'close'; @@ -604,11 +617,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-exclude-pattern-delta-urls', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-exclude-pattern-curated-urls', row.id); + }, }, { data: null, @@ -690,11 +709,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-include-pattern-delta-urls', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-include-pattern-curated-urls', row.id); + }, }, { data: null, @@ -773,11 +798,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-title-pattern-delta-urls', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-title-pattern-curated-urls', row.id); + }, }, { data: null, @@ -856,11 +887,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-document-type-pattern-delta-urls', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'view-document-type-pattern-curated-urls', row.id); + }, }, { data: null, @@ -998,6 +1035,7 @@ function setupClickHandlers() { handleDivisionSelect(); handleExcludeIndividualUrlClick(); handleNewTitleChange(); + handleShowAffectedURLsListButtonClick(); handleUrlLinkClick(); handleTabsClick(); @@ -2246,3 +2284,18 @@ function handleReindexingStatusSelect() { }); }); } + +function handleShowAffectedURLsListButtonClick() { + const patterns = ['exclude', 'include', 'title', 'document-type']; + const urlTypes = ['delta', 'curated']; + + patterns.forEach(pattern => { + urlTypes.forEach(urlType => { + const buttonClass = `.view-${pattern}-pattern-${urlType}-urls`; + $("body").on("click", buttonClass, function() { + const matchPatternId = $(this).data("row-id"); + window.open(`/${pattern}-pattern/${matchPatternId}/${urlType}-urls`, '_blank'); + }); + }); + }); +} diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index e69de29b..8ffa6f96 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -0,0 +1,100 @@ +{% extends "layouts/base.html" %} +{% load static i18n %} +{% load humanize %} +{% block title %} +Affected URLs for {{pattern_type}} Pattern +{% endblock title %} + +{% block stylesheets %} + {{ block.super }} + + + + +{% endblock stylesheets %} + +{% block content %} + {% csrf_token %} +
+

{{url_type}} URLs

+
+ +
+ +

+ {{ url_count|intcomma }} affected URLs for {{ pattern_type | lower }} pattern: + {{ pattern.match_pattern }} +

+ +
+ + + + + + {% if pattern_type == "Exclude" %} + + {% endif %} + + + + + {% if pattern_type == "Exclude" %} + + {% endif %} + + +
URLInclude URL
+
+ +
    +
  • Create Include Pattern
  • +
+ + + +
+{% endblock content %} + +{% block javascripts %} + {{ block.super }} + + + + + + + + + + +{% endblock javascripts %} From 58feab81fd598783977368df878f2294dac47daa Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 27 Feb 2025 03:20:22 -0600 Subject: [PATCH 03/12] First demo working --- sde_collections/urls.py | 30 +- sde_collections/views.py | 67 ++- .../static/css/affected_urls.css | 275 +++++++++++++ .../static/js/affected_urls.js | 389 ++++++++++++++++++ .../sde_collections/affected_urls.html | 7 +- 5 files changed, 707 insertions(+), 61 deletions(-) diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 80243501..0518fd97 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -16,22 +16,20 @@ router.register(r"document-type-patterns", views.DocumentTypePatternViewSet) router.register(r"division-patterns", views.DivisionPatternViewSet) router.register(r"environmental-justice", EnvironmentalJusticeRowViewSet) -# router.register( -# r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, -# basename="include-pattern-affected-urls" -# ) -# router.register( -# r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, -# basename="exclude-pattern-affected-urls" -# ) -# router.register( -# r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" -# ) -# router.register( -# r"documenttype-pattern-affected-urls", -# views.DocumentTypePatternAffectedURLsViewSet, -# basename="documenttype-pattern-affected-urls", -# ) +router.register( + r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, basename="exclude-pattern-affected-urls" +) +router.register( + r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, basename="include-pattern-affected-urls" +) +router.register( + r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" +) +router.register( + r"documenttype-pattern-affected-urls", + views.DocumentTypePatternAffectedURLsViewSet, + basename="documenttype-pattern-affected-urls", +) app_name = "sde_collections" diff --git a/sde_collections/views.py b/sde_collections/views.py index 383ee996..24be9aba 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -683,56 +683,39 @@ class DocumentTypePatternAffectedURLsListView(BaseAffectedURLsListView): pattern_type = "Document Type" -# class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): -# queryset = CandidateURL.objects.all() -# serializer_class = AffectedURLSerializer -# pattern_model = None -# pattern_type = None +class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): -# def get_queryset(self): -# pattern_id = self.request.GET.get("pattern_id") -# self.pattern = self.pattern_model.objects.get(id=pattern_id) -# queryset = self.pattern.matched_urls() -# return queryset - - -# class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): -# pattern_model = IncludePattern -# pattern_type = "Include" + pattern_model = None + pattern_type = None + def get_serializer_class(self): + url_type = self.request.GET.get("url_type") + return DeltaURLSerializer if url_type == "delta" else CuratedURLSerializer -# class ExcludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): -# pattern_model = ExcludePattern -# pattern_type = "Exclude" + def get_queryset(self): + pattern_id = self.request.GET.get("pattern_id") + url_type = self.request.GET.get("url_type") + self.pattern = self.pattern_model.objects.get(id=pattern_id) + return ( + self.pattern.get_matching_delta_urls() if url_type == "delta" else self.pattern.get_matching_curated_urls() + ) -# def get_queryset(self): -# pattern_id = self.request.GET.get("pattern_id") -# self.pattern = self.pattern_model.objects.get(id=pattern_id) -# queryset = self.pattern.matched_urls() -# # Subquery to get the match_pattern and id of the IncludePattern -# include_pattern_subquery = IncludePattern.objects.filter(candidate_urls=models.OuterRef("pk")).values( -# "match_pattern", "id" -# )[:1] +class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaIncludePattern + pattern_type = "Include" -# # Annotate with inclusion status, match_pattern, and id of the IncludePattern -# queryset = queryset.annotate( -# included=models.Exists(include_pattern_subquery), -# included_by_pattern=models.Subquery( -# include_pattern_subquery.values("match_pattern"), output_field=models.CharField() -# ), -# match_pattern_id=models.Subquery(include_pattern_subquery.values("id"), -# output_field=models.IntegerField()), -# ) -# return queryset +class ExcludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaExcludePattern + pattern_type = "Exclude" -# class TitlePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): -# pattern_model = TitlePattern -# pattern_type = "Title" +class TitlePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaTitlePattern + pattern_type = "Title" -# class DocumentTypePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): -# pattern_model = DocumentTypePattern -# pattern_type = "Document Type" +class DocumentTypePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaDocumentTypePattern + pattern_type = "Document Type" diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index e69de29b..d6da61d3 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -0,0 +1,275 @@ +.dataTables_scrollHead, +.dataTables_scrollBody { + overflow: visible !important; +} + +.dataTables_wrapper .dataTables_paginate .paginate_button:hover { + background: none; + border: none; +} + +.dataTables_wrapper .dataTables_paginate .paginate_button { + padding: 0em; +} + +.dataTables_wrapper .dataTables_paginate .paginate_button { + padding: .5em; +} + +.paginate_input { + width: 15%; +} + +.table_filter_row_input { + width: 100%; +} + +.candidateUrlContainer { + background: #15232E; + padding: 40px 30px; + border-radius: 15px; +} + +.table tbody tr:nth-child(odd) { + background-color: #050E19 !important; +} + +.table tbody tr:nth-child(even) { + background-color: #3F4A58 !important; +} + +.candidateTitle { + font-size: 24px; + font-weight: 500; +} + +.custom-select, +.buttons-csv, +.customizeColumns, +.addPattern { + border-style: solid !important; + border-color: #A7BACD !important; + border-width: 1px !important; + color: #A7BACD !important; + border-radius: 5px !important; + padding: 11px 15px; +} + +.addPattern { + background-color: #0066CA !important; + border-color: #0066CA !important; + color: #fff !important; +} + +#match_pattern_input { + background: #3F4A58; + border-radius: 4px; +} + +.asterik { + color: #C3001A; +} + +.customizeColumns { + margin-left: 10px !important; +} + +.form-control:read-only { + background-image: none; +} + +.form-control { + color: white; +} + +.form-control:focus { + color: white; +} + +.dt-container div.dt-length label { + display: none; +} + +div.dt-container div.dt-info { + padding-top: 0; + white-space: normal; +} + +.page-link { + color: white !important; + border: 0.5px solid !important; + margin-left: 3px; + margin-right: 3px; +} + +.page-link:hover { + background-color: #0066CA !important; +} + +.page-item.disabled .page-link { + color: grey !important; +} + +.dt-paging-input { + color: white; +} + +.dt-paging-input input { + background-color: #3F4A58; + color: white; + border: solid 0.5px !important; +} + +.dt-inputpaging { + position: absolute; + right: 16px; + top: -27px; +} + +.ml-auto { + width: 50%; +} + +.custom-select-sm { + margin-left: 5px; +} + +.selected { + background-color: inherit !important; +} + +.headerDiv { + display: flex; + justify-content: space-between; +} + +.url-cell { + display: flex; + align-items: center; + justify-content: space-between; + word-wrap: break-word; + word-break: break-all; + white-space: normal; + overflow-wrap: break-word; + min-width: 100%; + max-width: 100%; +} + +.url-icon { + color: #65B1EF; +} + +/* pagination position */ +div.dt-container div.dt-paging ul.pagination { + position: absolute; + right: 60px; +} + +/* Dark theme adjustments later added */ +body { + background-color: #2c2c2c; + color: #f5f5f5; +} + +.pageTitle { + color: #f5f5f5; +} + +.table { + background-color: #1e1e1e; + color: #f5f5f5; +} + +.table thead th { + background-color: #444; + color: #f5f5f5; +} + +.table tbody tr:hover { + background-color: #333; +} + +.table tbody tr td a { + color: #61dafb; + /* URL links */ +} + +.table tbody tr td a:hover { + color: #f5f5f5; + /* Hover effect for links */ +} + +/* Optional styles for the Include URL button */ +.include-url-btn { + background-color: transparent; + border: none; + font-size: 2rem; + cursor: pointer; +} + +.modalTitle { + font-size: 24px; + font-weight: 600; + line-height: 36px; + letter-spacing: -0.03em; +} + +#hideShowColumnsModal { + position: fixed; + top: 0; + right: 0 !important; + left: unset !important; + background: #FFFFFF; + width: 30vw; + z-index: 2000; +} + +.modalFooter { + position: sticky; + bottom: 0; + position: sticky; + bottom: 0; + padding: 10px 0; + background: #FFFFFF; +} + +.modal-body .bmd-label-static { + top: -20px !important; +} + +.modal-header { + margin-bottom: 40px; +} + +.row .col-md-auto { + display: flex; + align-items: center; + justify-content: space-between; +} + +.dt-buttons { + margin-left: auto; +} + +.custom-menu { + display: none; + z-index: 1000; + position: absolute; + overflow: hidden; + border: 1px solid #CCC; + white-space: nowrap; + font-family: sans-serif; + background: #FFF; + color: white; + border-radius: 5px; + background-color: #15232E; +} + +.custom-menu li { + padding: 8px 12px; + cursor: pointer; +} + +.custom-menu li:hover { + background-color: #0066CA; +} diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index e69de29b..2c1a7875 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -0,0 +1,389 @@ +var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); +var INDIVIDUAL_URL = 1; +var MULTI_URL_PATTERN = 2; +var selected_text = ""; + +$(document).ready(function () { + handleAjaxStartAndStop(); + initializeDataTable(); + // setupClickHandlers(); +}); + +function handleAjaxStartAndStop() { + $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI); +} + +function initializeDataTable() { + const PATTERN_ENDPOINTS = { + Exclude: "exclude-pattern-affected-urls", + Include: "include-pattern-affected-urls", + Title: "title-pattern-affected-urls", + "Document Type": "documenttype-pattern-affected-urls", + }; + + var affected_urls_table = $("#affectedURLsTable").DataTable({ + processing: true, + pageLength: 100, + colReorder: true, + stateSave: true, + serverSide: true, + orderCellsTop: true, + pagingType: "input", + paging: true, + rowId: "url", + layout: { + bottomEnd: "inputPaging", + topEnd: null, + topStart: { + info: true, + pageLength: { + menu: [ + [25, 50, 100, 500], + ["Show 25", "Show 50", "Show 100", "Show 500"], + ], + }, + buttons: [], + }, + }, + columnDefs: [ + { orderable: true, targets: "_all" }, + { orderable: false, targets: "filter-row" }, + ], + orderCellsTop: true, + ajax: { + url: `/api/${PATTERN_ENDPOINTS[patternType]}/?format=datatables&url_type=${urlType}&pattern_id=${pattern_id}`, + data: function (d) {}, + complete: function (xhr, status) {}, + }, + + columns: [ + getURLColumn(), + // ...getConditionalColumns(patternType), + { data: "id", visible: false, searchable: false }, + ], + }); + + $("#affectedURLsFilter").on( + "beforeinput", + DataTable.util.debounce(function (val) { + affected_urls_table.columns(0).search(this.value).draw(); + }, 1000) + ); +} + +function getURLColumn() { + return { + data: "url", + width: "30%", + render: function (data, type, row) { + return `
${data} + + open_in_new +
`; + }, + }; +} + +// function getIncludeURLColumn() { +// return { +// data: "included", +// width: "30%", +// render: function (data, type, row) { +// return ` +// ${ +// data +// ? 'check' +// : 'close' +// } +// `; +// }, +// class: "col-3 text-center", +// }; +// } + +// function getConditionalColumns(patternType) { +// // add these columns if patternType is "Exclude" +// if (patternType === "Exclude") { +// return [ +// getIncludeURLColumn(), +// { data: "included_by_pattern", visible: false, searchable: false }, +// { data: "match_pattern_id", visible: false, searchable: false }, +// { data: "excluded", visible: false, searchable: false }, +// ]; +// } +// return []; +// } + +// function setupClickHandlers() { +// handleHideorShowSubmitButton(); +// handleHideorShowKeypress(); +// handleIncludeIndividualUrlClick(); +// } + +// function handleIncludeIndividualUrlClick() { +// $("#affectedURLsTable").on("click", ".include-url-btn", function () { +// const inclusion_status = this.querySelector("i"); +// if (inclusion_status.classList.contains("cross-mark")) { +// match_pattern = remove_protocol($(this).attr("value")); +// match_pattern_type = INDIVIDUAL_URL; + +// postIncludePatterns(match_pattern, match_pattern_type) +// .then((result) => { +// // refresh the table after a pattern is added +// $("#affectedURLsTable").DataTable().ajax.reload(null, false); +// }) +// .catch((error) => { +// toastr.error("Error:", error); +// }); +// } else { +// var url = $(this).attr("value"); +// var included_by_pattern = $(this).attr("included_by_pattern"); +// var match_pattern_id = $(this).attr("match_pattern_id"); + +// if (remove_protocol(included_by_pattern) === remove_protocol(url)) { +// currentURLtoDelete = `/api/include-patterns/${match_pattern_id}/`; +// deletePattern(currentURLtoDelete, (data_type = "Include Pattern")); +// toastr.success("URL excluded successfully"); +// } else { +// toastr.error( +// "This URL is affected by a multi-URL include pattern: " + +// included_by_pattern +// ); +// } +// } +// }); +// } + +// function postIncludePatterns(match_pattern, match_pattern_type = 0) { +// return new Promise((resolve, reject) => { +// $.ajax({ +// url: "/api/include-patterns/", +// type: "POST", +// data: { +// collection: collection_id, +// match_pattern: match_pattern, +// match_pattern_type: match_pattern_type, +// csrfmiddlewaretoken: csrftoken, +// }, +// success: function (data) { +// toastr.success("Added to include patterns successfully"); +// resolve({ +// id: data.id, +// match_pattern: data.match_pattern, +// }); +// }, +// error: function (xhr, status, error) { +// var errorMessage = xhr.responseText; +// toastr.error(errorMessage); +// reject(error); +// }, +// }); +// }); +// } + +// function remove_protocol(url) { +// return url.replace(/(^\w+:|^)\/\//, ""); +// } + +// function deletePattern( +// url, +// data_type, +// url_type = null, +// candidate_urls_count = null +// ) { +// return new Promise((resolve, reject) => { +// $.ajax({ +// url: url, +// type: "DELETE", +// data: { +// csrfmiddlewaretoken: csrftoken, +// }, +// headers: { +// "X-CSRFToken": csrftoken, +// }, +// success: function (data) { +// // refresh the table after a pattern is deleted +// $("#affectedURLsTable").DataTable().ajax.reload(null, false); +// }, +// error: function (xhr, status, error) { +// var errorMessage = xhr.responseText; +// toastr.error(errorMessage); +// }, +// }); +// }); +// } + +// function handleHideorShowKeypress() { +// $("body").on("keydown", function () { +// //Close modal via escape +// if (event.key == "Escape" && $("#hideShowColumnsModal").is(":visible")) { +// $("#hideShowColumnsModal").modal("hide"); +// } +// //Confirm modal selections via enter +// if (event.key == "Enter" && $("#hideShowColumnsModal").is(":visible")) { +// var table = $(uniqueId).DataTable(); +// $("[id^='checkbox_']").each(function () { +// var checkboxValue = $(this).val(); +// let column = table.column(checkboxValue); +// var isChecked = $(this).is(":checked"); +// if (column.visible() === false && isChecked) column.visible(true); +// else if (column.visible() === true && !isChecked) column.visible(false); +// }); +// $("#hideShowColumnsModal").modal("hide"); +// } +// }); + +// $("body").on("click", ".modal-backdrop", function () { +// $("#hideShowColumnsModal").modal("hide"); +// }); + +// //adding each modals keypress functionalities +// addEnterEscapeKeypress("#includePatternModal", "#include_pattern_form"); +// } + +// //template to add enter and escape functionalities to add pattern modals +// function addEnterEscapeKeypress(modalID, formID) { +// $("body").on("keydown", function (event) { +// let modal = $(modalID); +// let form = $(formID); +// if (event.key == "Escape" && modal.is(":visible")) { +// modal.modal("hide"); +// } +// if (event.key == "Enter" && modal.is(":visible")) { +// form.submit(); +// modal.modal("hide"); +// } +// }); +// } + +// function handleHideorShowSubmitButton() { +// $("body").on("click", "#hideShowSubmitButton", function () { +// var table = $(uniqueId).DataTable(); +// $("[id^='checkbox_']").each(function () { +// var checkboxValue = $(this).val(); +// let column = table.column(checkboxValue); +// var isChecked = $(this).is(":checked"); +// if (column.visible() === false && isChecked) column.visible(true); +// else if (column.visible() === true && !isChecked) column.visible(false); +// }); + +// $("#hideShowColumnsModal").modal("hide"); +// }); +// } + +// $("#include_pattern_form").on("submit", function (e) { +// e.preventDefault(); + +// // check if pattern already exists +// input_serialized = $(this).serializeArray(); +// $.ajax({ +// url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, +// type: "GET", +// success: function (response) { +// var existingPatterns = response.data.map((item) => item.match_pattern); +// if (existingPatterns.includes(input_serialized[0].value)) { +// toastr.warning("Pattern already exists"); +// $("#includePatternModal").modal("hide"); +// return; +// } else { +// // if pattern does not exist, create a new pattern +// inputs = {}; +// input_serialized.forEach((field) => { +// inputs[field.name] = field.value; +// }); + +// postIncludePatterns( +// (match_pattern = inputs.match_pattern), +// (match_pattern_type = 2) +// ) +// .then(() => { +// // Reload the DataTable after the successful postIncludePatterns call +// $("#affectedURLsTable").DataTable().ajax.reload(null, false); +// }) +// .catch((error) => { +// toastr.error("Error posting include patterns:", error); +// }); +// } +// }, +// error: function (xhr, status, error) { +// toastr.error("An error occurred while checking existing patterns"); +// }, +// }); + +// // close the modal if it is open +// $("#includePatternModal").modal("hide"); +// }); + +// // Trigger action when the contexmenu is about to be shown +// $("body").on("contextmenu", ".candidate_url", function (event) { +// // Avoid the real one +// event.preventDefault(); + +// // Show contextmenu +// $(".custom-menu") +// .finish() +// .toggle(100) +// // In the right position (the mouse) +// .css({ +// top: event.pageY + "px", +// left: event.pageX - 80 + "px", +// }); +// }); + +// // If the document is clicked somewhere +// $(document).bind("mousedown", function (e) { +// selected_text = get_selection(); + +// // If the clicked element is not the menu +// if (!$(e.target).parents(".custom-menu").length > 0) { +// // Hide it +// $(".custom-menu").hide(100); +// } +// }); + +// function get_selection() { +// var text = ""; +// if (window.getSelection) { +// text = window.getSelection().toString(); +// } else if (document.selection && document.selection.type != "Control") { +// text = document.selection.createRange().text; +// } + +// return text; +// } + +// // If the menu element is clicked +// $(".custom-menu li").click(function () { +// // Check if the include pattern already exists +// $.ajax({ +// url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, +// type: "GET", +// success: function (response) { +// var existingPatterns = response.data.map((item) => item.match_pattern); +// if (existingPatterns.includes(selected_text.trim())) { +// toastr.warning("Pattern already exists"); +// $(".custom-menu").hide(100); +// return; +// } else { +// postIncludePatterns( +// remove_protocol(selected_text.trim()), +// (match_pattern_type = MULTI_URL_PATTERN) +// ) +// .then(() => { +// $("#affectedURLsTable").DataTable().ajax.reload(null, false); +// }) +// .catch((error) => { +// toastr.error("Error posting include patterns:", error); +// }); +// $(".custom-menu").hide(100); +// } +// }, +// error: function (xhr, status, error) { +// toastr.error("An error occurred while checking existing patterns"); +// }, +// }); +// }); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 8ffa6f96..eae240e3 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -2,7 +2,7 @@ {% load static i18n %} {% load humanize %} {% block title %} -Affected URLs for {{pattern_type}} Pattern +Affected {{url_type | capfirst}} URLs for {{pattern_type}} Pattern {% endblock title %} {% block stylesheets %} @@ -16,13 +16,13 @@ {% block content %} {% csrf_token %}
-

{{url_type}} URLs

+

{{url_type | capfirst}} URLs

- {{ url_count|intcomma }} affected URLs for {{ pattern_type | lower }} pattern: + {{ url_count|intcomma }} affected URL{{ url_count|pluralize }} for {{ pattern_type | lower }} pattern: {{ pattern.match_pattern }}

@@ -86,6 +86,7 @@ From 6f10ef04680ba3f2abe21bb9697286bab56105ac Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 27 Feb 2025 14:23:00 -0600 Subject: [PATCH 04/12] Add ID column --- sde_collections/views.py | 10 +++++----- sde_indexing_helper/static/js/affected_urls.js | 3 ++- .../templates/sde_collections/affected_urls.html | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sde_collections/views.py b/sde_collections/views.py index 24be9aba..407ab1ad 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -701,16 +701,16 @@ def get_queryset(self): ) -class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): - pattern_model = DeltaIncludePattern - pattern_type = "Include" - - class ExcludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): pattern_model = DeltaExcludePattern pattern_type = "Exclude" +class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaIncludePattern + pattern_type = "Include" + + class TitlePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): pattern_model = DeltaTitlePattern pattern_type = "Title" diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 2c1a7875..22eda8d3 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -57,9 +57,10 @@ function initializeDataTable() { }, columns: [ + { data: "id", class: "whiteText text-center" }, getURLColumn(), // ...getConditionalColumns(patternType), - { data: "id", visible: false, searchable: false }, + // { data: "id", visible: false, searchable: false }, ], }); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index eae240e3..19a9e267 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -30,18 +30,18 @@

- + - {% if pattern_type == "Exclude" %} + - + - {% if pattern_type == "Exclude" %} +
ID URL
From 68e3c953c6738d7a17739789c27e3476a7da9398 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 27 Feb 2025 14:37:54 -0600 Subject: [PATCH 05/12] Remove unnecessary elements --- .../static/js/affected_urls.js | 308 ------------------ .../sde_collections/affected_urls.html | 37 --- 2 files changed, 345 deletions(-) diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 22eda8d3..9cd74101 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -1,12 +1,10 @@ var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); var INDIVIDUAL_URL = 1; var MULTI_URL_PATTERN = 2; -var selected_text = ""; $(document).ready(function () { handleAjaxStartAndStop(); initializeDataTable(); - // setupClickHandlers(); }); function handleAjaxStartAndStop() { @@ -59,8 +57,6 @@ function initializeDataTable() { columns: [ { data: "id", class: "whiteText text-center" }, getURLColumn(), - // ...getConditionalColumns(patternType), - // { data: "id", visible: false, searchable: false }, ], }); @@ -84,307 +80,3 @@ function getURLColumn() { }, }; } - -// function getIncludeURLColumn() { -// return { -// data: "included", -// width: "30%", -// render: function (data, type, row) { -// return ` -// ${ -// data -// ? 'check' -// : 'close' -// } -// `; -// }, -// class: "col-3 text-center", -// }; -// } - -// function getConditionalColumns(patternType) { -// // add these columns if patternType is "Exclude" -// if (patternType === "Exclude") { -// return [ -// getIncludeURLColumn(), -// { data: "included_by_pattern", visible: false, searchable: false }, -// { data: "match_pattern_id", visible: false, searchable: false }, -// { data: "excluded", visible: false, searchable: false }, -// ]; -// } -// return []; -// } - -// function setupClickHandlers() { -// handleHideorShowSubmitButton(); -// handleHideorShowKeypress(); -// handleIncludeIndividualUrlClick(); -// } - -// function handleIncludeIndividualUrlClick() { -// $("#affectedURLsTable").on("click", ".include-url-btn", function () { -// const inclusion_status = this.querySelector("i"); -// if (inclusion_status.classList.contains("cross-mark")) { -// match_pattern = remove_protocol($(this).attr("value")); -// match_pattern_type = INDIVIDUAL_URL; - -// postIncludePatterns(match_pattern, match_pattern_type) -// .then((result) => { -// // refresh the table after a pattern is added -// $("#affectedURLsTable").DataTable().ajax.reload(null, false); -// }) -// .catch((error) => { -// toastr.error("Error:", error); -// }); -// } else { -// var url = $(this).attr("value"); -// var included_by_pattern = $(this).attr("included_by_pattern"); -// var match_pattern_id = $(this).attr("match_pattern_id"); - -// if (remove_protocol(included_by_pattern) === remove_protocol(url)) { -// currentURLtoDelete = `/api/include-patterns/${match_pattern_id}/`; -// deletePattern(currentURLtoDelete, (data_type = "Include Pattern")); -// toastr.success("URL excluded successfully"); -// } else { -// toastr.error( -// "This URL is affected by a multi-URL include pattern: " + -// included_by_pattern -// ); -// } -// } -// }); -// } - -// function postIncludePatterns(match_pattern, match_pattern_type = 0) { -// return new Promise((resolve, reject) => { -// $.ajax({ -// url: "/api/include-patterns/", -// type: "POST", -// data: { -// collection: collection_id, -// match_pattern: match_pattern, -// match_pattern_type: match_pattern_type, -// csrfmiddlewaretoken: csrftoken, -// }, -// success: function (data) { -// toastr.success("Added to include patterns successfully"); -// resolve({ -// id: data.id, -// match_pattern: data.match_pattern, -// }); -// }, -// error: function (xhr, status, error) { -// var errorMessage = xhr.responseText; -// toastr.error(errorMessage); -// reject(error); -// }, -// }); -// }); -// } - -// function remove_protocol(url) { -// return url.replace(/(^\w+:|^)\/\//, ""); -// } - -// function deletePattern( -// url, -// data_type, -// url_type = null, -// candidate_urls_count = null -// ) { -// return new Promise((resolve, reject) => { -// $.ajax({ -// url: url, -// type: "DELETE", -// data: { -// csrfmiddlewaretoken: csrftoken, -// }, -// headers: { -// "X-CSRFToken": csrftoken, -// }, -// success: function (data) { -// // refresh the table after a pattern is deleted -// $("#affectedURLsTable").DataTable().ajax.reload(null, false); -// }, -// error: function (xhr, status, error) { -// var errorMessage = xhr.responseText; -// toastr.error(errorMessage); -// }, -// }); -// }); -// } - -// function handleHideorShowKeypress() { -// $("body").on("keydown", function () { -// //Close modal via escape -// if (event.key == "Escape" && $("#hideShowColumnsModal").is(":visible")) { -// $("#hideShowColumnsModal").modal("hide"); -// } -// //Confirm modal selections via enter -// if (event.key == "Enter" && $("#hideShowColumnsModal").is(":visible")) { -// var table = $(uniqueId).DataTable(); -// $("[id^='checkbox_']").each(function () { -// var checkboxValue = $(this).val(); -// let column = table.column(checkboxValue); -// var isChecked = $(this).is(":checked"); -// if (column.visible() === false && isChecked) column.visible(true); -// else if (column.visible() === true && !isChecked) column.visible(false); -// }); -// $("#hideShowColumnsModal").modal("hide"); -// } -// }); - -// $("body").on("click", ".modal-backdrop", function () { -// $("#hideShowColumnsModal").modal("hide"); -// }); - -// //adding each modals keypress functionalities -// addEnterEscapeKeypress("#includePatternModal", "#include_pattern_form"); -// } - -// //template to add enter and escape functionalities to add pattern modals -// function addEnterEscapeKeypress(modalID, formID) { -// $("body").on("keydown", function (event) { -// let modal = $(modalID); -// let form = $(formID); -// if (event.key == "Escape" && modal.is(":visible")) { -// modal.modal("hide"); -// } -// if (event.key == "Enter" && modal.is(":visible")) { -// form.submit(); -// modal.modal("hide"); -// } -// }); -// } - -// function handleHideorShowSubmitButton() { -// $("body").on("click", "#hideShowSubmitButton", function () { -// var table = $(uniqueId).DataTable(); -// $("[id^='checkbox_']").each(function () { -// var checkboxValue = $(this).val(); -// let column = table.column(checkboxValue); -// var isChecked = $(this).is(":checked"); -// if (column.visible() === false && isChecked) column.visible(true); -// else if (column.visible() === true && !isChecked) column.visible(false); -// }); - -// $("#hideShowColumnsModal").modal("hide"); -// }); -// } - -// $("#include_pattern_form").on("submit", function (e) { -// e.preventDefault(); - -// // check if pattern already exists -// input_serialized = $(this).serializeArray(); -// $.ajax({ -// url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, -// type: "GET", -// success: function (response) { -// var existingPatterns = response.data.map((item) => item.match_pattern); -// if (existingPatterns.includes(input_serialized[0].value)) { -// toastr.warning("Pattern already exists"); -// $("#includePatternModal").modal("hide"); -// return; -// } else { -// // if pattern does not exist, create a new pattern -// inputs = {}; -// input_serialized.forEach((field) => { -// inputs[field.name] = field.value; -// }); - -// postIncludePatterns( -// (match_pattern = inputs.match_pattern), -// (match_pattern_type = 2) -// ) -// .then(() => { -// // Reload the DataTable after the successful postIncludePatterns call -// $("#affectedURLsTable").DataTable().ajax.reload(null, false); -// }) -// .catch((error) => { -// toastr.error("Error posting include patterns:", error); -// }); -// } -// }, -// error: function (xhr, status, error) { -// toastr.error("An error occurred while checking existing patterns"); -// }, -// }); - -// // close the modal if it is open -// $("#includePatternModal").modal("hide"); -// }); - -// // Trigger action when the contexmenu is about to be shown -// $("body").on("contextmenu", ".candidate_url", function (event) { -// // Avoid the real one -// event.preventDefault(); - -// // Show contextmenu -// $(".custom-menu") -// .finish() -// .toggle(100) -// // In the right position (the mouse) -// .css({ -// top: event.pageY + "px", -// left: event.pageX - 80 + "px", -// }); -// }); - -// // If the document is clicked somewhere -// $(document).bind("mousedown", function (e) { -// selected_text = get_selection(); - -// // If the clicked element is not the menu -// if (!$(e.target).parents(".custom-menu").length > 0) { -// // Hide it -// $(".custom-menu").hide(100); -// } -// }); - -// function get_selection() { -// var text = ""; -// if (window.getSelection) { -// text = window.getSelection().toString(); -// } else if (document.selection && document.selection.type != "Control") { -// text = document.selection.createRange().text; -// } - -// return text; -// } - -// // If the menu element is clicked -// $(".custom-menu li").click(function () { -// // Check if the include pattern already exists -// $.ajax({ -// url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, -// type: "GET", -// success: function (response) { -// var existingPatterns = response.data.map((item) => item.match_pattern); -// if (existingPatterns.includes(selected_text.trim())) { -// toastr.warning("Pattern already exists"); -// $(".custom-menu").hide(100); -// return; -// } else { -// postIncludePatterns( -// remove_protocol(selected_text.trim()), -// (match_pattern_type = MULTI_URL_PATTERN) -// ) -// .then(() => { -// $("#affectedURLsTable").DataTable().ajax.reload(null, false); -// }) -// .catch((error) => { -// toastr.error("Error posting include patterns:", error); -// }); -// $(".custom-menu").hide(100); -// } -// }, -// error: function (xhr, status, error) { -// toastr.error("An error occurred while checking existing patterns"); -// }, -// }); -// }); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 19a9e267..a6545315 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -32,52 +32,15 @@

ID URL - -

-
    -
  • Create Include Pattern
  • -
- - - {% endblock content %} From c7d350bbac1d18352f100b3a103b82baac13082c Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 27 Feb 2025 15:44:46 -0600 Subject: [PATCH 06/12] Add Changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a4f5035..14ab69f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,3 +65,12 @@ For each PR made, an entry should be added to this changelog. It should contain - Added universal search functionality tests - Created search pane filter tests - Added pattern application form tests with validation checks + +- affected-urls-page + - Description: Added functionality to view affected URLs (both Delta and Curated URLs) for each pattern type (Include, Exclude, Title, Document Type). + - Changes: + - Created new URL patterns with dynamic URL type handling (`///`) + - Added `BaseAffectedURLsListView` and pattern-specific views for handling URL displays + - Implemented `BaseAffectedURLsViewSet` and pattern-specific views for API endpoints + - Added `affected_urls.html`, `affected_urls.css` and`affected_urls.js` + - Enhanced pattern list views with clickable eye icons From 93c09aa52a399fb7c72e0e4efa02d43a86167191 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Tue, 4 Mar 2025 15:24:43 -0600 Subject: [PATCH 07/12] Create popup elements --- .../static/css/delta_url_list.css | 20 +++ .../static/js/delta_url_list.js | 138 +++++++++++++++--- .../sde_collections/delta_urls_list.html | 32 ++++ 3 files changed, 169 insertions(+), 21 deletions(-) diff --git a/sde_indexing_helper/static/css/delta_url_list.css b/sde_indexing_helper/static/css/delta_url_list.css index 06689207..4fc79ddb 100644 --- a/sde_indexing_helper/static/css/delta_url_list.css +++ b/sde_indexing_helper/static/css/delta_url_list.css @@ -451,3 +451,23 @@ div.dt-container div.dt-paging ul.pagination { max-width: 100%; min-width: 100%; } + +/* Specific styling for affected URLs modal only */ +#affectedURLsModal .modal-dialog { + max-width: 90%; + margin: 1.75rem auto; +} + +#affectedURLsModal .modal-content { + background-color: #15232E; + color: white; + width: 100%; +} + +#affectedURLsModal .modal-header { + border-bottom: 1px solid #3F4A58; +} + +#affectedURLsModal .close { + color: white; +} diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index 67891544..8f19f574 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -103,13 +103,17 @@ function modalContents(tableName) { }); } -function renderCountWithViewButton(count, buttonClass, rowId) { +function renderCountWithViewButton(count, patternType, urlType, rowId) { return `
${count} -
@@ -618,7 +622,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-exclude-pattern-delta-urls', row.id); + return renderCountWithViewButton(data, 'exclude', 'delta', row.id); }, }, { @@ -626,7 +630,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-exclude-pattern-curated-urls', row.id); + return renderCountWithViewButton(data, 'exclude', 'curated', row.id); }, }, { @@ -710,7 +714,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-include-pattern-delta-urls', row.id); + return renderCountWithViewButton(data, 'include', 'delta', row.id); }, }, { @@ -718,7 +722,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-include-pattern-curated-urls', row.id); + return renderCountWithViewButton(data, 'include', 'curated', row.id); }, }, { @@ -799,7 +803,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-title-pattern-delta-urls', row.id); + return renderCountWithViewButton(data, 'title', 'delta', row.id); }, }, { @@ -807,7 +811,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-title-pattern-curated-urls', row.id); + return renderCountWithViewButton(data, 'title', 'curated', row.id); }, }, { @@ -888,7 +892,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-document-type-pattern-delta-urls', row.id); + return renderCountWithViewButton(data, 'document-type', 'delta', row.id); }, }, { @@ -896,7 +900,7 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return renderCountWithViewButton(data, 'view-document-type-pattern-curated-urls', row.id); + return renderCountWithViewButton(data, 'document-type', 'curated', row.id); }, }, { @@ -2285,17 +2289,109 @@ function handleReindexingStatusSelect() { }); } +// function handleShowAffectedURLsListButtonClick() { +// const patterns = ['exclude', 'include', 'title', 'document-type']; +// const urlTypes = ['delta', 'curated']; + +// patterns.forEach(pattern => { +// urlTypes.forEach(urlType => { +// const buttonClass = `.view-${pattern}-pattern-${urlType}-urls`; +// $("body").on("click", buttonClass, function() { +// const matchPatternId = $(this).data("row-id"); +// window.open(`/${pattern}-pattern/${matchPatternId}/${urlType}-urls`, '_blank'); +// }); +// }); +// }); +// } + function handleShowAffectedURLsListButtonClick() { - const patterns = ['exclude', 'include', 'title', 'document-type']; - const urlTypes = ['delta', 'curated']; - - patterns.forEach(pattern => { - urlTypes.forEach(urlType => { - const buttonClass = `.view-${pattern}-pattern-${urlType}-urls`; - $("body").on("click", buttonClass, function() { - const matchPatternId = $(this).data("row-id"); - window.open(`/${pattern}-pattern/${matchPatternId}/${urlType}-urls`, '_blank'); - }); - }); + const PATTERN_ENDPOINTS = { + 'exclude': 'exclude-pattern-affected-urls', + 'include': 'include-pattern-affected-urls', + 'title': 'title-pattern-affected-urls', + 'document-type': 'documenttype-pattern-affected-urls' + }; + + $("body").on("click", ".view-pattern-urls", function() { + const matchPatternId = $(this).data("row-id"); + const patternType = $(this).data("pattern-type"); + const urlType = $(this).data("url-type"); + + // Update modal title and pattern info + $("#affectedURLsModalTitle").text(`Affected ${urlType} URLs`); + $("#patternInfo").text(`${urlType} URLs for ${patternType} pattern:`); // ${pattern.match_pattern}`); + + // Always destroy existing table and create new one + if ($.fn.DataTable.isDataTable('#affectedURLsModalTable')) { + $('#affectedURLsModalTable').DataTable().destroy(); + } + + initializeModalDataTable(PATTERN_ENDPOINTS[patternType], matchPatternId, urlType); + $("#affectedURLsModal").modal('show'); + }); } + +function initializeModalDataTable(endpoint, patternId, urlType) { + + affected_urls_table = $("#affectedURLsModalTable").DataTable({ + processing: true, + pageLength: 100, + colReorder: true, + stateSave: true, + serverSide: true, + orderCellsTop: true, + pagingType: "input", + paging: true, + rowId: "url", + layout: { + bottomEnd: "inputPaging", + topEnd: null, + topStart: { + info: true, + pageLength: { + menu: [ + [25, 50, 100, 500], + ["Show 25", "Show 50", "Show 100", "Show 500"], + ], + }, + buttons: [], + }, + }, + columnDefs: [ + { orderable: true, targets: "_all" }, + { orderable: false, targets: "filter-row" }, + ], + orderCellsTop: true, + ajax: { + url: `/api/${endpoint}/?format=datatables&url_type=${urlType}&pattern_id=${patternId}`, + data: function (d) {}, + complete: function (xhr, status) {}, + }, + + columns: [ + { data: "id", class: "whiteText text-center" }, + getURLColumn(), + ], + }); + + $("#affectedURLsFilter").on( + "beforeinput", + DataTable.util.debounce(function (val) { + affected_urls_table.columns(0).search(this.value).draw(); + }, 1000) + ); +} + +function getURLColumn() { + return { + data: "url", + width: "30%", + render: function (data, type, row) { + return `
${data} + + open_in_new +
`; + }, + }; +} diff --git a/sde_indexing_helper/templates/sde_collections/delta_urls_list.html b/sde_indexing_helper/templates/sde_collections/delta_urls_list.html index e17317a3..2266a85e 100644 --- a/sde_indexing_helper/templates/sde_collections/delta_urls_list.html +++ b/sde_indexing_helper/templates/sde_collections/delta_urls_list.html @@ -762,6 +762,38 @@ + + {% endblock content %} {% block javascripts %} From 295f8e944c20a0c548f91b9684bb647efc1025d2 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 5 Mar 2025 22:36:37 -0600 Subject: [PATCH 08/12] Cleanup unnecessary files --- sde_collections/views.py | 49 ---- .../static/css/affected_urls.css | 275 ------------------ .../static/css/delta_url_list.css | 21 ++ .../static/js/affected_urls.js | 82 ------ .../static/js/delta_url_list.js | 30 +- .../sde_collections/affected_urls.html | 64 ---- .../sde_collections/delta_urls_list.html | 30 +- 7 files changed, 42 insertions(+), 509 deletions(-) delete mode 100644 sde_indexing_helper/static/css/affected_urls.css delete mode 100644 sde_indexing_helper/static/js/affected_urls.js delete mode 100644 sde_indexing_helper/templates/sde_collections/affected_urls.html diff --git a/sde_collections/views.py b/sde_collections/views.py index 407ab1ad..91b67888 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -634,55 +634,6 @@ def get(self, request, *args, **kwargs): return render(request, "sde_collections/titles_and_errors_list.html", context) -class BaseAffectedURLsListView(LoginRequiredMixin, ListView): - """ - Base view for displaying a list of URLs affected by a match pattern - """ - - template_name = "sde_collections/affected_urls.html" - context_object_name = "affected_urls" - pattern_model = None - pattern_type = None - - def get_queryset(self): - self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) - if self.kwargs["url_type"] == "delta": - queryset = self.pattern.get_matching_delta_urls() - elif self.kwargs["url_type"] == "curated": - queryset = self.pattern.get_matching_curated_urls() - return queryset - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["pattern"] = self.pattern - context["pattern_id"] = self.kwargs["id"] - context["url_count"] = self.get_queryset().count() - context["collection"] = self.pattern.collection - context["pattern_type"] = self.pattern_type - context["url_type"] = self.kwargs["url_type"] - return context - - -class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): - pattern_model = DeltaExcludePattern - pattern_type = "Exclude" - - -class IncludePatternAffectedURLsListView(BaseAffectedURLsListView): - pattern_model = DeltaIncludePattern - pattern_type = "Include" - - -class TitlePatternAffectedURLsListView(BaseAffectedURLsListView): - pattern_model = DeltaTitlePattern - pattern_type = "Title" - - -class DocumentTypePatternAffectedURLsListView(BaseAffectedURLsListView): - pattern_model = DeltaDocumentTypePattern - pattern_type = "Document Type" - - class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): pattern_model = None diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css deleted file mode 100644 index d6da61d3..00000000 --- a/sde_indexing_helper/static/css/affected_urls.css +++ /dev/null @@ -1,275 +0,0 @@ -.dataTables_scrollHead, -.dataTables_scrollBody { - overflow: visible !important; -} - -.dataTables_wrapper .dataTables_paginate .paginate_button:hover { - background: none; - border: none; -} - -.dataTables_wrapper .dataTables_paginate .paginate_button { - padding: 0em; -} - -.dataTables_wrapper .dataTables_paginate .paginate_button { - padding: .5em; -} - -.paginate_input { - width: 15%; -} - -.table_filter_row_input { - width: 100%; -} - -.candidateUrlContainer { - background: #15232E; - padding: 40px 30px; - border-radius: 15px; -} - -.table tbody tr:nth-child(odd) { - background-color: #050E19 !important; -} - -.table tbody tr:nth-child(even) { - background-color: #3F4A58 !important; -} - -.candidateTitle { - font-size: 24px; - font-weight: 500; -} - -.custom-select, -.buttons-csv, -.customizeColumns, -.addPattern { - border-style: solid !important; - border-color: #A7BACD !important; - border-width: 1px !important; - color: #A7BACD !important; - border-radius: 5px !important; - padding: 11px 15px; -} - -.addPattern { - background-color: #0066CA !important; - border-color: #0066CA !important; - color: #fff !important; -} - -#match_pattern_input { - background: #3F4A58; - border-radius: 4px; -} - -.asterik { - color: #C3001A; -} - -.customizeColumns { - margin-left: 10px !important; -} - -.form-control:read-only { - background-image: none; -} - -.form-control { - color: white; -} - -.form-control:focus { - color: white; -} - -.dt-container div.dt-length label { - display: none; -} - -div.dt-container div.dt-info { - padding-top: 0; - white-space: normal; -} - -.page-link { - color: white !important; - border: 0.5px solid !important; - margin-left: 3px; - margin-right: 3px; -} - -.page-link:hover { - background-color: #0066CA !important; -} - -.page-item.disabled .page-link { - color: grey !important; -} - -.dt-paging-input { - color: white; -} - -.dt-paging-input input { - background-color: #3F4A58; - color: white; - border: solid 0.5px !important; -} - -.dt-inputpaging { - position: absolute; - right: 16px; - top: -27px; -} - -.ml-auto { - width: 50%; -} - -.custom-select-sm { - margin-left: 5px; -} - -.selected { - background-color: inherit !important; -} - -.headerDiv { - display: flex; - justify-content: space-between; -} - -.url-cell { - display: flex; - align-items: center; - justify-content: space-between; - word-wrap: break-word; - word-break: break-all; - white-space: normal; - overflow-wrap: break-word; - min-width: 100%; - max-width: 100%; -} - -.url-icon { - color: #65B1EF; -} - -/* pagination position */ -div.dt-container div.dt-paging ul.pagination { - position: absolute; - right: 60px; -} - -/* Dark theme adjustments later added */ -body { - background-color: #2c2c2c; - color: #f5f5f5; -} - -.pageTitle { - color: #f5f5f5; -} - -.table { - background-color: #1e1e1e; - color: #f5f5f5; -} - -.table thead th { - background-color: #444; - color: #f5f5f5; -} - -.table tbody tr:hover { - background-color: #333; -} - -.table tbody tr td a { - color: #61dafb; - /* URL links */ -} - -.table tbody tr td a:hover { - color: #f5f5f5; - /* Hover effect for links */ -} - -/* Optional styles for the Include URL button */ -.include-url-btn { - background-color: transparent; - border: none; - font-size: 2rem; - cursor: pointer; -} - -.modalTitle { - font-size: 24px; - font-weight: 600; - line-height: 36px; - letter-spacing: -0.03em; -} - -#hideShowColumnsModal { - position: fixed; - top: 0; - right: 0 !important; - left: unset !important; - background: #FFFFFF; - width: 30vw; - z-index: 2000; -} - -.modalFooter { - position: sticky; - bottom: 0; - position: sticky; - bottom: 0; - padding: 10px 0; - background: #FFFFFF; -} - -.modal-body .bmd-label-static { - top: -20px !important; -} - -.modal-header { - margin-bottom: 40px; -} - -.row .col-md-auto { - display: flex; - align-items: center; - justify-content: space-between; -} - -.dt-buttons { - margin-left: auto; -} - -.custom-menu { - display: none; - z-index: 1000; - position: absolute; - overflow: hidden; - border: 1px solid #CCC; - white-space: nowrap; - font-family: sans-serif; - background: #FFF; - color: white; - border-radius: 5px; - background-color: #15232E; -} - -.custom-menu li { - padding: 8px 12px; - cursor: pointer; -} - -.custom-menu li:hover { - background-color: #0066CA; -} diff --git a/sde_indexing_helper/static/css/delta_url_list.css b/sde_indexing_helper/static/css/delta_url_list.css index 4fc79ddb..a35f322b 100644 --- a/sde_indexing_helper/static/css/delta_url_list.css +++ b/sde_indexing_helper/static/css/delta_url_list.css @@ -453,8 +453,13 @@ div.dt-container div.dt-paging ul.pagination { } /* Specific styling for affected URLs modal only */ +#affectedURLsModal { + padding: 0 !important; +} + #affectedURLsModal .modal-dialog { max-width: 90%; + height: auto; margin: 1.75rem auto; } @@ -462,12 +467,28 @@ div.dt-container div.dt-paging ul.pagination { background-color: #15232E; color: white; width: 100%; + height: auto; + max-height: 80vh; + display: flex; + flex-direction: column; } #affectedURLsModal .modal-header { border-bottom: 1px solid #3F4A58; + margin-bottom: 0px; +} + +#affectedURLsModal .modal-body { + flex: 0 1 auto; + overflow: auto; + padding: 1rem; } #affectedURLsModal .close { color: white; } + +#affectedURLsModal .dataTables_wrapper { + display: flex; + flex-direction: column; +} diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js deleted file mode 100644 index 9cd74101..00000000 --- a/sde_indexing_helper/static/js/affected_urls.js +++ /dev/null @@ -1,82 +0,0 @@ -var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); -var INDIVIDUAL_URL = 1; -var MULTI_URL_PATTERN = 2; - -$(document).ready(function () { - handleAjaxStartAndStop(); - initializeDataTable(); -}); - -function handleAjaxStartAndStop() { - $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI); -} - -function initializeDataTable() { - const PATTERN_ENDPOINTS = { - Exclude: "exclude-pattern-affected-urls", - Include: "include-pattern-affected-urls", - Title: "title-pattern-affected-urls", - "Document Type": "documenttype-pattern-affected-urls", - }; - - var affected_urls_table = $("#affectedURLsTable").DataTable({ - processing: true, - pageLength: 100, - colReorder: true, - stateSave: true, - serverSide: true, - orderCellsTop: true, - pagingType: "input", - paging: true, - rowId: "url", - layout: { - bottomEnd: "inputPaging", - topEnd: null, - topStart: { - info: true, - pageLength: { - menu: [ - [25, 50, 100, 500], - ["Show 25", "Show 50", "Show 100", "Show 500"], - ], - }, - buttons: [], - }, - }, - columnDefs: [ - { orderable: true, targets: "_all" }, - { orderable: false, targets: "filter-row" }, - ], - orderCellsTop: true, - ajax: { - url: `/api/${PATTERN_ENDPOINTS[patternType]}/?format=datatables&url_type=${urlType}&pattern_id=${pattern_id}`, - data: function (d) {}, - complete: function (xhr, status) {}, - }, - - columns: [ - { data: "id", class: "whiteText text-center" }, - getURLColumn(), - ], - }); - - $("#affectedURLsFilter").on( - "beforeinput", - DataTable.util.debounce(function (val) { - affected_urls_table.columns(0).search(this.value).draw(); - }, 1000) - ); -} - -function getURLColumn() { - return { - data: "url", - width: "30%", - render: function (data, type, row) { - return `
${data} - - open_in_new -
`; - }, - }; -} diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index 8f19f574..103b584f 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -106,7 +106,7 @@ function modalContents(tableName) { function renderCountWithViewButton(count, patternType, urlType, rowId) { return `
- + ${count}
From 541c96e92a0ffb920f4df2bd1f21215d45aac71a Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 5 Mar 2025 22:39:16 -0600 Subject: [PATCH 09/12] Remove unnecessary url mappings --- sde_collections/urls.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 0518fd97..dcba1d94 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -82,24 +82,4 @@ name="candidate-url-api", ), path("titles-and-errors/", views.TitlesAndErrorsView.as_view(), name="titles-and-errors-list"), - path( - "exclude-pattern//-urls", - view=views.ExcludePatternAffectedURLsListView.as_view(), - name="exclude_pattern_urls", - ), - path( - "include-pattern//-urls", - view=views.IncludePatternAffectedURLsListView.as_view(), - name="include_pattern_urls", - ), - path( - "title-pattern//-urls", - view=views.TitlePatternAffectedURLsListView.as_view(), - name="title_pattern_urls", - ), - path( - "document-type-pattern//-urls", - view=views.DocumentTypePatternAffectedURLsListView.as_view(), - name="document_type_pattern_urls", - ), ] From d9c03b1f187dc31cd6157758c87ebf5034184753 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 5 Mar 2025 22:50:43 -0600 Subject: [PATCH 10/12] Update Changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14ab69f9..e23935ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,10 +67,10 @@ For each PR made, an entry should be added to this changelog. It should contain - Added pattern application form tests with validation checks - affected-urls-page - - Description: Added functionality to view affected URLs (both Delta and Curated URLs) for each pattern type (Include, Exclude, Title, Document Type). + - Description: Added functionality to view affected URLs (both Delta and Curated URLs) for each pattern type (Include, Exclude, Title, Document Type) in a modal view. - Changes: - - Created new URL patterns with dynamic URL type handling (`///`) - - Added `BaseAffectedURLsListView` and pattern-specific views for handling URL displays - - Implemented `BaseAffectedURLsViewSet` and pattern-specific views for API endpoints - - Added `affected_urls.html`, `affected_urls.css` and`affected_urls.js` - - Enhanced pattern list views with clickable eye icons + - Created new API endpoints for each pattern type to fetch affected URLs + - Added `BaseAffectedURLsViewSet` and pattern-specific views for API endpoints + - Implemented modal-based display for affected URLs with dynamic sizing + - Enhanced pattern list views with clickable eye icons that shows "Affected URLs" modal + - Improved user experience by keeping users on the same page while viewing affected URLs From cdb7fc60888fd37155e5c4607b11a283e71eba48 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 5 Mar 2025 23:04:37 -0600 Subject: [PATCH 11/12] Fix XSS Suggestion --- sde_indexing_helper/static/css/delta_url_list.css | 6 ++++++ sde_indexing_helper/static/js/delta_url_list.js | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sde_indexing_helper/static/css/delta_url_list.css b/sde_indexing_helper/static/css/delta_url_list.css index a35f322b..65865064 100644 --- a/sde_indexing_helper/static/css/delta_url_list.css +++ b/sde_indexing_helper/static/css/delta_url_list.css @@ -492,3 +492,9 @@ div.dt-container div.dt-paging ul.pagination { display: flex; flex-direction: column; } + +#affectedURLsModal .col-md-auto { + display: flex; + align-items: center; + gap: 1rem; +} diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index 103b584f..1849d987 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -2306,10 +2306,9 @@ function handleShowAffectedURLsListButtonClick() { // Update modal title and pattern info $("#affectedURLsModalTitle").text(`Affected ${urlType} URLs`); - $("#patternInfo").html(` + $("#patternInfo").text(` ${urlCount} affected URL${urlCount === '1' ? '' : 's'} for ${patternType} pattern: - ${patternName} - `); + `).append($('').css('color', '#65B1EF').text(patternName)); if ($.fn.DataTable.isDataTable('#affectedURLsModalTable')) { $('#affectedURLsModalTable').DataTable().destroy(); From 1866f7a1bb7bb5dc40cbfd75ded279b91d880fd7 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 6 Mar 2025 12:37:11 -0600 Subject: [PATCH 12/12] Capitalise title --- sde_indexing_helper/static/js/delta_url_list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index 1f6b4a5e..7464d4a9 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -2310,7 +2310,8 @@ function handleShowAffectedURLsListButtonClick() { const urlCount = $(this).prev('.urlCount').text().trim(); // Update modal title and pattern info - $("#affectedURLsModalTitle").text(`Affected ${urlType} URLs`); + const capitalize = str => str[0].toUpperCase() + str.slice(1); + $("#affectedURLsModalTitle").text(`Affected ${capitalize(urlType)} URLs`); $("#patternInfo").text(` ${urlCount} affected URL${urlCount === '1' ? '' : 's'} for ${patternType} pattern: `).append($('').css('color', '#65B1EF').text(patternName));