From 933852ba70a7539266c04e937c7d6e981ebbb1a2 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Mon, 16 Sep 2024 21:24:56 -0500 Subject: [PATCH 01/26] Add 'Show Affected URLs' button and configure click handler --- .../static/js/candidate_url_list.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sde_indexing_helper/static/js/candidate_url_list.js b/sde_indexing_helper/static/js/candidate_url_list.js index ed6d3e4b..a4f397ab 100644 --- a/sde_indexing_helper/static/js/candidate_url_list.js +++ b/sde_indexing_helper/static/js/candidate_url_list.js @@ -387,6 +387,14 @@ function initializeDataTable() { data: "candidate_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return `
+ ${data} + +
`; + }, }, { data: null, @@ -813,6 +821,7 @@ function setupClickHandlers() { handleDeleteIncludePatternButtonClick(); handleDeleteTitlePatternButtonClick(); handleDeleteDivisionButtonClick(); + handleShowAffectedURLsListButtonClick(); handleDocumentTypeSelect(); handleDivisionSelect(); @@ -1126,6 +1135,14 @@ function handleExcludeIndividualUrlClick() { }); } +function handleShowAffectedURLsListButtonClick() { + $("body").on("click", ".view-affected-urls", function () { + var matchPatternId = $(this).data("row-id"); + console.log(matchPatternId); + }); +} + + function handleDeleteExcludePatternButtonClick() { $("body").on("click", ".delete-exclude-pattern-button", function () { var patternRowId = $(this).data("row-id"); From b9d2385281eb20839ff5d8070d8383148afbe2ca Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Tue, 17 Sep 2024 01:37:38 -0500 Subject: [PATCH 02/26] Add necessary files for 'Affected URLs' page --- sde_collections/urls.py | 5 + sde_collections/views.py | 26 + .../static/css/affected_urls.css | 466 ++++++++++++++++++ .../static/js/candidate_url_list.js | 3 + .../sde_collections/affected_urls.html | 66 +++ 5 files changed, 566 insertions(+) create mode 100644 sde_indexing_helper/static/css/affected_urls.css 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 4e3d0534..9d5a43cb 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -43,6 +43,11 @@ view=views.CandidateURLsListView.as_view(), name="candidate_urls", ), + path( + "exclude-pattern//", + view=views.AffectedURLsListView.as_view(), + name="affected_urls", + ), path( "consolidate/", view=views.WebappGitHubConsolidationView.as_view(), diff --git a/sde_collections/views.py b/sde_collections/views.py index 241979ba..2a008ad6 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -225,6 +225,32 @@ def get_context_data(self, **kwargs): return context +class AffectedURLsListView(LoginRequiredMixin, ListView): + """ + Display a list of URLs affected by a match pattern + """ + + model = CandidateURL + template_name = "sde_collections/affected_urls.html" + context_object_name = "affected_urls" + paginate_by = 100 + + def get_queryset(self): + self.pattern = ExcludePattern.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 + + # affected_urls = self.pattern.matched_urls() + # for url in affected_urls: + # print(url.collection) + # Your affected_urls is a list of CandidateURL model objects. + + return context + class SdeDashboardView(LoginRequiredMixin, ListView): model = Collection 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..fff75d34 --- /dev/null +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -0,0 +1,466 @@ +#top_pattern_bar { + position: sticky; + top: 0 +} + +.done_color { + color: green; +} + +.close_color { + color: red; +} + +#test_url { + text-decoration: underline; + text-decoration-style: dotted; + text-decoration-line: underline; + text-decoration-thickness: 1px; +} + +.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; +} + +.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; +} + +.pattern_type { + /* padding-top: 10px; + padding-bottom: 10px; */ +} + +.dataTables_wrapper .dataTables_paginate .paginate_button { + padding: .5em; +} + +.paginate_input { + width: 15%; +} + +.exclude_individual_url { + cursor: pointer +} + +.document_type_form_select { + cursor: pointer +} + +.table_filter_row_input{ + width: 100%; +} + + + .select-dropdown { + text-align: center; + width: 100% !important; + color: #333333;; + background-color: #fafafa; + border-radius: 0.2rem; + border-color: #fafafa; + font-size: 0.6875rem; + box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); } + + .select-dropdown:hover { + box-shadow: 0 14px 26px -12px rgba(250, 250, 250, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(250, 250, 250, 0.2); + } + + .select-dropdown:focus, + .select-dropdown.focus { + box-shadow: none, 0 0 0 0.2rem rgba(76, 175, 80, 0.5); + } + +/* badge showing workflow status by header */ +.badge { + cursor: default !important; + margin-left: 15px; +} + + +.table_filter_row_input, .doc-dropdown{ + width: 100%; +} + +.doc-dropdown { + background: #A7BACD !important; + font-size: 15px; + font-weight: 500; + line-height: 17.58px; + color: #1F2935; + display: flex; + justify-content: space-between; + align-items: center; + text-transform: capitalize; + border-radius: 4px; + margin-bottom: 0; +} + +.doc-dropdown-input { + flex-direction: column; + width: 100%; +} + +.dropdown-item:hover { + background-color: #0066CA; +} + +.doc-type-form { + width: 100%; + background: #15232E; + color: white; + border: 1px solid white; + padding: 24px 15px; + border-radius: 4px; +} + +.doc-type-form a { + height: 24px; + padding: 0; +} + +.document_type_form_select { + font-size: 16px; + font-weight: 600; + letter-spacing: -0.02em; + height: 37px !important; + align-items: center; + display: flex; + padding-left: 10px !important; + border-radius: 1px !important; +} + +.candidateUrlContainer { + background: #15232E; + padding: 40px 30px; + border-radius: 15px; +} +.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; +} + +#caption, #subTitle { +font-size: 14px; +font-weight: 400; +line-height: 21px; +letter-spacing: -0.02em; +} + + + + .checkbox-wrapper { + display: flex; + align-items: baseline; + } + + .checkbox-wrapper label { + font-weight: 600; + font-size: 16px; + line-height: 24px; + margin-bottom: 0; + color: rgba(31, 41, 53, 1); + padding-left: 10px; + } + + .modalFooter { + position: sticky; + bottom: 0; + position: sticky; + bottom: 0; + padding: 10px 0; + background: #FFFFFF; + } +.badge{ + background-color: #FF3D57; +} + +.notifyBadge{ + margin-left:5px !important; +} + +.sorting_1 { + overflow: visible; + white-space: normal; + word-wrap: break-word; + max-width: 600px; + width: 600px; + color: #65B1EF; + } + +.title-dropdown { + width: fit-content !important; + margin-top:20px; + margin-bottom:20px; +} +.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; + } + + #exclude_patterns_table_wrapper .dt-buttons, #include_patterns_table_wrapper .dt-buttons, #document_type_patterns_table_wrapper .dt-buttons, #title_patterns_table_wrapper .dt-buttons { + width: 89%; + justify-content: end; + } + + .customizeColumns { + margin-left: 10px !important; + } + + .form-control:read-only { + background-image: none; + } + + .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; +} + + +div.dt-buttons .btn.processing:after { + border: 2px solid #FFFFFF !important; + border-left-color: transparent !important; + border-right-color: transparent !important; + -webkit-animation: dtb-spinner 1500ms infinite linear; +} + +.document_type_dropdown, .division_dropdown, .dropdown-toggle { + width: 100%; + display: flex; + justify-content: center; +} + + .dropdown-toggle { + width: 80%; + /* display: flex; */ + align-items: center; + /* justify-content: space-between; */ + } + +.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; + } +#match_pattern_input, #title_pattern_input { + background: #3F4A58; + border-radius: 4px; +} + +.modal-body .bmd-label-static { + top: -20px !important; +} + +.modal-header { + margin-bottom: 40px; +} + +.form-label { + color: white; + display: flex; + font-size: 12px; + font-weight: 500; + letter-spacing: -0.02em; +} + +.asterik { + color: #C3001A; +} + +.title_pattern-form-group { + margin-top: 40px; +} + +.is-focused [class^='bmd-label']{ + color:#0066CA; + } + .form-control{ + color:white; + } + + .form-control:focus{ + color:white; + } + + .is-focused .form-label{ + background-image:linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); + color:#AAAAAA; + } + + .dropdown-item:hover{ + background-color: #0066CA !important; + } + + +/* pagination position */ +div.dt-container div.dt-paging ul.pagination { + position: absolute; + right: 60px; +} + +.individual_title_input { + width: 100%; + max-width: 100%; + min-width: 100%; +} + + +/* 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 */ +} +.alert-info { + background-color: #444; + color: #f5f5f5; +} +.list-group-item { + background-color: #333; + color: #f5f5f5; +} \ No newline at end of file diff --git a/sde_indexing_helper/static/js/candidate_url_list.js b/sde_indexing_helper/static/js/candidate_url_list.js index a4f397ab..6dee56ba 100644 --- a/sde_indexing_helper/static/js/candidate_url_list.js +++ b/sde_indexing_helper/static/js/candidate_url_list.js @@ -1139,6 +1139,9 @@ function handleShowAffectedURLsListButtonClick() { $("body").on("click", ".view-affected-urls", function () { var matchPatternId = $(this).data("row-id"); console.log(matchPatternId); + + // Redirect to the new page that will display the URLs + window.open(`/exclude-pattern/${matchPatternId}/`, '_blank'); }); } 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..6e7b597c --- /dev/null +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -0,0 +1,66 @@ +{% extends "layouts/base.html" %} +{% load static i18n %} +{% load humanize %} +{% block title %} +Affected URLs +{% endblock title %} + +{% block stylesheets %} + {{ block.super }} + + + + +{% endblock stylesheets %} + +{% block content %} + {% csrf_token %} +
+

Affected URLs

+
+ +
+ + + + + + + + + + {% for url in affected_urls %} + + + + + {% empty %} + + + + {% endfor %} + +
#URL
{{ forloop.counter }}{{ url.url }}
No URLs were affected by this pattern.
+ + +
+ +
+
+{% endblock content %} From e89eaa15a557c30d7ba961db85aed74de4cc99ff Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 18 Sep 2024 22:53:19 -0500 Subject: [PATCH 03/26] Add DataTables features to the page --- sde_collections/views.py | 6 +- .../static/css/affected_urls.css | 16 ++ .../static/js/affected_urls.js | 205 ++++++++++++++++++ .../sde_collections/affected_urls.html | 54 ++++- 4 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 sde_indexing_helper/static/js/affected_urls.js diff --git a/sde_collections/views.py b/sde_collections/views.py index 2a008ad6..712bde6a 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -233,7 +233,7 @@ class AffectedURLsListView(LoginRequiredMixin, ListView): model = CandidateURL template_name = "sde_collections/affected_urls.html" context_object_name = "affected_urls" - paginate_by = 100 + # paginate_by = 100 def get_queryset(self): self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) @@ -243,6 +243,10 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["pattern"] = self.pattern + context["url_count"] = self.pattern.matched_urls().count() + context["collection"] = self.pattern.collection + + # print(self.pattern.collection.id) # affected_urls = self.pattern.matched_urls() # for url in affected_urls: diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index fff75d34..72c85918 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -463,4 +463,20 @@ body { .list-group-item { background-color: #333; color: #f5f5f5; +} + +/* Optional styles for the Include URL button */ +.include-url-btn { + background-color: transparent; + border: none; + font-size: 1.5rem; + cursor: pointer; +} + +.include-url-btn .cross-mark { + color: red; +} + +.include-url-btn .tick-mark { + color: green; } \ No newline at end of file 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..b18afc6d --- /dev/null +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -0,0 +1,205 @@ +var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); +var INDIVIDUAL_URL = 1; +var MULTI_URL_PATTERN = 2; +collection_id = getCollectionId(); + +// Maybe you need to define a new DataTable for this page as well +// So that you can refresh it any way you want +// Or maybe this is not necessary + +$(document).ready(function () { + // handleAjaxStartAndStop(); + initializeDataTable(); + setupClickHandlers(); + }); + +function initializeDataTable() { + var affected_urls_table = $("#urlsTable").DataTable({ + pageLength: 100, + colReorder: true, + stateSave: true, + layout: { + bottomEnd: "inputPaging", + topEnd: null, + topStart: { + info: true, + pageLength: { + menu: [ + [25, 50, 100, 500], + ["Show 25", "Show 50", "Show 100", "Show 500"], + ], + }, + }, + serverSide: true, + orderCellsTop: true, + pagingType: "input", + rowId: "url" + } + }) +} + +function setupClickHandlers() { + handleIncludeIndividualUrlClick(); + } + +function handleIncludeIndividualUrlClick() { + $("body").on("click", ".include-url-btn", function () { + + const span = this.querySelector('span'); + if (span.classList.contains('cross-mark')) { + // Change to tick mark + span.classList.remove('cross-mark'); + span.innerHTML = '✔'; // Tick mark + // then add that URL to the includeURLs list + match_pattern = remove_protocol($(this).attr("value")) ; + match_pattern_type = INDIVIDUAL_URL; + console.log(match_pattern); + postIncludePatterns( + match_pattern = match_pattern, + match_pattern_type = match_pattern_type, + true + ); + + const row = $(this).closest('tr'); // Get the closest table row + const rowId = $("#urlsTable").DataTable().row(row).index(); + console.log(rowId); + deleteRowById(rowId); + + //Along with this, remove this pattern from the exclude_patterns + // First, check if similar kind of pattern is available in the exclude_pattern table + // If yes, this run this block of code. + // postExcludePatterns( + // match_pattern = match_pattern, + // match_pattern_type = match_pattern_type, + // true + // ); + + + } else { + // Change back to cross mark + span.classList.add('cross-mark'); + span.innerHTML = '❌'; // Cross mark + } + + }); + } + + function handleExcludeIndividualUrlClick() { + // exclude that URL + // check in the include patterns if similar URL is present + // if yes then delete that URL in the + + } + +function postIncludePatterns(match_pattern, match_pattern_type = 0) { + if (!match_pattern) { + toastr.error("Please highlight a pattern to include."); + return; + } + + // if pattern exists in table already + // var table = $("#include_patterns_table").DataTable(); + // var itemIdColumnData = table.column(0).data().toArray(); + // if (itemIdColumnData.includes(match_pattern)) { + // toastr.success("Pattern already exists"); + // return; + // } + + $.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) { + console.log("Success on adding to the Included URLs"); + // $("#candidate_urls_table").DataTable().ajax.reload(null, false); + // $("#include_patterns_table").DataTable().ajax.reload(null, false); + // if(currentTab === ""){ //Only add a notification if we are on the first tab + // newIncludePatternsCount = newIncludePatternsCount + 1; + // $("#includePatternsTab").html( + // `Include Patterns ` + + // newIncludePatternsCount + " new" + + // `` + // ); + // } + }, + error: function (xhr, status, error) { + var errorMessage = xhr.responseText; + toastr.error(errorMessage); + }, + }); +} + +function getCollectionId() { + return collection_id; +} + +function remove_protocol(url) { + return url.replace(/(^\w+:|^)\/\//, ""); + } + + +function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { +// if (!match_pattern) { +// toastr.error("Please highlight a pattern to exclude."); +// return; +// } +// if (!force) { +// //If the user clicked the icon in the table, we make the change regardless +// // if pattern exists in table already (unless another pattern overrules it) +// var table = $("#exclude_patterns_table").DataTable(); +// var itemIdColumnData = table.column(0).data().toArray(); +// if (itemIdColumnData.includes(match_pattern)) { +// toastr.success("Pattern already exists"); +// return; +// } +// } + + $.ajax({ + url: "/api/exclude-patterns/", + type: "POST", + data: { + collection: collection_id, + match_pattern: match_pattern, + match_pattern_type: match_pattern_type, + csrfmiddlewaretoken: csrftoken, + }, + success: function (data) { + console.log("Success on removing from Excluded URLs"); + // $("#candidate_urls_table").DataTable().ajax.reload(null, false); + // $("#exclude_patterns_table").DataTable().ajax.reload(null, false); + // if(currentTab === ""){ //Only add a notification if we are on the first tab + // newExcludePatternsCount = newExcludePatternsCount + 1; + // $("#excludePatternsTab").html( + // `Exclude Patterns ` + + // newExcludePatternsCount + " new" + + // `` + // ); + // } + }, + error: function (xhr, status, error) { + var errorMessage = xhr.responseText; + toastr.error(errorMessage); + }, + }); +} + +function deleteRowById(rowId) { + // Find the DataTable instance + var affected_urls_table = $("#urlsTable").DataTable(); + + // Find the row with ID 1 + var rowToDelete = affected_urls_table.row(rowId); // Adjust based on 0-indexing + + if (rowToDelete.length) { + rowToDelete.remove(); // Remove the row + affected_urls_table.draw(); // Redraw the table + } else { + console.log("Row not found."); + } + } + \ No newline at end of file diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 6e7b597c..114b35b9 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -19,31 +19,43 @@

Affected URLs

+

+ {{ url_count }} affected URLs for exclude pattern: {{ pattern.match_pattern }} +

+
- + + {% for url in affected_urls %} - + + {% empty %} - + {% endfor %}
#URLURLInclude URL
{{ forloop.counter }}{{ url.url }} + {{ url.url }} + + +
No URLs were affected by this pattern.No URLs were affected by this pattern.
-
+ {% endblock content %} + +{% block javascripts %} + {{ block.super }} + + + + + + + + + + + + +{% endblock javascripts %} \ No newline at end of file From 194929a04368b7adf170370f9edcd5300f20a174 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Mon, 23 Sep 2024 18:14:51 -0500 Subject: [PATCH 04/26] Affected URLs page for excluded patterns working properly --- sde_collections/views.py | 29 +++- .../static/js/affected_urls.js | 131 +++++++++++++++--- .../sde_collections/affected_urls.html | 27 ++-- 3 files changed, 157 insertions(+), 30 deletions(-) diff --git a/sde_collections/views.py b/sde_collections/views.py index 712bde6a..39eeab22 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -51,6 +51,8 @@ User = get_user_model() +from django.db.models import BooleanField, Case, Value, When, Q + class CollectionListView(LoginRequiredMixin, ListView): """ @@ -235,9 +237,34 @@ class AffectedURLsListView(LoginRequiredMixin, ListView): context_object_name = "affected_urls" # paginate_by = 100 + # def get_queryset(self): + # self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) + # queryset = self.pattern.matched_urls() + # return queryset + def get_queryset(self): + # Get the exclude pattern based on the ID from the URL kwargs self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) - queryset = self.pattern.matched_urls() + + # Get excluded URLs + excluded_urls = self.pattern.matched_urls().annotate(is_included=Value(False, output_field=BooleanField())) + + # Get included URLs for the same collection + include_patterns = IncludePattern.objects.filter(collection=self.pattern.collection) + included_urls = CandidateURL.objects.filter( + collection=self.pattern.collection, + id__in=include_patterns.values_list('candidate_urls__id', flat=True) + ).distinct() + + # Annotate excluded URLs with is_included if they are also in included_urls + queryset = excluded_urls.annotate( + is_included=Case( + When(id__in=included_urls.values('id'), then=Value(True)), + default=Value(False), + output_field=BooleanField() + ) + ) + return queryset def get_context_data(self, **kwargs): diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index b18afc6d..c0eaf79d 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -3,18 +3,14 @@ var INDIVIDUAL_URL = 1; var MULTI_URL_PATTERN = 2; collection_id = getCollectionId(); -// Maybe you need to define a new DataTable for this page as well -// So that you can refresh it any way you want -// Or maybe this is not necessary - $(document).ready(function () { // handleAjaxStartAndStop(); initializeDataTable(); setupClickHandlers(); - }); +}); function initializeDataTable() { - var affected_urls_table = $("#urlsTable").DataTable({ + var affected_urls_table = $("#affectedURLsTable").DataTable({ pageLength: 100, colReorder: true, stateSave: true, @@ -34,7 +30,18 @@ function initializeDataTable() { orderCellsTop: true, pagingType: "input", rowId: "url" - } + }, + // createdRow: function (row, data, dataIndex) { + // // Assuming the 'Include URL' column is at index 3 + // let includeUrlCell = $(row).find('td').eq(2); + + // // Check if the cell has the cross-mark + // if (includeUrlCell.find('i.cross-mark').length > 0) { + // // Highlight the row if it contains a cross-mark + // $(row).css('background-color', 'rgba(255, 61, 87, 0.36)'); // Light red background + // } + // } + }) } @@ -45,11 +52,22 @@ function setupClickHandlers() { function handleIncludeIndividualUrlClick() { $("body").on("click", ".include-url-btn", function () { - const span = this.querySelector('span'); - if (span.classList.contains('cross-mark')) { + const i = this.querySelector('i'); + if (i.classList.contains('cross-mark')) { // Change to tick mark - span.classList.remove('cross-mark'); - span.innerHTML = '✔'; // Tick mark + i.classList.remove('cross-mark'); + i.classList.add('tick-mark'); // Add the tick-mark class + i.style.color = 'green'; // Change color to green + i.textContent = 'check'; // Set the text to "check" + let parentCol3 = i.closest('.col-3'); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute('data-sort', '1'); // Set data-sort to '1' for the check-mark + } + + // // Change to tick mark + // i.classList.remove('cross-mark'); + // i.innerHTML = '✔'; // Tick mark // then add that URL to the includeURLs list match_pattern = remove_protocol($(this).attr("value")) ; match_pattern_type = INDIVIDUAL_URL; @@ -60,10 +78,10 @@ function handleIncludeIndividualUrlClick() { true ); - const row = $(this).closest('tr'); // Get the closest table row - const rowId = $("#urlsTable").DataTable().row(row).index(); - console.log(rowId); - deleteRowById(rowId); + // const row = $(this).closest('tr'); // Get the closest table row + // const rowId = $("#affectedURLsTable").DataTable().row(row).index(); + // console.log(rowId); + // deleteRowById(rowId); //Along with this, remove this pattern from the exclude_patterns // First, check if similar kind of pattern is available in the exclude_pattern table @@ -74,11 +92,42 @@ function handleIncludeIndividualUrlClick() { // true // ); - + } else { - // Change back to cross mark - span.classList.add('cross-mark'); - span.innerHTML = '❌'; // Cross mark + // Handle the functionality of excluding that URL again (maybe delete that include pattern which was just created) + var url = $(this).attr("value"); + console.log("url", url); + getCorrespondingIncludePattern(url).then(function(patternId) { + if (patternId !== null) { + console.log('Pattern ID:', patternId); + currentURLtoDelete = `/api/include-patterns/${patternId}/`; + deletePattern( + currentURLtoDelete, + (data_type = "Include Pattern") + ); + + // Change back to cross mark + i.classList.remove('tick-mark'); + i.classList.add('cross-mark'); // Add the cross-mark class + i.style.color = 'red'; // Change color to red + i.textContent = 'close'; // Set the text to "close" + let parentCol3 = i.closest('.col-3'); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute('data-sort', '0'); // Set data-sort to '0' for the cross-mark + } + + + console.log("URL removed from the include pattern") + + + } else { + console.log('No matching pattern found.'); + } + }).catch(function(error) { + console.error("Error occurred:", error); + }); + } }); @@ -190,7 +239,7 @@ function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { function deleteRowById(rowId) { // Find the DataTable instance - var affected_urls_table = $("#urlsTable").DataTable(); + var affected_urls_table = $("#affectedURLsTable").DataTable(); // Find the row with ID 1 var rowToDelete = affected_urls_table.row(rowId); // Adjust based on 0-indexing @@ -202,4 +251,44 @@ function deleteRowById(rowId) { console.log("Row not found."); } } - \ No newline at end of file + +function deletePattern( + url, + data_type, + url_type = null, + candidate_urls_count = null +) { + $.ajax({ + url: url, + type: "DELETE", + data: { + csrfmiddlewaretoken: csrftoken, + }, + headers: { + "X-CSRFToken": csrftoken, + }, + success: function (data) { + console.log("Successfully deleted.") + }, + }); +} + +function getCorrespondingIncludePattern(url) { + return $.ajax({ + url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, + method: 'GET', + dataType: 'json' + }).then(function(response) { + // Iterate through the 'data' array to find a matching pattern + for (let i = 0; i < response.data.length; i++) { + let pattern = response.data[i].match_pattern; + if ( pattern === remove_protocol(url)) { + return response.data[i].id; // Return the first matching pattern id + } + } + return null; // Return null if no pattern matches + }).catch(function(error) { + console.error("Error fetching include patterns:", error); + return null; + }); +} \ No newline at end of file diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 114b35b9..9d00d65d 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -25,25 +25,36 @@

- +
- - + + + {% for url in affected_urls %} + - + + {% empty %} From b15410e2bf338c360d4680da8bd610ce4d91ec6f Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 25 Sep 2024 16:56:45 -0500 Subject: [PATCH 05/26] add view affected urls button to title patterns and document-type patterns --- sde_collections/models/candidate_url.py | 8 + sde_collections/urls.py | 15 ++ sde_collections/views.py | 75 +++++--- .../static/css/affected_urls.css | 4 + .../static/js/affected_urls.js | 161 ++++++++++-------- .../static/js/candidate_url_list.js | 53 +++++- .../sde_collections/affected_urls.html | 96 ++++------- 7 files changed, 255 insertions(+), 157 deletions(-) diff --git a/sde_collections/models/candidate_url.py b/sde_collections/models/candidate_url.py index 51c3a28b..7ae05c38 100644 --- a/sde_collections/models/candidate_url.py +++ b/sde_collections/models/candidate_url.py @@ -11,6 +11,14 @@ class CandidateURLQuerySet(models.QuerySet): def with_exclusion_status(self): + # currently this is only handling the exclude_patterns + # need to add functionality of including include_patterns + # exclude=false + # if some_include_pattern exists which matches this url: + # then exclude = false + # else + # if some_exclude_pattern exists which matches this url: + # exclude = true return self.annotate( excluded=models.Exists( ExcludePattern.candidate_urls.through.objects.filter(candidateurl=models.OuterRef("pk")) diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 9d5a43cb..131efbed 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -48,6 +48,21 @@ view=views.AffectedURLsListView.as_view(), name="affected_urls", ), + path( + "include-pattern//", + view=views.AffectedURLsListView.as_view(), + name="affected_urls", + ), + path( + "title-pattern//", + view=views.AffectedURLsListView.as_view(), + name="affected_urls", + ), + path( + "document-type-pattern//", + view=views.AffectedURLsListView.as_view(), + name="affected_urls", + ), path( "consolidate/", view=views.WebappGitHubConsolidationView.as_view(), diff --git a/sde_collections/views.py b/sde_collections/views.py index 39eeab22..3d5e6ffb 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -235,35 +235,56 @@ class AffectedURLsListView(LoginRequiredMixin, ListView): model = CandidateURL template_name = "sde_collections/affected_urls.html" context_object_name = "affected_urls" - # paginate_by = 100 - - # def get_queryset(self): - # self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) - # queryset = self.pattern.matched_urls() - # return queryset def get_queryset(self): - # Get the exclude pattern based on the ID from the URL kwargs - self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) - - # Get excluded URLs - excluded_urls = self.pattern.matched_urls().annotate(is_included=Value(False, output_field=BooleanField())) - - # Get included URLs for the same collection - include_patterns = IncludePattern.objects.filter(collection=self.pattern.collection) - included_urls = CandidateURL.objects.filter( - collection=self.pattern.collection, - id__in=include_patterns.values_list('candidate_urls__id', flat=True) - ).distinct() - - # Annotate excluded URLs with is_included if they are also in included_urls - queryset = excluded_urls.annotate( - is_included=Case( - When(id__in=included_urls.values('id'), then=Value(True)), - default=Value(False), - output_field=BooleanField() + + if 'exclude-pattern' in self.request.path: + print("Going with exclude patterns......") + # Get the exclude pattern based on the ID from the URL kwargs + self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) + + # Get excluded URLs + excluded_urls = self.pattern.matched_urls().annotate(is_included=Value(False, output_field=BooleanField())) + + # Get included URLs for the same collection + include_patterns = IncludePattern.objects.filter(collection=self.pattern.collection) + included_urls = CandidateURL.objects.filter( + collection=self.pattern.collection, + id__in=include_patterns.values_list('candidate_urls__id', flat=True) + ).distinct() + + # Annotate excluded URLs with is_included if they are also in included_urls + queryset = excluded_urls.annotate( + is_included=Case( + When(id__in=included_urls.values('id'), then=Value(True)), + default=Value(False), + output_field=BooleanField() + ) + ) + + elif 'include-pattern' in self.request.path: + print("Going with include patterns......") + # Get the include pattern based on the ID from the URL kwargs + self.pattern = IncludePattern.objects.get(id=self.kwargs["id"]) + + # Get included URLs for the pattern + included_urls = self.pattern.matched_urls().annotate(is_excluded=Value(False, output_field=BooleanField())) + + # Get excluded URLs for the same collection + exclude_patterns = ExcludePattern.objects.filter(collection=self.pattern.collection) + excluded_urls = CandidateURL.objects.filter( + collection=self.pattern.collection, + id__in=exclude_patterns.values_list('candidate_urls__id', flat=True) + ).distinct() + + # Annotate included URLs with is_excluded if they are also in excluded_urls + queryset = included_urls.annotate( + is_excluded=Case( + When(id__in=excluded_urls.values('id'), then=Value(True)), + default=Value(False), + output_field=BooleanField() + ) ) - ) return queryset @@ -272,6 +293,8 @@ def get_context_data(self, **kwargs): context["pattern"] = self.pattern context["url_count"] = self.pattern.matched_urls().count() context["collection"] = self.pattern.collection + context["pattern_type"] = "Exclude" if 'exclude-pattern' in self.request.path else "Include" + # print(self.pattern.collection.id) diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index 72c85918..0345d56e 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -479,4 +479,8 @@ body { .include-url-btn .tick-mark { color: green; +} + +.cross-mark, .tick-mark { + cursor: pointer; /* Show hand cursor */ } \ No newline at end of file diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index c0eaf79d..cf94d6f6 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -14,6 +14,7 @@ function initializeDataTable() { pageLength: 100, colReorder: true, stateSave: true, + searching: true, layout: { bottomEnd: "inputPaging", topEnd: null, @@ -43,10 +44,18 @@ function initializeDataTable() { // } }) + + $("#affectedURLsFilter").on( + "beforeinput", + DataTable.util.debounce(function (val) { + affected_urls_table.columns(1).search(this.value).draw(); + }, 1000) + ); } function setupClickHandlers() { handleIncludeIndividualUrlClick(); + handleExcludeIndividualUrlClick(); } function handleIncludeIndividualUrlClick() { @@ -65,10 +74,6 @@ function handleIncludeIndividualUrlClick() { parentCol3.setAttribute('data-sort', '1'); // Set data-sort to '1' for the check-mark } - // // Change to tick mark - // i.classList.remove('cross-mark'); - // i.innerHTML = '✔'; // Tick mark - // then add that URL to the includeURLs list match_pattern = remove_protocol($(this).attr("value")) ; match_pattern_type = INDIVIDUAL_URL; console.log(match_pattern); @@ -77,20 +82,6 @@ function handleIncludeIndividualUrlClick() { match_pattern_type = match_pattern_type, true ); - - // const row = $(this).closest('tr'); // Get the closest table row - // const rowId = $("#affectedURLsTable").DataTable().row(row).index(); - // console.log(rowId); - // deleteRowById(rowId); - - //Along with this, remove this pattern from the exclude_patterns - // First, check if similar kind of pattern is available in the exclude_pattern table - // If yes, this run this block of code. - // postExcludePatterns( - // match_pattern = match_pattern, - // match_pattern_type = match_pattern_type, - // true - // ); } else { @@ -133,26 +124,73 @@ function handleIncludeIndividualUrlClick() { }); } + function handleExcludeIndividualUrlClick() { - // exclude that URL - // check in the include patterns if similar URL is present - // if yes then delete that URL in the + $("body").on("click", ".exclude-url-btn", function () { - } + const i = this.querySelector('i'); + if (i.classList.contains('cross-mark')) { + // Change to tick mark + i.classList.remove('cross-mark'); + i.classList.add('tick-mark'); // Add the tick-mark class + i.style.color = 'green'; // Change color to green + i.textContent = 'check'; // Set the text to "check" + let parentCol3 = i.closest('.col-3'); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute('data-sort', '1'); // Set data-sort to '1' for the check-mark + } -function postIncludePatterns(match_pattern, match_pattern_type = 0) { - if (!match_pattern) { - toastr.error("Please highlight a pattern to include."); - return; + match_pattern = remove_protocol($(this).attr("value")) ; + match_pattern_type = INDIVIDUAL_URL; + console.log(match_pattern); + postExcludePatterns( + match_pattern = match_pattern, + match_pattern_type = match_pattern_type, + true + ); + + + } else { + // Handle the functionality of including that URL again (maybe delete that exclude pattern which was just created) + var url = $(this).attr("value"); + console.log("url", url); + getCorrespondingExcludePattern(url).then(function(patternId) { + if (patternId !== null) { + console.log('Pattern ID:', patternId); + currentURLtoDelete = `/api/exclude-patterns/${patternId}/`; + deletePattern( + currentURLtoDelete, + (data_type = "Exclude Pattern") + ); + + // Change back to cross mark + i.classList.remove('tick-mark'); + i.classList.add('cross-mark'); // Add the cross-mark class + i.style.color = 'red'; // Change color to red + i.textContent = 'close'; // Set the text to "close" + let parentCol3 = i.closest('.col-3'); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute('data-sort', '0'); // Set data-sort to '0' for the cross-mark + } + + console.log("URL removed from the exclude pattern") + + + } else { + console.log('No matching pattern found.'); + } + }).catch(function(error) { + console.error("Error occurred:", error); + }); + + } + + }); } - // if pattern exists in table already - // var table = $("#include_patterns_table").DataTable(); - // var itemIdColumnData = table.column(0).data().toArray(); - // if (itemIdColumnData.includes(match_pattern)) { - // toastr.success("Pattern already exists"); - // return; - // } +function postIncludePatterns(match_pattern, match_pattern_type = 0) { $.ajax({ url: "/api/include-patterns/", @@ -165,16 +203,6 @@ function postIncludePatterns(match_pattern, match_pattern_type = 0) { }, success: function (data) { console.log("Success on adding to the Included URLs"); - // $("#candidate_urls_table").DataTable().ajax.reload(null, false); - // $("#include_patterns_table").DataTable().ajax.reload(null, false); - // if(currentTab === ""){ //Only add a notification if we are on the first tab - // newIncludePatternsCount = newIncludePatternsCount + 1; - // $("#includePatternsTab").html( - // `Include Patterns ` + - // newIncludePatternsCount + " new" + - // `` - // ); - // } }, error: function (xhr, status, error) { var errorMessage = xhr.responseText; @@ -193,21 +221,6 @@ function remove_protocol(url) { function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { -// if (!match_pattern) { -// toastr.error("Please highlight a pattern to exclude."); -// return; -// } -// if (!force) { -// //If the user clicked the icon in the table, we make the change regardless -// // if pattern exists in table already (unless another pattern overrules it) -// var table = $("#exclude_patterns_table").DataTable(); -// var itemIdColumnData = table.column(0).data().toArray(); -// if (itemIdColumnData.includes(match_pattern)) { -// toastr.success("Pattern already exists"); -// return; -// } -// } - $.ajax({ url: "/api/exclude-patterns/", type: "POST", @@ -218,17 +231,7 @@ function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { csrfmiddlewaretoken: csrftoken, }, success: function (data) { - console.log("Success on removing from Excluded URLs"); - // $("#candidate_urls_table").DataTable().ajax.reload(null, false); - // $("#exclude_patterns_table").DataTable().ajax.reload(null, false); - // if(currentTab === ""){ //Only add a notification if we are on the first tab - // newExcludePatternsCount = newExcludePatternsCount + 1; - // $("#excludePatternsTab").html( - // `Exclude Patterns ` + - // newExcludePatternsCount + " new" + - // `` - // ); - // } + console.log("Success on adding to the Excluded URLs"); }, error: function (xhr, status, error) { var errorMessage = xhr.responseText; @@ -291,4 +294,24 @@ function getCorrespondingIncludePattern(url) { console.error("Error fetching include patterns:", error); return null; }); +} + +function getCorrespondingExcludePattern(url) { + return $.ajax({ + url: `/api/exclude-patterns/?format=datatables&collection_id=${collection_id}`, + method: 'GET', + dataType: 'json' + }).then(function(response) { + // Iterate through the 'data' array to find a matching pattern + for (let i = 0; i < response.data.length; i++) { + let pattern = response.data[i].match_pattern; + if ( pattern === remove_protocol(url)) { + return response.data[i].id; // Return the first matching pattern id + } + } + return null; // Return null if no pattern matches + }).catch(function(error) { + console.error("Error fetching exclude patterns:", error); + return null; + }); } \ No newline at end of file diff --git a/sde_indexing_helper/static/js/candidate_url_list.js b/sde_indexing_helper/static/js/candidate_url_list.js index 6dee56ba..88e3fc93 100644 --- a/sde_indexing_helper/static/js/candidate_url_list.js +++ b/sde_indexing_helper/static/js/candidate_url_list.js @@ -390,7 +390,7 @@ function initializeDataTable() { render: function (data, type, row) { return `
${data} -
`; @@ -475,6 +475,14 @@ function initializeDataTable() { data: "candidate_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return `
+ ${data} + +
`; + }, }, { data: null, @@ -555,6 +563,14 @@ function initializeDataTable() { data: "candidate_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return `
+ ${data} + +
`; + }, }, { data: null, @@ -667,7 +683,16 @@ function initializeDataTable() { data: "candidate_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return `
+ ${data} + +
`; + }, }, + { data: null, sortable: false, @@ -1136,13 +1161,37 @@ function handleExcludeIndividualUrlClick() { } function handleShowAffectedURLsListButtonClick() { - $("body").on("click", ".view-affected-urls", function () { + $("body").on("click", ".view-exclude-pattern-urls", function () { var matchPatternId = $(this).data("row-id"); console.log(matchPatternId); // Redirect to the new page that will display the URLs window.open(`/exclude-pattern/${matchPatternId}/`, '_blank'); }); + + $("body").on("click", ".view-include-pattern-urls", function () { + var matchPatternId = $(this).data("row-id"); + console.log(matchPatternId); + + // Redirect to the new page that will display the URLs + window.open(`/include-pattern/${matchPatternId}/`, '_blank'); + }); + + $("body").on("click", ".view-title-pattern-urls", function () { + var matchPatternId = $(this).data("row-id"); + console.log(matchPatternId); + + // Redirect to the new page that will display the URLs + window.open(`/title-pattern/${matchPatternId}/`, '_blank'); + }); + + $("body").on("click", ".view-document-type-pattern-urls", function () { + var matchPatternId = $(this).data("row-id"); + console.log(matchPatternId); + + // Redirect to the new page that will display the URLs + window.open(`/document-type-pattern/${matchPatternId}/`, '_blank'); + }); } diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 9d00d65d..2198db45 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -9,8 +9,8 @@ {{ block.super }} - + {% endblock stylesheets %} {% block content %} @@ -20,9 +20,10 @@

Affected URLs

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

- +
#URLInclude URL URLInclude URL
{{ forloop.counter }} {{ url.url }} - - + + + {% if url.is_included %} + check + + {% else %} + close + + {% endif %}
@@ -30,32 +31,46 @@

- - + + + + + {% for url in affected_urls %} - - - {% empty %} @@ -65,27 +80,6 @@

# URLInclude URL
{{ forloop.counter }} - {{ url.url }} +
{{ url.url }} + + open_in_new +
- - {% if url.is_included %} - check - - {% else %} - close - +
- - {% endblock content %} {% block javascripts %} @@ -96,26 +90,8 @@

- + - - {% endblock javascripts %} \ No newline at end of file From 7a279fcb16d0194dc4f393e5a3f1c111a81ae1f3 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Wed, 25 Sep 2024 18:56:04 -0500 Subject: [PATCH 06/26] finalise the affected urls page for all the pattern types --- sde_collections/models/candidate_url.py | 8 --- sde_collections/views.py | 67 ++++--------------- .../static/js/affected_urls.js | 32 ++------- .../sde_collections/affected_urls.html | 13 ++-- 4 files changed, 25 insertions(+), 95 deletions(-) diff --git a/sde_collections/models/candidate_url.py b/sde_collections/models/candidate_url.py index 7ae05c38..51c3a28b 100644 --- a/sde_collections/models/candidate_url.py +++ b/sde_collections/models/candidate_url.py @@ -11,14 +11,6 @@ class CandidateURLQuerySet(models.QuerySet): def with_exclusion_status(self): - # currently this is only handling the exclude_patterns - # need to add functionality of including include_patterns - # exclude=false - # if some_include_pattern exists which matches this url: - # then exclude = false - # else - # if some_exclude_pattern exists which matches this url: - # exclude = true return self.annotate( excluded=models.Exists( ExcludePattern.candidate_urls.through.objects.filter(candidateurl=models.OuterRef("pk")) diff --git a/sde_collections/views.py b/sde_collections/views.py index 3d5e6ffb..44fc55cd 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -239,53 +239,21 @@ class AffectedURLsListView(LoginRequiredMixin, ListView): def get_queryset(self): if 'exclude-pattern' in self.request.path: - print("Going with exclude patterns......") - # Get the exclude pattern based on the ID from the URL kwargs self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) - - # Get excluded URLs - excluded_urls = self.pattern.matched_urls().annotate(is_included=Value(False, output_field=BooleanField())) - - # Get included URLs for the same collection - include_patterns = IncludePattern.objects.filter(collection=self.pattern.collection) - included_urls = CandidateURL.objects.filter( - collection=self.pattern.collection, - id__in=include_patterns.values_list('candidate_urls__id', flat=True) - ).distinct() - - # Annotate excluded URLs with is_included if they are also in included_urls - queryset = excluded_urls.annotate( - is_included=Case( - When(id__in=included_urls.values('id'), then=Value(True)), - default=Value(False), - output_field=BooleanField() - ) - ) - + self.pattern_type = "Exclude" elif 'include-pattern' in self.request.path: - print("Going with include patterns......") - # Get the include pattern based on the ID from the URL kwargs self.pattern = IncludePattern.objects.get(id=self.kwargs["id"]) - - # Get included URLs for the pattern - included_urls = self.pattern.matched_urls().annotate(is_excluded=Value(False, output_field=BooleanField())) - - # Get excluded URLs for the same collection - exclude_patterns = ExcludePattern.objects.filter(collection=self.pattern.collection) - excluded_urls = CandidateURL.objects.filter( - collection=self.pattern.collection, - id__in=exclude_patterns.values_list('candidate_urls__id', flat=True) - ).distinct() - - # Annotate included URLs with is_excluded if they are also in excluded_urls - queryset = included_urls.annotate( - is_excluded=Case( - When(id__in=excluded_urls.values('id'), then=Value(True)), - default=Value(False), - output_field=BooleanField() - ) - ) - + self.pattern_type = "Include" + elif 'title-pattern' in self.request.path: + self.pattern = TitlePattern.objects.get(id=self.kwargs["id"]) + self.pattern_type = "Title" + elif 'document-type-pattern' in self.request.path: + self.pattern = DocumentTypePattern.objects.get(id=self.kwargs["id"]) + self.pattern_type = "Document Type" + else: + return super().get_queryset() + + queryset = self.pattern.matched_urls() return queryset def get_context_data(self, **kwargs): @@ -293,19 +261,10 @@ def get_context_data(self, **kwargs): context["pattern"] = self.pattern context["url_count"] = self.pattern.matched_urls().count() context["collection"] = self.pattern.collection - context["pattern_type"] = "Exclude" if 'exclude-pattern' in self.request.path else "Include" - - - # print(self.pattern.collection.id) - - # affected_urls = self.pattern.matched_urls() - # for url in affected_urls: - # print(url.collection) - # Your affected_urls is a list of CandidateURL model objects. + context["pattern_type"] = self.pattern_type return context - class SdeDashboardView(LoginRequiredMixin, ListView): model = Collection template_name = "sde_collections/sde_dashboard.html" diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index cf94d6f6..8ee3acb2 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -4,7 +4,6 @@ var MULTI_URL_PATTERN = 2; collection_id = getCollectionId(); $(document).ready(function () { - // handleAjaxStartAndStop(); initializeDataTable(); setupClickHandlers(); }); @@ -14,7 +13,6 @@ function initializeDataTable() { pageLength: 100, colReorder: true, stateSave: true, - searching: true, layout: { bottomEnd: "inputPaging", topEnd: null, @@ -32,16 +30,6 @@ function initializeDataTable() { pagingType: "input", rowId: "url" }, - // createdRow: function (row, data, dataIndex) { - // // Assuming the 'Include URL' column is at index 3 - // let includeUrlCell = $(row).find('td').eq(2); - - // // Check if the cell has the cross-mark - // if (includeUrlCell.find('i.cross-mark').length > 0) { - // // Highlight the row if it contains a cross-mark - // $(row).css('background-color', 'rgba(255, 61, 87, 0.36)'); // Light red background - // } - // } }) @@ -85,7 +73,6 @@ function handleIncludeIndividualUrlClick() { } else { - // Handle the functionality of excluding that URL again (maybe delete that include pattern which was just created) var url = $(this).attr("value"); console.log("url", url); getCorrespondingIncludePattern(url).then(function(patternId) { @@ -240,21 +227,6 @@ function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { }); } -function deleteRowById(rowId) { - // Find the DataTable instance - var affected_urls_table = $("#affectedURLsTable").DataTable(); - - // Find the row with ID 1 - var rowToDelete = affected_urls_table.row(rowId); // Adjust based on 0-indexing - - if (rowToDelete.length) { - rowToDelete.remove(); // Remove the row - affected_urls_table.draw(); // Redraw the table - } else { - console.log("Row not found."); - } - } - function deletePattern( url, data_type, @@ -273,6 +245,10 @@ function deletePattern( success: function (data) { console.log("Successfully deleted.") }, + error: function (xhr, status, error) { + var errorMessage = xhr.responseText; + toastr.error(errorMessage); + }, }); } diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 2198db45..9071ffa8 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -2,15 +2,15 @@ {% load static i18n %} {% load humanize %} {% block title %} -Affected URLs +Affected URLs for {{pattern_type}} Pattern {% endblock title %} {% block stylesheets %} {{ block.super }} - + {% endblock stylesheets %} {% block content %} @@ -19,6 +19,8 @@

Affected URLs

+
+

{{ url_count|intcomma }} affected URLs for {{ pattern_type | lower }} pattern: {{ pattern.match_pattern }} @@ -26,7 +28,7 @@

- +
@@ -39,7 +41,7 @@

{% endif %} -->

- + @@ -79,7 +81,8 @@

{% endfor %}

#
- +
+

{% endblock content %} {% block javascripts %} From e736d2c6d26fc61cf24a25ea86c307dfde89049c Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 15:13:54 -0500 Subject: [PATCH 07/26] Refactor AffectedURLsListView into pattern-specific views --- sde_collections/urls.py | 8 +++---- sde_collections/views.py | 46 ++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 131efbed..db96c6e0 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -45,22 +45,22 @@ ), path( "exclude-pattern//", - view=views.AffectedURLsListView.as_view(), + view=views.ExcludePatternAffectedURLsListView.as_view(), name="affected_urls", ), path( "include-pattern//", - view=views.AffectedURLsListView.as_view(), + view=views.IncludePatternAffectedURLsListView.as_view(), name="affected_urls", ), path( "title-pattern//", - view=views.AffectedURLsListView.as_view(), + view=views.TitlePatternAffectedURLsListView.as_view(), name="affected_urls", ), path( "document-type-pattern//", - view=views.AffectedURLsListView.as_view(), + view=views.DocumentTypePatternAffectedURLsListView.as_view(), name="affected_urls", ), path( diff --git a/sde_collections/views.py b/sde_collections/views.py index 44fc55cd..38181d21 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -227,44 +227,44 @@ def get_context_data(self, **kwargs): return context -class AffectedURLsListView(LoginRequiredMixin, ListView): +class BaseAffectedURLsListView(LoginRequiredMixin, ListView): """ - Display a list of URLs affected by a match pattern + Base view for displaying a list of URLs affected by a match pattern """ - model = CandidateURL template_name = "sde_collections/affected_urls.html" context_object_name = "affected_urls" + pattern_model = None + pattern_type = None def get_queryset(self): - - if 'exclude-pattern' in self.request.path: - self.pattern = ExcludePattern.objects.get(id=self.kwargs["id"]) - self.pattern_type = "Exclude" - elif 'include-pattern' in self.request.path: - self.pattern = IncludePattern.objects.get(id=self.kwargs["id"]) - self.pattern_type = "Include" - elif 'title-pattern' in self.request.path: - self.pattern = TitlePattern.objects.get(id=self.kwargs["id"]) - self.pattern_type = "Title" - elif 'document-type-pattern' in self.request.path: - self.pattern = DocumentTypePattern.objects.get(id=self.kwargs["id"]) - self.pattern_type = "Document Type" - else: - return super().get_queryset() - - queryset = self.pattern.matched_urls() - return queryset + self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) + return self.pattern.matched_urls() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["pattern"] = self.pattern - context["url_count"] = self.pattern.matched_urls().count() + 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" + +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 SdeDashboardView(LoginRequiredMixin, ListView): model = Collection template_name = "sde_collections/sde_dashboard.html" From 242d41478423d5f2f876b37295e1281e05a3bc1b Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 17:19:59 -0500 Subject: [PATCH 08/26] remove unnecessary comments and format code --- .../static/css/affected_urls.css | 250 ++++++++------- .../static/css/candidate_url_list.css | 223 ++++++++------ .../static/js/affected_urls.js | 291 ++---------------- .../static/js/candidate_url_list.js | 21 +- .../sde_collections/affected_urls.html | 24 -- 5 files changed, 282 insertions(+), 527 deletions(-) diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index 0345d56e..df0dd90c 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -18,11 +18,11 @@ text-decoration-thickness: 1px; } -.dataTables_scrollHead, .dataTables_scrollBody { +.dataTables_scrollHead, +.dataTables_scrollBody { overflow: visible !important; } - .dataTables_wrapper .dataTables_paginate .paginate_button:hover { background: none; border: none; @@ -43,7 +43,7 @@ background: #FFF; color: white; border-radius: 5px; - background-color:#15232E; + background-color: #15232E; } .custom-menu li { @@ -76,29 +76,29 @@ cursor: pointer } -.table_filter_row_input{ +.table_filter_row_input { width: 100%; } - - .select-dropdown { +.select-dropdown { text-align: center; width: 100% !important; - color: #333333;; + color: #333333; background-color: #fafafa; border-radius: 0.2rem; - border-color: #fafafa; + border-color: #fafafa; font-size: 0.6875rem; - box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); } + box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); +} - .select-dropdown:hover { +.select-dropdown:hover { box-shadow: 0 14px 26px -12px rgba(250, 250, 250, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(250, 250, 250, 0.2); - } +} - .select-dropdown:focus, - .select-dropdown.focus { +.select-dropdown:focus, +.select-dropdown.focus { box-shadow: none, 0 0 0 0.2rem rgba(76, 175, 80, 0.5); - } +} /* badge showing workflow status by header */ .badge { @@ -106,8 +106,8 @@ margin-left: 15px; } - -.table_filter_row_input, .doc-dropdown{ +.table_filter_row_input, +.doc-dropdown { width: 100%; } @@ -164,11 +164,12 @@ padding: 40px 30px; border-radius: 15px; } + .modalTitle { -font-size: 24px; -font-weight: 600; -line-height: 36px; -letter-spacing: -0.03em; + font-size: 24px; + font-weight: 600; + line-height: 36px; + letter-spacing: -0.03em; } #hideShowColumnsModal { @@ -181,43 +182,43 @@ letter-spacing: -0.03em; z-index: 2000; } -#caption, #subTitle { -font-size: 14px; -font-weight: 400; -line-height: 21px; -letter-spacing: -0.02em; +#caption, +#subTitle { + font-size: 14px; + font-weight: 400; + line-height: 21px; + letter-spacing: -0.02em; } - - - .checkbox-wrapper { +.checkbox-wrapper { display: flex; align-items: baseline; - } +} - .checkbox-wrapper label { +.checkbox-wrapper label { font-weight: 600; font-size: 16px; line-height: 24px; margin-bottom: 0; color: rgba(31, 41, 53, 1); padding-left: 10px; - } +} - .modalFooter { +.modalFooter { position: sticky; bottom: 0; position: sticky; bottom: 0; padding: 10px 0; background: #FFFFFF; - } -.badge{ +} + +.badge { background-color: #FF3D57; } -.notifyBadge{ - margin-left:5px !important; +.notifyBadge { + margin-left: 5px !important; } .sorting_1 { @@ -227,106 +228,113 @@ letter-spacing: -0.02em; max-width: 600px; width: 600px; color: #65B1EF; - } +} .title-dropdown { width: fit-content !important; - margin-top:20px; - margin-bottom:20px; + margin-top: 20px; + margin-bottom: 20px; } + .table tbody tr:nth-child(odd) { background-color: #050E19 !important; - } +} - .table tbody tr:nth-child(even) { +.table tbody tr:nth-child(even) { background-color: #3F4A58 !important; - } - .candidateTitle{ - font-size:24px; - font-weight: 500; - } - +} +.candidateTitle { + font-size: 24px; + font-weight: 500; +} - .custom-select, .buttons-csv, .customizeColumns, .addPattern{ +.custom-select, +.buttons-csv, +.customizeColumns, +.addPattern { border-style: solid !important; border-color: #A7BACD !important; border-width: 1px !important; - color:#A7BACD !important; + color: #A7BACD !important; border-radius: 5px !important; padding: 11px 15px; - } +} - .addPattern { +.addPattern { background-color: #0066CA !important; border-color: #0066CA !important; color: #fff !important; - } +} - #exclude_patterns_table_wrapper .dt-buttons, #include_patterns_table_wrapper .dt-buttons, #document_type_patterns_table_wrapper .dt-buttons, #title_patterns_table_wrapper .dt-buttons { +#exclude_patterns_table_wrapper .dt-buttons, +#include_patterns_table_wrapper .dt-buttons, +#document_type_patterns_table_wrapper .dt-buttons, +#title_patterns_table_wrapper .dt-buttons { width: 89%; justify-content: end; - } +} - .customizeColumns { +.customizeColumns { margin-left: 10px !important; - } +} - .form-control:read-only { +.form-control:read-only { background-image: none; - } +} - .dt-container div.dt-length label { +.dt-container div.dt-length label { display: none; - } +} - div.dt-container div.dt-info { +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 { + color: white !important; + border: 0.5px solid !important; + margin-left: 3px; + margin-right: 3px; } -.page-link:hover{ - background-color: #0066CA !important; +.page-link:hover { + background-color: #0066CA !important; } .page-item.disabled .page-link { - color:grey!important; + color: grey !important; } -.dt-paging-input{ - color:white; + +.dt-paging-input { + color: white; } -.dt-paging-input input{ +.dt-paging-input input { background-color: #3F4A58; color: white; - border:solid 0.5px !important; + border: solid 0.5px !important; } -.dt-inputpaging{ - position: absolute; - right: 16px; - top: -27px; +.dt-inputpaging { + position: absolute; + right: 16px; + top: -27px; } -.ml-auto{ - width:50%; + +.ml-auto { + width: 50%; } -.custom-select-sm{ - margin-left:5px; +.custom-select-sm { + margin-left: 5px; } -.selected{ +.selected { background-color: inherit !important; } - div.dt-buttons .btn.processing:after { border: 2px solid #FFFFFF !important; border-left-color: transparent !important; @@ -334,26 +342,28 @@ div.dt-buttons .btn.processing:after { -webkit-animation: dtb-spinner 1500ms infinite linear; } -.document_type_dropdown, .division_dropdown, .dropdown-toggle { +.document_type_dropdown, +.division_dropdown, +.dropdown-toggle { width: 100%; display: flex; justify-content: center; } - .dropdown-toggle { +.dropdown-toggle { width: 80%; /* display: flex; */ align-items: center; /* justify-content: space-between; */ - } +} -.headerDiv{ +.headerDiv { display: flex; justify-content: space-between; } .url-cell { - display:flex; + display: flex; align-items: center; justify-content: space-between; word-wrap: break-word; @@ -362,17 +372,19 @@ div.dt-buttons .btn.processing:after { overflow-wrap: break-word; min-width: 100%; max-width: 100%; - } +} - .url-icon { +.url-icon { color: #65B1EF; - } -#match_pattern_input, #title_pattern_input { +} + +#match_pattern_input, +#title_pattern_input { background: #3F4A58; border-radius: 4px; } -.modal-body .bmd-label-static { +.modal-body .bmd-label-static { top: -20px !important; } @@ -396,26 +408,26 @@ div.dt-buttons .btn.processing:after { margin-top: 40px; } -.is-focused [class^='bmd-label']{ - color:#0066CA; - } - .form-control{ - color:white; - } +.is-focused [class^='bmd-label'] { + color: #0066CA; +} - .form-control:focus{ - color:white; - } +.form-control { + color: white; +} - .is-focused .form-label{ - background-image:linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); - color:#AAAAAA; - } +.form-control:focus { + color: white; +} - .dropdown-item:hover{ - background-color: #0066CA !important; - } +.is-focused .form-label { + background-image: linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); + color: #AAAAAA; +} +.dropdown-item:hover { + background-color: #0066CA !important; +} /* pagination position */ div.dt-container div.dt-paging ul.pagination { @@ -429,7 +441,6 @@ div.dt-container div.dt-paging ul.pagination { min-width: 100%; } - /* Dark theme adjustments later added */ body { background-color: #2c2c2c; @@ -439,27 +450,36 @@ body { .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 */ + color: #61dafb; + /* URL links */ } + .table tbody tr td a:hover { - color: #f5f5f5; /* Hover effect for links */ + color: #f5f5f5; + /* Hover effect for links */ } + .alert-info { background-color: #444; color: #f5f5f5; } + .list-group-item { background-color: #333; color: #f5f5f5; @@ -481,6 +501,8 @@ body { color: green; } -.cross-mark, .tick-mark { - cursor: pointer; /* Show hand cursor */ +.cross-mark, +.tick-mark { + cursor: pointer; + /* Show hand cursor */ } \ No newline at end of file diff --git a/sde_indexing_helper/static/css/candidate_url_list.css b/sde_indexing_helper/static/css/candidate_url_list.css index aa2d5d18..cfd1876c 100644 --- a/sde_indexing_helper/static/css/candidate_url_list.css +++ b/sde_indexing_helper/static/css/candidate_url_list.css @@ -18,7 +18,8 @@ text-decoration-thickness: 1px; } -.dataTables_scrollHead, .dataTables_scrollBody { +.dataTables_scrollHead, +.dataTables_scrollBody { overflow: visible !important; } @@ -43,7 +44,7 @@ background: #FFF; color: white; border-radius: 5px; - background-color:#15232E; + background-color: #15232E; } .custom-menu li { @@ -76,29 +77,31 @@ cursor: pointer } -.table_filter_row_input{ +.table_filter_row_input { width: 100%; } - .select-dropdown { +.select-dropdown { text-align: center; width: 100% !important; - color: #333333;; + color: #333333; + ; background-color: #fafafa; border-radius: 0.2rem; - border-color: #fafafa; + border-color: #fafafa; font-size: 0.6875rem; - box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); } + box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); +} - .select-dropdown:hover { +.select-dropdown:hover { box-shadow: 0 14px 26px -12px rgba(250, 250, 250, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(250, 250, 250, 0.2); - } +} - .select-dropdown:focus, - .select-dropdown.focus { +.select-dropdown:focus, +.select-dropdown.focus { box-shadow: none, 0 0 0 0.2rem rgba(76, 175, 80, 0.5); - } +} /* badge showing workflow status by header */ .badge { @@ -107,7 +110,8 @@ } -.table_filter_row_input, .doc-dropdown{ +.table_filter_row_input, +.doc-dropdown { width: 100%; } @@ -164,11 +168,12 @@ padding: 40px 30px; border-radius: 15px; } + .modalTitle { -font-size: 24px; -font-weight: 600; -line-height: 36px; -letter-spacing: -0.03em; + font-size: 24px; + font-weight: 600; + line-height: 36px; + letter-spacing: -0.03em; } #hideShowColumnsModal { @@ -181,43 +186,45 @@ letter-spacing: -0.03em; z-index: 2000; } -#caption, #subTitle { -font-size: 14px; -font-weight: 400; -line-height: 21px; -letter-spacing: -0.02em; +#caption, +#subTitle { + font-size: 14px; + font-weight: 400; + line-height: 21px; + letter-spacing: -0.02em; } - .checkbox-wrapper { +.checkbox-wrapper { display: flex; align-items: baseline; - } +} - .checkbox-wrapper label { +.checkbox-wrapper label { font-weight: 600; font-size: 16px; line-height: 24px; margin-bottom: 0; color: rgba(31, 41, 53, 1); padding-left: 10px; - } +} - .modalFooter { +.modalFooter { position: sticky; bottom: 0; position: sticky; bottom: 0; padding: 10px 0; background: #FFFFFF; - } -.badge{ +} + +.badge { background-color: #FF3D57; } -.notifyBadge{ - margin-left:5px !important; +.notifyBadge { + margin-left: 5px !important; } .sorting_1 { @@ -227,102 +234,113 @@ letter-spacing: -0.02em; max-width: 600px; width: 600px; color: #65B1EF; - } +} .title-dropdown { width: fit-content !important; - margin-top:20px; - margin-bottom:20px; + margin-top: 20px; + margin-bottom: 20px; } + .table tbody tr:nth-child(odd) { background-color: #050E19 !important; - } +} - .table tbody tr:nth-child(even) { +.table tbody tr:nth-child(even) { background-color: #3F4A58 !important; - } - .candidateTitle{ - font-size:24px; +} + +.candidateTitle { + font-size: 24px; font-weight: 500; - } +} - .custom-select, .buttons-csv, .customizeColumns, .addPattern{ +.custom-select, +.buttons-csv, +.customizeColumns, +.addPattern { border-style: solid !important; border-color: #A7BACD !important; border-width: 1px !important; - color:#A7BACD !important; + color: #A7BACD !important; border-radius: 5px !important; padding: 11px 15px; - } +} - .addPattern { +.addPattern { background-color: #0066CA !important; border-color: #0066CA !important; color: #fff !important; - } +} - #exclude_patterns_table_wrapper .dt-buttons, #include_patterns_table_wrapper .dt-buttons, #document_type_patterns_table_wrapper .dt-buttons, #title_patterns_table_wrapper .dt-buttons { +#exclude_patterns_table_wrapper .dt-buttons, +#include_patterns_table_wrapper .dt-buttons, +#document_type_patterns_table_wrapper .dt-buttons, +#title_patterns_table_wrapper .dt-buttons { width: 89%; justify-content: end; - } +} - .customizeColumns { +.customizeColumns { margin-left: 10px !important; - } +} - .form-control:read-only { +.form-control:read-only { background-image: none; - } +} - .dt-container div.dt-length label { +.dt-container div.dt-length label { display: none; - } +} - div.dt-container div.dt-info { +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 { + color: white !important; + border: 0.5px solid !important; + margin-left: 3px; + margin-right: 3px; } -.page-link:hover{ + +.page-link:hover { background-color: #0066CA !important; } .page-item.disabled .page-link { - color:grey!important; + color: grey !important; } -.dt-paging-input{ - color:white; + +.dt-paging-input { + color: white; } -.dt-paging-input input{ +.dt-paging-input input { background-color: #3F4A58; color: white; - border:solid 0.5px !important; + border: solid 0.5px !important; } -.dt-inputpaging{ - position: absolute; - right: 16px; - top: -27px; +.dt-inputpaging { + position: absolute; + right: 16px; + top: -27px; } -.ml-auto{ - width:50%; + +.ml-auto { + width: 50%; } -.custom-select-sm{ - margin-left:5px; +.custom-select-sm { + margin-left: 5px; } -.selected{ +.selected { background-color: inherit !important; } @@ -334,26 +352,28 @@ div.dt-buttons .btn.processing:after { -webkit-animation: dtb-spinner 1500ms infinite linear; } -.document_type_dropdown, .division_dropdown, .dropdown-toggle { +.document_type_dropdown, +.division_dropdown, +.dropdown-toggle { width: 100%; display: flex; justify-content: center; } - .dropdown-toggle { +.dropdown-toggle { width: 80%; /* display: flex; */ align-items: center; /* justify-content: space-between; */ - } +} -.headerDiv{ +.headerDiv { display: flex; justify-content: space-between; } .url-cell { - display:flex; + display: flex; align-items: center; justify-content: space-between; word-wrap: break-word; @@ -362,17 +382,19 @@ div.dt-buttons .btn.processing:after { overflow-wrap: break-word; min-width: 100%; max-width: 100%; - } +} - .url-icon { +.url-icon { color: #65B1EF; - } -#match_pattern_input, #title_pattern_input { +} + +#match_pattern_input, +#title_pattern_input { background: #3F4A58; border-radius: 4px; } -.modal-body .bmd-label-static { +.modal-body .bmd-label-static { top: -20px !important; } @@ -396,25 +418,26 @@ div.dt-buttons .btn.processing:after { margin-top: 40px; } -.is-focused [class^='bmd-label']{ - color:#0066CA; - } - .form-control{ - color:white; - } +.is-focused [class^='bmd-label'] { + color: #0066CA; +} - .form-control:focus{ - color:white; - } +.form-control { + color: white; +} - .is-focused .form-label{ - background-image:linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); - color:#AAAAAA; - } +.form-control:focus { + color: white; +} - .dropdown-item:hover{ +.is-focused .form-label { + background-image: linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); + color: #AAAAAA; +} + +.dropdown-item:hover { background-color: #0066CA !important; - } +} /* pagination position */ @@ -427,4 +450,4 @@ div.dt-container div.dt-paging ul.pagination { width: 100%; max-width: 100%; min-width: 100%; -} +} \ No newline at end of file diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 8ee3acb2..39d0438b 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -4,290 +4,37 @@ var MULTI_URL_PATTERN = 2; collection_id = getCollectionId(); $(document).ready(function () { - initializeDataTable(); - setupClickHandlers(); + initializeDataTable(); }); function initializeDataTable() { var affected_urls_table = $("#affectedURLsTable").DataTable({ - pageLength: 100, - colReorder: true, - stateSave: true, - layout: { - bottomEnd: "inputPaging", - topEnd: null, - topStart: { - info: true, - pageLength: { - menu: [ - [25, 50, 100, 500], - ["Show 25", "Show 50", "Show 100", "Show 500"], - ], - }, + pageLength: 100, + colReorder: true, + stateSave: true, + layout: { + bottomEnd: "inputPaging", + topEnd: null, + topStart: { + info: true, + pageLength: { + menu: [ + [25, 50, 100, 500], + ["Show 25", "Show 50", "Show 100", "Show 500"], + ], + }, }, serverSide: true, orderCellsTop: true, pagingType: "input", - rowId: "url" - }, - - }) + rowId: "url", + }, + }); $("#affectedURLsFilter").on( "beforeinput", DataTable.util.debounce(function (val) { - affected_urls_table.columns(1).search(this.value).draw(); + affected_urls_table.columns(1).search(this.value).draw(); }, 1000) ); } - -function setupClickHandlers() { - handleIncludeIndividualUrlClick(); - handleExcludeIndividualUrlClick(); - } - -function handleIncludeIndividualUrlClick() { - $("body").on("click", ".include-url-btn", function () { - - const i = this.querySelector('i'); - if (i.classList.contains('cross-mark')) { - // Change to tick mark - i.classList.remove('cross-mark'); - i.classList.add('tick-mark'); // Add the tick-mark class - i.style.color = 'green'; // Change color to green - i.textContent = 'check'; // Set the text to "check" - let parentCol3 = i.closest('.col-3'); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute('data-sort', '1'); // Set data-sort to '1' for the check-mark - } - - match_pattern = remove_protocol($(this).attr("value")) ; - match_pattern_type = INDIVIDUAL_URL; - console.log(match_pattern); - postIncludePatterns( - match_pattern = match_pattern, - match_pattern_type = match_pattern_type, - true - ); - - - } else { - var url = $(this).attr("value"); - console.log("url", url); - getCorrespondingIncludePattern(url).then(function(patternId) { - if (patternId !== null) { - console.log('Pattern ID:', patternId); - currentURLtoDelete = `/api/include-patterns/${patternId}/`; - deletePattern( - currentURLtoDelete, - (data_type = "Include Pattern") - ); - - // Change back to cross mark - i.classList.remove('tick-mark'); - i.classList.add('cross-mark'); // Add the cross-mark class - i.style.color = 'red'; // Change color to red - i.textContent = 'close'; // Set the text to "close" - let parentCol3 = i.closest('.col-3'); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute('data-sort', '0'); // Set data-sort to '0' for the cross-mark - } - - - console.log("URL removed from the include pattern") - - - } else { - console.log('No matching pattern found.'); - } - }).catch(function(error) { - console.error("Error occurred:", error); - }); - - } - - }); - } - - - function handleExcludeIndividualUrlClick() { - $("body").on("click", ".exclude-url-btn", function () { - - const i = this.querySelector('i'); - if (i.classList.contains('cross-mark')) { - // Change to tick mark - i.classList.remove('cross-mark'); - i.classList.add('tick-mark'); // Add the tick-mark class - i.style.color = 'green'; // Change color to green - i.textContent = 'check'; // Set the text to "check" - let parentCol3 = i.closest('.col-3'); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute('data-sort', '1'); // Set data-sort to '1' for the check-mark - } - - match_pattern = remove_protocol($(this).attr("value")) ; - match_pattern_type = INDIVIDUAL_URL; - console.log(match_pattern); - postExcludePatterns( - match_pattern = match_pattern, - match_pattern_type = match_pattern_type, - true - ); - - - } else { - // Handle the functionality of including that URL again (maybe delete that exclude pattern which was just created) - var url = $(this).attr("value"); - console.log("url", url); - getCorrespondingExcludePattern(url).then(function(patternId) { - if (patternId !== null) { - console.log('Pattern ID:', patternId); - currentURLtoDelete = `/api/exclude-patterns/${patternId}/`; - deletePattern( - currentURLtoDelete, - (data_type = "Exclude Pattern") - ); - - // Change back to cross mark - i.classList.remove('tick-mark'); - i.classList.add('cross-mark'); // Add the cross-mark class - i.style.color = 'red'; // Change color to red - i.textContent = 'close'; // Set the text to "close" - let parentCol3 = i.closest('.col-3'); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute('data-sort', '0'); // Set data-sort to '0' for the cross-mark - } - - console.log("URL removed from the exclude pattern") - - - } else { - console.log('No matching pattern found.'); - } - }).catch(function(error) { - console.error("Error occurred:", error); - }); - - } - - }); - } - -function postIncludePatterns(match_pattern, match_pattern_type = 0) { - - $.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) { - console.log("Success on adding to the Included URLs"); - }, - error: function (xhr, status, error) { - var errorMessage = xhr.responseText; - toastr.error(errorMessage); - }, - }); -} - -function getCollectionId() { - return collection_id; -} - -function remove_protocol(url) { - return url.replace(/(^\w+:|^)\/\//, ""); - } - - -function postExcludePatterns(match_pattern, match_pattern_type = 0, force) { - $.ajax({ - url: "/api/exclude-patterns/", - type: "POST", - data: { - collection: collection_id, - match_pattern: match_pattern, - match_pattern_type: match_pattern_type, - csrfmiddlewaretoken: csrftoken, - }, - success: function (data) { - console.log("Success on adding to the Excluded URLs"); - }, - error: function (xhr, status, error) { - var errorMessage = xhr.responseText; - toastr.error(errorMessage); - }, - }); -} - -function deletePattern( - url, - data_type, - url_type = null, - candidate_urls_count = null -) { - $.ajax({ - url: url, - type: "DELETE", - data: { - csrfmiddlewaretoken: csrftoken, - }, - headers: { - "X-CSRFToken": csrftoken, - }, - success: function (data) { - console.log("Successfully deleted.") - }, - error: function (xhr, status, error) { - var errorMessage = xhr.responseText; - toastr.error(errorMessage); - }, - }); -} - -function getCorrespondingIncludePattern(url) { - return $.ajax({ - url: `/api/include-patterns/?format=datatables&collection_id=${collection_id}`, - method: 'GET', - dataType: 'json' - }).then(function(response) { - // Iterate through the 'data' array to find a matching pattern - for (let i = 0; i < response.data.length; i++) { - let pattern = response.data[i].match_pattern; - if ( pattern === remove_protocol(url)) { - return response.data[i].id; // Return the first matching pattern id - } - } - return null; // Return null if no pattern matches - }).catch(function(error) { - console.error("Error fetching include patterns:", error); - return null; - }); -} - -function getCorrespondingExcludePattern(url) { - return $.ajax({ - url: `/api/exclude-patterns/?format=datatables&collection_id=${collection_id}`, - method: 'GET', - dataType: 'json' - }).then(function(response) { - // Iterate through the 'data' array to find a matching pattern - for (let i = 0; i < response.data.length; i++) { - let pattern = response.data[i].match_pattern; - if ( pattern === remove_protocol(url)) { - return response.data[i].id; // Return the first matching pattern id - } - } - return null; // Return null if no pattern matches - }).catch(function(error) { - console.error("Error fetching exclude patterns:", error); - return null; - }); -} \ No newline at end of file diff --git a/sde_indexing_helper/static/js/candidate_url_list.js b/sde_indexing_helper/static/js/candidate_url_list.js index 88e3fc93..65f27173 100644 --- a/sde_indexing_helper/static/js/candidate_url_list.js +++ b/sde_indexing_helper/static/js/candidate_url_list.js @@ -1163,38 +1163,25 @@ function handleExcludeIndividualUrlClick() { function handleShowAffectedURLsListButtonClick() { $("body").on("click", ".view-exclude-pattern-urls", function () { var matchPatternId = $(this).data("row-id"); - console.log(matchPatternId); - - // Redirect to the new page that will display the URLs - window.open(`/exclude-pattern/${matchPatternId}/`, '_blank'); + window.open(`/exclude-pattern/${matchPatternId}/`, '_blank'); }); $("body").on("click", ".view-include-pattern-urls", function () { var matchPatternId = $(this).data("row-id"); - console.log(matchPatternId); - - // Redirect to the new page that will display the URLs - window.open(`/include-pattern/${matchPatternId}/`, '_blank'); + window.open(`/include-pattern/${matchPatternId}/`, '_blank'); }); $("body").on("click", ".view-title-pattern-urls", function () { var matchPatternId = $(this).data("row-id"); - console.log(matchPatternId); - - // Redirect to the new page that will display the URLs - window.open(`/title-pattern/${matchPatternId}/`, '_blank'); + window.open(`/title-pattern/${matchPatternId}/`, '_blank'); }); $("body").on("click", ".view-document-type-pattern-urls", function () { var matchPatternId = $(this).data("row-id"); - console.log(matchPatternId); - - // Redirect to the new page that will display the URLs - window.open(`/document-type-pattern/${matchPatternId}/`, '_blank'); + window.open(`/document-type-pattern/${matchPatternId}/`, '_blank'); }); } - function handleDeleteExcludePatternButtonClick() { $("body").on("click", ".delete-exclude-pattern-button", function () { var patternRowId = $(this).data("row-id"); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 9071ffa8..c49b6088 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -33,13 +33,6 @@

# URL - @@ -56,23 +49,6 @@

open_in_new

- {% empty %} From 32ed7f86f1c71b84965eea9df052550e1fe4d857 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 17:40:17 -0500 Subject: [PATCH 09/26] add integrity check to contents loaded from CDN --- .../static/js/candidate_url_list.js | 64 ++++++++++++------- .../sde_collections/affected_urls.html | 10 +-- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/sde_indexing_helper/static/js/candidate_url_list.js b/sde_indexing_helper/static/js/candidate_url_list.js index 65f27173..39b764f2 100644 --- a/sde_indexing_helper/static/js/candidate_url_list.js +++ b/sde_indexing_helper/static/js/candidate_url_list.js @@ -388,12 +388,16 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return `
- ${data} - -
`; + return ` +
+ + ${data} + + +
+ `; }, }, { @@ -476,12 +480,16 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return `
- ${data} - -
`; + return ` +
+ + ${data} + + +
+ `; }, }, { @@ -564,12 +572,16 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return `
- ${data} - -
`; + return ` +
+ + ${data} + + +
+ `; }, }, { @@ -684,12 +696,16 @@ function initializeDataTable() { class: "text-center whiteText", sortable: true, render: function (data, type, row) { - return `
- ${data} - -
`; + return ` +
+ + ${data} + + +
+ `; }, }, diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index c49b6088..47a8f48a 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -64,11 +64,11 @@

{% block javascripts %} {{ block.super }} - - - - - + + + + + From 74e074aa60de2127dd0f950800355f2406a40d47 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 18:35:52 -0500 Subject: [PATCH 10/26] remove unnecessary css declarations --- .../static/css/affected_urls.css | 331 ------------------ .../static/js/affected_urls.js | 4 + 2 files changed, 4 insertions(+), 331 deletions(-) diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index df0dd90c..435089bc 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -1,23 +1,3 @@ -#top_pattern_bar { - position: sticky; - top: 0 -} - -.done_color { - color: green; -} - -.close_color { - color: red; -} - -#test_url { - text-decoration: underline; - text-decoration-style: dotted; - text-decoration-line: underline; - text-decoration-thickness: 1px; -} - .dataTables_scrollHead, .dataTables_scrollBody { overflow: visible !important; @@ -32,34 +12,6 @@ padding: 0em; } -.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; -} - -.pattern_type { - /* padding-top: 10px; - padding-bottom: 10px; */ -} - .dataTables_wrapper .dataTables_paginate .paginate_button { padding: .5em; } @@ -68,174 +20,16 @@ width: 15%; } -.exclude_individual_url { - cursor: pointer -} - -.document_type_form_select { - cursor: pointer -} - .table_filter_row_input { width: 100%; } -.select-dropdown { - text-align: center; - width: 100% !important; - color: #333333; - background-color: #fafafa; - border-radius: 0.2rem; - border-color: #fafafa; - font-size: 0.6875rem; - box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); -} - -.select-dropdown:hover { - box-shadow: 0 14px 26px -12px rgba(250, 250, 250, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(250, 250, 250, 0.2); -} - -.select-dropdown:focus, -.select-dropdown.focus { - box-shadow: none, 0 0 0 0.2rem rgba(76, 175, 80, 0.5); -} - -/* badge showing workflow status by header */ -.badge { - cursor: default !important; - margin-left: 15px; -} - -.table_filter_row_input, -.doc-dropdown { - width: 100%; -} - -.doc-dropdown { - background: #A7BACD !important; - font-size: 15px; - font-weight: 500; - line-height: 17.58px; - color: #1F2935; - display: flex; - justify-content: space-between; - align-items: center; - text-transform: capitalize; - border-radius: 4px; - margin-bottom: 0; -} - -.doc-dropdown-input { - flex-direction: column; - width: 100%; -} - -.dropdown-item:hover { - background-color: #0066CA; -} - -.doc-type-form { - width: 100%; - background: #15232E; - color: white; - border: 1px solid white; - padding: 24px 15px; - border-radius: 4px; -} - -.doc-type-form a { - height: 24px; - padding: 0; -} - -.document_type_form_select { - font-size: 16px; - font-weight: 600; - letter-spacing: -0.02em; - height: 37px !important; - align-items: center; - display: flex; - padding-left: 10px !important; - border-radius: 1px !important; -} - .candidateUrlContainer { background: #15232E; padding: 40px 30px; border-radius: 15px; } -.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; -} - -#caption, -#subTitle { - font-size: 14px; - font-weight: 400; - line-height: 21px; - letter-spacing: -0.02em; -} - -.checkbox-wrapper { - display: flex; - align-items: baseline; -} - -.checkbox-wrapper label { - font-weight: 600; - font-size: 16px; - line-height: 24px; - margin-bottom: 0; - color: rgba(31, 41, 53, 1); - padding-left: 10px; -} - -.modalFooter { - position: sticky; - bottom: 0; - position: sticky; - bottom: 0; - padding: 10px 0; - background: #FFFFFF; -} - -.badge { - background-color: #FF3D57; -} - -.notifyBadge { - margin-left: 5px !important; -} - -.sorting_1 { - overflow: visible; - white-space: normal; - word-wrap: break-word; - max-width: 600px; - width: 600px; - color: #65B1EF; -} - -.title-dropdown { - width: fit-content !important; - margin-top: 20px; - margin-bottom: 20px; -} - .table tbody tr:nth-child(odd) { background-color: #050E19 !important; } @@ -261,20 +55,6 @@ padding: 11px 15px; } -.addPattern { - background-color: #0066CA !important; - border-color: #0066CA !important; - color: #fff !important; -} - -#exclude_patterns_table_wrapper .dt-buttons, -#include_patterns_table_wrapper .dt-buttons, -#document_type_patterns_table_wrapper .dt-buttons, -#title_patterns_table_wrapper .dt-buttons { - width: 89%; - justify-content: end; -} - .customizeColumns { margin-left: 10px !important; } @@ -335,28 +115,6 @@ div.dt-container div.dt-info { background-color: inherit !important; } -div.dt-buttons .btn.processing:after { - border: 2px solid #FFFFFF !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - -webkit-animation: dtb-spinner 1500ms infinite linear; -} - -.document_type_dropdown, -.division_dropdown, -.dropdown-toggle { - width: 100%; - display: flex; - justify-content: center; -} - -.dropdown-toggle { - width: 80%; - /* display: flex; */ - align-items: center; - /* justify-content: space-between; */ -} - .headerDiv { display: flex; justify-content: space-between; @@ -378,69 +136,12 @@ div.dt-buttons .btn.processing:after { color: #65B1EF; } -#match_pattern_input, -#title_pattern_input { - background: #3F4A58; - border-radius: 4px; -} - -.modal-body .bmd-label-static { - top: -20px !important; -} - -.modal-header { - margin-bottom: 40px; -} - -.form-label { - color: white; - display: flex; - font-size: 12px; - font-weight: 500; - letter-spacing: -0.02em; -} - -.asterik { - color: #C3001A; -} - -.title_pattern-form-group { - margin-top: 40px; -} - -.is-focused [class^='bmd-label'] { - color: #0066CA; -} - -.form-control { - color: white; -} - -.form-control:focus { - color: white; -} - -.is-focused .form-label { - background-image: linear-gradient(to top, #0066CA 2px, rgba(156, 39, 176, 0) 2px), linear-gradient(to top, #d2d2d2 1px, rgba(210, 210, 210, 0) 1px); - color: #AAAAAA; -} - -.dropdown-item:hover { - background-color: #0066CA !important; -} - /* pagination position */ div.dt-container div.dt-paging ul.pagination { position: absolute; right: 60px; } -.individual_title_input { - width: 100%; - max-width: 100%; - min-width: 100%; -} - /* Dark theme adjustments later added */ body { background-color: #2c2c2c; @@ -473,36 +174,4 @@ body { .table tbody tr td a:hover { color: #f5f5f5; /* Hover effect for links */ -} - -.alert-info { - background-color: #444; - color: #f5f5f5; -} - -.list-group-item { - background-color: #333; - color: #f5f5f5; -} - -/* Optional styles for the Include URL button */ -.include-url-btn { - background-color: transparent; - border: none; - font-size: 1.5rem; - cursor: pointer; -} - -.include-url-btn .cross-mark { - color: red; -} - -.include-url-btn .tick-mark { - color: green; -} - -.cross-mark, -.tick-mark { - cursor: pointer; - /* Show hand cursor */ } \ No newline at end of file diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 39d0438b..f6a8c8a2 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -38,3 +38,7 @@ function initializeDataTable() { }, 1000) ); } + +function getCollectionId() { + return collection_id; +} From dad63b04f69dfe348fbb334de504a04c13ee7f95 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 19:05:51 -0500 Subject: [PATCH 11/26] remove ordering functionality from second row of the datatable --- sde_indexing_helper/static/js/affected_urls.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index f6a8c8a2..dd0c6944 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -29,6 +29,11 @@ function initializeDataTable() { pagingType: "input", rowId: "url", }, + columnDefs: [ + { orderable: true, targets: "_all" }, + { orderable: false, targets: "filter-row" }, + ], + orderCellsTop: true, }); $("#affectedURLsFilter").on( From 11959fc2d030f29c3555acb5dbde0a91b998450c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:42:36 +0000 Subject: [PATCH 12/26] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- sde_collections/views.py | 11 +++++++++-- sde_indexing_helper/static/css/affected_urls.css | 2 +- .../static/css/candidate_url_list.css | 2 +- .../templates/sde_collections/affected_urls.html | 14 +++++++------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/sde_collections/views.py b/sde_collections/views.py index 38181d21..59c6546e 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -51,7 +51,7 @@ User = get_user_model() -from django.db.models import BooleanField, Case, Value, When, Q +from django.db.models import BooleanField, Case, Q, Value, When class CollectionListView(LoginRequiredMixin, ListView): @@ -227,10 +227,12 @@ def get_context_data(self, **kwargs): return context + class BaseAffectedURLsListView(LoginRequiredMixin, ListView): """ Base view for displaying a list of URLs affected by a match pattern """ + model = CandidateURL template_name = "sde_collections/affected_urls.html" context_object_name = "affected_urls" @@ -249,22 +251,27 @@ def get_context_data(self, **kwargs): context["pattern_type"] = self.pattern_type return context + class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): pattern_model = ExcludePattern pattern_type = "Exclude" + 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 SdeDashboardView(LoginRequiredMixin, ListView): model = Collection template_name = "sde_collections/sde_dashboard.html" diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index 435089bc..544acfbf 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -174,4 +174,4 @@ body { .table tbody tr td a:hover { color: #f5f5f5; /* Hover effect for links */ -} \ No newline at end of file +} diff --git a/sde_indexing_helper/static/css/candidate_url_list.css b/sde_indexing_helper/static/css/candidate_url_list.css index cfd1876c..9ef6a240 100644 --- a/sde_indexing_helper/static/css/candidate_url_list.css +++ b/sde_indexing_helper/static/css/candidate_url_list.css @@ -450,4 +450,4 @@ div.dt-container div.dt-paging ul.pagination { width: 100%; max-width: 100%; min-width: 100%; -} \ No newline at end of file +} diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 47a8f48a..f257ee33 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -22,10 +22,10 @@

Affected URLs

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

- +
@@ -45,10 +45,10 @@

+ {% empty %} @@ -66,11 +66,11 @@

- + -{% endblock javascripts %} \ No newline at end of file +{% endblock javascripts %} From e68b50e8c85ab48ba355f9dbae900afa5dcec900 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 26 Sep 2024 19:59:22 -0500 Subject: [PATCH 13/26] fix issues shown by flake8 --- sde_collections/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sde_collections/views.py b/sde_collections/views.py index 59c6546e..72b13bb3 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -51,9 +51,6 @@ User = get_user_model() -from django.db.models import BooleanField, Case, Q, Value, When - - class CollectionListView(LoginRequiredMixin, ListView): """ Display a list of collections in the system From c545b8ce27b4b1b03437f44497a481255f803d9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:59:40 +0000 Subject: [PATCH 14/26] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- sde_collections/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sde_collections/views.py b/sde_collections/views.py index 72b13bb3..b0be1213 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -51,6 +51,7 @@ User = get_user_model() + class CollectionListView(LoginRequiredMixin, ListView): """ Display a list of collections in the system From 4205c1d9659f1308592a1de77740d976840bcb3a Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Sat, 28 Sep 2024 14:48:07 -0500 Subject: [PATCH 15/26] added include-pattern functionality; fixed candidate-urls-api (inclusion added) --- sde_collections/models/candidate_url.py | 9 +- sde_collections/views.py | 28 +- .../static/css/affected_urls.css | 64 ++++ .../static/js/affected_urls.js | 316 ++++++++++++++++++ .../sde_collections/affected_urls.html | 48 ++- 5 files changed, 458 insertions(+), 7 deletions(-) diff --git a/sde_collections/models/candidate_url.py b/sde_collections/models/candidate_url.py index 51c3a28b..3461e11d 100644 --- a/sde_collections/models/candidate_url.py +++ b/sde_collections/models/candidate_url.py @@ -6,21 +6,24 @@ from .collection import Collection from .collection_choice_fields import Divisions, DocumentTypes -from .pattern import ExcludePattern, TitlePattern +from .pattern import ExcludePattern, IncludePattern, TitlePattern class CandidateURLQuerySet(models.QuerySet): - def with_exclusion_status(self): + def with_exclusion_and_inclusion_status(self): return self.annotate( excluded=models.Exists( ExcludePattern.candidate_urls.through.objects.filter(candidateurl=models.OuterRef("pk")) + ), + included=models.Exists( + IncludePattern.candidate_urls.through.objects.filter(candidateurl=models.OuterRef("pk")) ) ) class CandidateURLManager(models.Manager): def get_queryset(self): - return CandidateURLQuerySet(self.model, using=self._db).with_exclusion_status() + return CandidateURLQuerySet(self.model, using=self._db).with_exclusion_and_inclusion_status() class CandidateURL(models.Model): diff --git a/sde_collections/views.py b/sde_collections/views.py index b0be1213..d7ea2ffc 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -254,6 +254,30 @@ 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 @@ -362,8 +386,8 @@ def get(self, request, *args, **kwargs): def get_queryset(self): queryset = ( CandidateURL.objects.filter(collection__config_folder=self.config_folder) - .with_exclusion_status() - .filter(excluded=False) + .with_exclusion_and_inclusion_status() + .filter(models.Q(excluded=False) | models.Q(included=True)) ) return queryset diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index 544acfbf..b238c605 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -55,6 +55,20 @@ 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; } @@ -63,6 +77,14 @@ background-image: none; } +.form-control { + color: white; +} + +.form-control:focus { + color: white; +} + .dt-container div.dt-length label { display: none; } @@ -175,3 +197,45 @@ body { 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; +} \ No newline at end of file diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index dd0c6944..b5bc0a6f 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -5,6 +5,7 @@ collection_id = getCollectionId(); $(document).ready(function () { initializeDataTable(); + setupClickHandlers(); }); function initializeDataTable() { @@ -23,6 +24,15 @@ function initializeDataTable() { ["Show 25", "Show 50", "Show 100", "Show 500"], ], }, + buttons: [ + { + text: "Add Pattern", + className: "addPattern", + action: function () { + $modal = $("#includePatternModal").modal(); + }, + }, + ], }, serverSide: true, orderCellsTop: true, @@ -44,6 +54,312 @@ function initializeDataTable() { ); } +// var pattern_type = "Exclude"; +// function initializeDataTable() { +// var affected_urls_table = $("#affectedURLsTable").DataTable({ +// pageLength: 100, +// colReorder: true, +// stateSave: true, +// serverSide: true, +// ajax: { +// url: `/api/affected-urls/?format=datatables&collection_id=${collection_id}`, // Replace with your actual API endpoint +// data: function(d) { +// d.pattern_type = pattern_type; // Assuming pattern_type is defined globally +// } +// }, +// columns: [ +// { data: null, render: function (data, type, row, meta) { +// return meta.row + meta.settings._iDisplayStart + 1; +// } +// }, +// { data: 'url', render: function(data, type, row) { +// return '
' + data + '' + +// '' + +// 'open_in_new
'; +// } +// }, +// { data: 'included', render: function(data, type, row) { +// if (pattern_type === "Exclude") { +// var icon = data ? 'check' : 'close'; +// return '' + icon + ''; +// } +// return ''; +// }, +// visible: pattern_type === "Exclude" +// } +// ], +// layout: { +// bottomEnd: "inputPaging", +// topEnd: null, +// topStart: { +// info: true, +// pageLength: { +// menu: [ +// [25, 50, 100, 500], +// ["Show 25", "Show 50", "Show 100", "Show 500"], +// ], +// }, +// buttons: [ +// { +// text: "Add Pattern", +// className: "addPattern", +// action: function () { +// $("#includePatternModal").modal('show'); +// }, +// }, +// ], +// }, +// }, +// orderCellsTop: true, +// pagingType: "input", +// rowId: "url", +// columnDefs: [ +// { orderable: true, targets: "_all" }, +// { orderable: false, targets: "filter-row" }, +// ], +// }); + +// $("#affectedURLsFilter").on( +// "beforeinput", +// DataTable.util.debounce(function (val) { +// affected_urls_table.columns(1).search(this.value).draw(); +// }, 1000) +// ); +// } + function getCollectionId() { return collection_id; } + +function setupClickHandlers() { + handleHideorShowSubmitButton(); + handleHideorShowKeypress(); + handleIncludeIndividualUrlClick(); +} + +function handleIncludeIndividualUrlClick() { + var true_icon = 'check'; + var false_icon = 'close'; + + $("#affectedURLsTable").on("click", ".include-url-btn", function () { + const inclusion_status = this.querySelector("i"); + if (inclusion_status.classList.contains("cross-mark")) { + // Change to tick mark + // Also give this button the value of the include_pattern by which it was included. + inclusion_status.classList.remove("cross-mark"); + inclusion_status.classList.add("tick-mark"); // Add the tick-mark class + inclusion_status.style.color = "green"; // Change color to green + inclusion_status.textContent = "check"; // Set the text to "check" + let parentCol3 = inclusion_status.closest(".col-3"); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute("data-sort", "1"); // Set data-sort to '1' for the check-mark + } + + match_pattern = remove_protocol($(this).attr("value")); + match_pattern_type = INDIVIDUAL_URL; + console.log(match_pattern); + + postIncludePatterns(match_pattern, match_pattern_type) + .then((result) => { + console.log("New pattern ID:", result.id); + console.log("Match pattern:", result.match_pattern); + $(this).attr("included_by_pattern", result.match_pattern); + $(this).attr("match_pattern_id", result.id); + }) + .catch((error) => { + console.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"); + + console.log("url", url); + console.log("included_by_pattern", included_by_pattern); + console.log("match_pattern_id", match_pattern_id); + + if (included_by_pattern === remove_protocol(url)) { + console.log( + "Only this URL will be affected by this pattern: " + match_pattern_id + ); + currentURLtoDelete = `/api/include-patterns/${match_pattern_id}/`; + deletePattern(currentURLtoDelete, (data_type = "Include Pattern")); + toastr.success("URL excluded successfully"); + // Change back to cross mark + inclusion_status.classList.remove("tick-mark"); + inclusion_status.classList.add("cross-mark"); // Add the cross-mark class + inclusion_status.style.color = "red"; // Change color to red + inclusion_status.textContent = "close"; // Set the text to "close" + let parentCol3 = inclusion_status.closest(".col-3"); + // Change the data-sort attribute of the parent element + if (parentCol3) { + parentCol3.setAttribute("data-sort", "0"); // Set data-sort to '0' for the cross-mark + } + // Also remove the included_by_pattern and match_pattern_id attributes + $(this).attr("included_by_pattern", "None"); + $(this).attr("match_pattern_id", "None"); + } 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) { + console.log("Success on adding to the Included URLs"); + 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 +) { + $.ajax({ + url: url, + type: "DELETE", + data: { + csrfmiddlewaretoken: csrftoken, + }, + headers: { + "X-CSRFToken": csrftoken, + }, + success: function (data) { + console.log("Successfully deleted."); + }, + 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(); + console.log("input_serialized", input_serialized); + $.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); + console.log("existingPatterns", existingPatterns); + 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) + ); + } + }, + error: function(xhr, status, error) { + toastr.error("An error occurred while checking existing patterns"); + } + }); + + // close the modal if it is open + $("#includePatternModal").modal("hide"); +}); + diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index f257ee33..24d58787 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -31,12 +31,18 @@

{{ forloop.counter }} -
- - + + + {% if pattern_type == "Exclude" %} + + {% endif %} + {% if pattern_type == "Exclude" %} + + {% endif %} @@ -49,6 +55,16 @@

open_in_new + {% if pattern_type == "Exclude" %} +

+ {% endif %} {% empty %} @@ -58,6 +74,34 @@

#URL#URLInclude URL
+ + {% if url.included %} + check + {% else %} + close + {% endif %} +
+ + +
{% endblock content %} From 42cdda0af00947b6f96789bda1dc6c69b44281c6 Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Thu, 10 Oct 2024 14:06:32 -0500 Subject: [PATCH 16/26] remove 'add pattern' button from unnecessary pages --- .../static/js/affected_urls.js | 30 ++++++++++--------- .../sde_collections/affected_urls.html | 5 +++- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index b5bc0a6f..b32d13fe 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -1,10 +1,20 @@ var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); var INDIVIDUAL_URL = 1; var MULTI_URL_PATTERN = 2; -collection_id = getCollectionId(); +// collection_id = getCollectionId(); $(document).ready(function () { initializeDataTable(); + // Conditionally add the button based on patternType + if (patternType == "Exclude" ) { + $("#affectedURLsTable").DataTable().button().add(0, { + text: "Add Include Pattern", + className: "addPattern", + action: function () { + $modal = $("#includePatternModal").modal(); + }, + }); + } setupClickHandlers(); }); @@ -17,22 +27,14 @@ function initializeDataTable() { bottomEnd: "inputPaging", topEnd: null, topStart: { - info: true, + // info: true, pageLength: { menu: [ [25, 50, 100, 500], ["Show 25", "Show 50", "Show 100", "Show 500"], ], }, - buttons: [ - { - text: "Add Pattern", - className: "addPattern", - action: function () { - $modal = $("#includePatternModal").modal(); - }, - }, - ], + buttons: [], }, serverSide: true, orderCellsTop: true, @@ -127,9 +129,9 @@ function initializeDataTable() { // ); // } -function getCollectionId() { - return collection_id; -} +// function getCollectionId() { +// return collection_id; +// } function setupClickHandlers() { handleHideorShowSubmitButton(); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 24d58787..7e4f6d54 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -107,7 +107,10 @@ {% block javascripts %} {{ block.super }} - + From 6b212136cbb884456ae53be238bd43750f1a40cd Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Fri, 11 Oct 2024 00:35:18 -0500 Subject: [PATCH 17/26] add serializer class to affected urls functionalities --- sde_collections/serializers.py | 69 ++++++++++++++++ sde_collections/urls.py | 4 + sde_collections/views.py | 64 +++++++++++++++ .../static/js/affected_urls.js | 79 +++++++++++++++++-- .../sde_collections/affected_urls.html | 5 +- 5 files changed, 214 insertions(+), 7 deletions(-) diff --git a/sde_collections/serializers.py b/sde_collections/serializers.py index 9623e85d..1723a1b7 100644 --- a/sde_collections/serializers.py +++ b/sde_collections/serializers.py @@ -97,6 +97,56 @@ class Meta: "present_on_prod", ) +class AffectedURLSerializer(serializers.ModelSerializer): + excluded = serializers.BooleanField(required=False) + document_type_display = serializers.CharField(source="get_document_type_display", read_only=True) + division_display = serializers.CharField(source="get_division_display", read_only=True) + url = serializers.CharField(required=False) + generated_title_id = serializers.SerializerMethodField(read_only=True) + match_pattern_type = serializers.SerializerMethodField(read_only=True) + candidate_urls_count = serializers.SerializerMethodField(read_only=True) + + # New fields for annotated parameters + included = serializers.BooleanField(read_only=True) # Assuming it's a boolean + included_by_pattern = serializers.CharField(read_only=True) # Assuming it's a string + match_pattern_id = serializers.IntegerField(read_only=True) # Assuming it's an integer + + def get_candidate_urls_count(self, obj): + titlepattern = obj.titlepattern_urls.last() + return titlepattern.candidate_urls.count() if titlepattern else 0 + + def get_generated_title_id(self, obj): + titlepattern = obj.titlepattern_urls.last() + return titlepattern.id if titlepattern else None + + def get_match_pattern_type(self, obj): + titlepattern = obj.titlepattern_urls.last() + return titlepattern.match_pattern_type if titlepattern else None + + class Meta: + model = CandidateURL + fields = ( + "id", + "excluded", + "url", + "scraped_title", + "generated_title", + "generated_title_id", + "match_pattern_type", + "candidate_urls_count", + "document_type", + "document_type_display", + "division", + "division_display", + "visited", + "test_title", + "production_title", + "present_on_test", + "present_on_prod", + "included", # New field + "included_by_pattern", # New field + "match_pattern_id", # New field + ) class CandidateURLBulkCreateSerializer(serializers.ModelSerializer): class Meta: @@ -245,3 +295,22 @@ def validate_match_pattern(self, value): except DivisionPattern.DoesNotExist: pass return value + + +class BaseAffectedURLSerializer(serializers.ModelSerializer): + match_pattern_type_display = serializers.CharField(source="get_match_pattern_type_display", read_only=True) + candidate_urls_count = serializers.SerializerMethodField(read_only=True) + + def get_candidate_urls_count(self, instance): + return instance.candidate_urls.count() + + class Meta: + fields = ( + "id", + "collection", + "match_pattern", + "match_pattern_type", + "match_pattern_type_display", + "candidate_urls_count", + ) + abstract = True diff --git a/sde_collections/urls.py b/sde_collections/urls.py index db96c6e0..3d6946fd 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -9,6 +9,10 @@ router.register(r"collections", views.CollectionViewSet, basename="collection") router.register(r"collections-read", views.CollectionReadViewSet, basename="collection-read") router.register(r"candidate-urls", views.CandidateURLViewSet) +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-patterns", views.ExcludePatternViewSet) router.register(r"include-patterns", views.IncludePatternViewSet) router.register(r"title-patterns", views.TitlePatternViewSet) diff --git a/sde_collections/views.py b/sde_collections/views.py index d7ea2ffc..dba74eac 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -38,6 +38,7 @@ CandidateURLAPISerializer, CandidateURLBulkCreateSerializer, CandidateURLSerializer, + AffectedURLSerializer, CollectionReadSerializer, CollectionSerializer, DivisionPatternSerializer, @@ -239,11 +240,17 @@ class BaseAffectedURLsListView(LoginRequiredMixin, ListView): def get_queryset(self): self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) + queryset = self.pattern.matched_urls() + # print(list(queryset)) + for url in queryset: + print(url.id) return 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"] + print(context["pattern_id"]) context["url_count"] = self.get_queryset().count() context["collection"] = self.pattern.collection context["pattern_type"] = self.pattern_type @@ -622,3 +629,60 @@ def get(self, request, *args, **kwargs): "resolved_title_errors": resolved_title_errors, } return render(request, "sde_collections/titles_and_errors_list.html", context) + + +class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): + queryset = CandidateURL.objects.all() + serializer_class = AffectedURLSerializer + pattern_model = None + pattern_type = None + + def get_queryset(self): + # queryset = super().get_queryset() + pattern_id = self.request.GET.get("pattern_id") + self.pattern = self.pattern_model.objects.get(id=pattern_id) + queryset = self.pattern.matched_urls() + print("The length is ", len(queryset)) + 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/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index b32d13fe..676e1161 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -4,6 +4,7 @@ var MULTI_URL_PATTERN = 2; // collection_id = getCollectionId(); $(document).ready(function () { + // handleAjaxStartAndStop(); initializeDataTable(); // Conditionally add the button based on patternType if (patternType == "Exclude" ) { @@ -18,16 +19,20 @@ $(document).ready(function () { setupClickHandlers(); }); +function handleAjaxStartAndStop() { + $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI); +} + function initializeDataTable() { var affected_urls_table = $("#affectedURLsTable").DataTable({ pageLength: 100, colReorder: true, - stateSave: true, + // stateSave: true, layout: { bottomEnd: "inputPaging", topEnd: null, topStart: { - // info: true, + info: true, pageLength: { menu: [ [25, 50, 100, 500], @@ -46,6 +51,37 @@ function initializeDataTable() { { orderable: false, targets: "filter-row" }, ], orderCellsTop: true, + ajax: { + url: (function() { + let url = null; + if (patternType === "Exclude") { + url = `/api/exclude-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`; + } else if (patternType === "Include") { + url = `/api/include-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`; + } else if (patternType === "Title") { + url = `/api/title-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`; + } else if (patternType === "Document Type") { + url = `/api/documenttype-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`; + } + return url; + })(), + data: function (d) { + // d.is_excluded = $("#filter-checkbox").is(":checked") ? false : null; + }, + }, + columns: [ + { + data: null, // No data source, we will generate the number + render: function (data, type, row, meta) { + return meta.row + 1; // Return the serial number starting from 1 + }, + searchable: false, // Not searchable + class: "whiteText text-center" // Add any class you need + }, + getURLColumn(), + // Conditionally add the excluded column based on patternType + ...(patternType === "Exclude" ? [getIncludeURLColumn()] : []) ] + }); $("#affectedURLsFilter").on( @@ -56,6 +92,35 @@ function initializeDataTable() { ); } +function getURLColumn() { + return { + data: "url", + width: "30%", + render: function (data, type, row) { + return `
${data} + + open_in_new +
`; + }, + }; +} + +function getIncludeURLColumn() { + return { + data: "url", + width: "30%", + // + render: function (data, type, row) { + return ` + ${row["included"] ? + 'check' : + 'close'} + `; + }, + class: "col-3 text-center data-sort=\"0\"" + }; +} + // var pattern_type = "Exclude"; // function initializeDataTable() { // var affected_urls_table = $("#affectedURLsTable").DataTable({ @@ -353,8 +418,13 @@ $("#include_pattern_form").on("submit", function (e) { 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) => { + console.error("Error posting include patterns:", error); + }); + } }, error: function(xhr, status, error) { toastr.error("An error occurred while checking existing patterns"); @@ -364,4 +434,3 @@ $("#include_pattern_form").on("submit", function (e) { // close the modal if it is open $("#includePatternModal").modal("hide"); }); - diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 7e4f6d54..7ac46a6a 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -45,7 +45,7 @@

{% endif %} - +

@@ -110,6 +110,7 @@ From 9d666b2bb18478c5ef6b41178e2b05fc9fca556c Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Fri, 11 Oct 2024 16:45:55 -0500 Subject: [PATCH 18/26] add datatables reload() feature to skip unnecessary ui modifications --- .../static/css/affected_urls.css | 12 +- .../static/js/affected_urls.js | 158 ++++-------------- 2 files changed, 40 insertions(+), 130 deletions(-) diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index b238c605..a596daf7 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -238,4 +238,14 @@ body { .modal-header { margin-bottom: 40px; -} \ No newline at end of file +} + +.row .col-md-auto { + display: flex; + align-items: center; + justify-content: space-between; +} + +.dt-buttons { + margin-left: auto; +} diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 676e1161..80c60927 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -1,10 +1,9 @@ var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); var INDIVIDUAL_URL = 1; var MULTI_URL_PATTERN = 2; -// collection_id = getCollectionId(); $(document).ready(function () { - // handleAjaxStartAndStop(); + handleAjaxStartAndStop(); initializeDataTable(); // Conditionally add the button based on patternType if (patternType == "Exclude" ) { @@ -25,9 +24,10 @@ function handleAjaxStartAndStop() { function initializeDataTable() { var affected_urls_table = $("#affectedURLsTable").DataTable({ + processing: true, pageLength: 100, colReorder: true, - // stateSave: true, + stateSave: true, layout: { bottomEnd: "inputPaging", topEnd: null, @@ -44,6 +44,7 @@ function initializeDataTable() { serverSide: true, orderCellsTop: true, pagingType: "input", + paging: true, rowId: "url", }, columnDefs: [ @@ -51,6 +52,8 @@ function initializeDataTable() { { orderable: false, targets: "filter-row" }, ], orderCellsTop: true, + // ajax: `/api/exclude-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`, + // ajax: `/api/candidate-urls/?format=datatables&collection_id=${collection_id}&length=1000`, ajax: { url: (function() { let url = null; @@ -66,9 +69,20 @@ function initializeDataTable() { return url; })(), data: function (d) { + console.log(d); // d.is_excluded = $("#filter-checkbox").is(":checked") ? false : null; }, + complete: function(xhr, status) { + console.log(xhr.responseText); // Log the response from the server + } + + }, + createdRow: function(row, data, dataIndex) { + // Set data-sort attribute based on the included property + const dataSortValue = data.included ? '1' : '0'; + $(row).find('td').eq(2).attr('data-sort', dataSortValue); }, + columns: [ { data: null, // No data source, we will generate the number @@ -80,7 +94,8 @@ function initializeDataTable() { }, getURLColumn(), // Conditionally add the excluded column based on patternType - ...(patternType === "Exclude" ? [getIncludeURLColumn()] : []) ] + ...(patternType === "Exclude" ? [getIncludeURLColumn()] : []) + ] }); @@ -109,7 +124,6 @@ function getIncludeURLColumn() { return { data: "url", width: "30%", - // render: function (data, type, row) { return ` ${row["included"] ? @@ -117,87 +131,10 @@ function getIncludeURLColumn() { 'close'} `; }, - class: "col-3 text-center data-sort=\"0\"" + class: "col-3 text-center" }; } -// var pattern_type = "Exclude"; -// function initializeDataTable() { -// var affected_urls_table = $("#affectedURLsTable").DataTable({ -// pageLength: 100, -// colReorder: true, -// stateSave: true, -// serverSide: true, -// ajax: { -// url: `/api/affected-urls/?format=datatables&collection_id=${collection_id}`, // Replace with your actual API endpoint -// data: function(d) { -// d.pattern_type = pattern_type; // Assuming pattern_type is defined globally -// } -// }, -// columns: [ -// { data: null, render: function (data, type, row, meta) { -// return meta.row + meta.settings._iDisplayStart + 1; -// } -// }, -// { data: 'url', render: function(data, type, row) { -// return '
' + data + '' + -// '' + -// 'open_in_new
'; -// } -// }, -// { data: 'included', render: function(data, type, row) { -// if (pattern_type === "Exclude") { -// var icon = data ? 'check' : 'close'; -// return '' + icon + ''; -// } -// return ''; -// }, -// visible: pattern_type === "Exclude" -// } -// ], -// layout: { -// bottomEnd: "inputPaging", -// topEnd: null, -// topStart: { -// info: true, -// pageLength: { -// menu: [ -// [25, 50, 100, 500], -// ["Show 25", "Show 50", "Show 100", "Show 500"], -// ], -// }, -// buttons: [ -// { -// text: "Add Pattern", -// className: "addPattern", -// action: function () { -// $("#includePatternModal").modal('show'); -// }, -// }, -// ], -// }, -// }, -// orderCellsTop: true, -// pagingType: "input", -// rowId: "url", -// columnDefs: [ -// { orderable: true, targets: "_all" }, -// { orderable: false, targets: "filter-row" }, -// ], -// }); - -// $("#affectedURLsFilter").on( -// "beforeinput", -// DataTable.util.debounce(function (val) { -// affected_urls_table.columns(1).search(this.value).draw(); -// }, 1000) -// ); -// } - -// function getCollectionId() { -// return collection_id; -// } - function setupClickHandlers() { handleHideorShowSubmitButton(); handleHideorShowKeypress(); @@ -205,67 +142,30 @@ function setupClickHandlers() { } function handleIncludeIndividualUrlClick() { - var true_icon = 'check'; - var false_icon = 'close'; $("#affectedURLsTable").on("click", ".include-url-btn", function () { const inclusion_status = this.querySelector("i"); if (inclusion_status.classList.contains("cross-mark")) { - // Change to tick mark - // Also give this button the value of the include_pattern by which it was included. - inclusion_status.classList.remove("cross-mark"); - inclusion_status.classList.add("tick-mark"); // Add the tick-mark class - inclusion_status.style.color = "green"; // Change color to green - inclusion_status.textContent = "check"; // Set the text to "check" - let parentCol3 = inclusion_status.closest(".col-3"); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute("data-sort", "1"); // Set data-sort to '1' for the check-mark - } - match_pattern = remove_protocol($(this).attr("value")); match_pattern_type = INDIVIDUAL_URL; - console.log(match_pattern); postIncludePatterns(match_pattern, match_pattern_type) .then((result) => { - console.log("New pattern ID:", result.id); - console.log("Match pattern:", result.match_pattern); - $(this).attr("included_by_pattern", result.match_pattern); - $(this).attr("match_pattern_id", result.id); + // refresh the table after a pattern is added + $("#affectedURLsTable").DataTable().ajax.reload(null, false); }) .catch((error) => { - console.error("Error:", 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"); - console.log("url", url); - console.log("included_by_pattern", included_by_pattern); - console.log("match_pattern_id", match_pattern_id); - if (included_by_pattern === remove_protocol(url)) { - console.log( - "Only this URL will be affected by this pattern: " + match_pattern_id - ); currentURLtoDelete = `/api/include-patterns/${match_pattern_id}/`; - deletePattern(currentURLtoDelete, (data_type = "Include Pattern")); + deletePattern(currentURLtoDelete, (data_type = "Include Pattern")) toastr.success("URL excluded successfully"); - // Change back to cross mark - inclusion_status.classList.remove("tick-mark"); - inclusion_status.classList.add("cross-mark"); // Add the cross-mark class - inclusion_status.style.color = "red"; // Change color to red - inclusion_status.textContent = "close"; // Set the text to "close" - let parentCol3 = inclusion_status.closest(".col-3"); - // Change the data-sort attribute of the parent element - if (parentCol3) { - parentCol3.setAttribute("data-sort", "0"); // Set data-sort to '0' for the cross-mark - } - // Also remove the included_by_pattern and match_pattern_id attributes - $(this).attr("included_by_pattern", "None"); - $(this).attr("match_pattern_id", "None"); } else { toastr.error( "This URL is affected by a multi-URL include pattern: " + @@ -288,7 +188,6 @@ function postIncludePatterns(match_pattern, match_pattern_type = 0) { csrfmiddlewaretoken: csrftoken, }, success: function (data) { - console.log("Success on adding to the Included URLs"); toastr.success("Added to include patterns successfully"); resolve({ id: data.id, @@ -314,6 +213,7 @@ function deletePattern( url_type = null, candidate_urls_count = null ) { + return new Promise((resolve, reject) => { $.ajax({ url: url, type: "DELETE", @@ -324,13 +224,15 @@ function deletePattern( "X-CSRFToken": csrftoken, }, success: function (data) { - console.log("Successfully deleted."); + // 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() { @@ -396,13 +298,11 @@ $("#include_pattern_form").on("submit", function (e) { // check if pattern already exists input_serialized = $(this).serializeArray(); - console.log("input_serialized", input_serialized); $.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); - console.log("existingPatterns", existingPatterns); if (existingPatterns.includes(input_serialized[0].value)) { toastr.warning("Pattern already exists"); $("#includePatternModal").modal("hide"); @@ -422,7 +322,7 @@ $("#include_pattern_form").on("submit", function (e) { // Reload the DataTable after the successful postIncludePatterns call $("#affectedURLsTable").DataTable().ajax.reload(null, false); }).catch((error) => { - console.error("Error posting include patterns:", error); + toastr.error("Error posting include patterns:", error); }); } }, From 6ec494377ab3f91668ebf5f72e0d9fd5c3c1435c Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Sat, 12 Oct 2024 18:26:35 -0500 Subject: [PATCH 19/26] fix ajax issues of datatable --- sde_collections/views.py | 53 +++--- .../static/js/affected_urls.js | 167 ++++++++++-------- .../sde_collections/affected_urls.html | 30 +--- 3 files changed, 111 insertions(+), 139 deletions(-) diff --git a/sde_collections/views.py b/sde_collections/views.py index dba74eac..35c06aed 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -241,16 +241,12 @@ class BaseAffectedURLsListView(LoginRequiredMixin, ListView): def get_queryset(self): self.pattern = self.pattern_model.objects.get(id=self.kwargs["id"]) queryset = self.pattern.matched_urls() - # print(list(queryset)) - for url in queryset: - print(url.id) return 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"] - print(context["pattern_id"]) context["url_count"] = self.get_queryset().count() context["collection"] = self.pattern.collection context["pattern_type"] = self.pattern_type @@ -264,25 +260,21 @@ class ExcludePatternAffectedURLsListView(BaseAffectedURLsListView): 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] + 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() + include_pattern_subquery.values("match_pattern"), output_field=models.CharField() ), - match_pattern_id=models.Subquery( - include_pattern_subquery.values('id'), - output_field=models.IntegerField() - ) + match_pattern_id=models.Subquery(include_pattern_subquery.values("id"), output_field=models.IntegerField()), ) - + return queryset @@ -638,51 +630,48 @@ class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): pattern_type = None def get_queryset(self): - # queryset = super().get_queryset() pattern_id = self.request.GET.get("pattern_id") self.pattern = self.pattern_model.objects.get(id=pattern_id) queryset = self.pattern.matched_urls() - print("The length is ", len(queryset)) 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] + 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() + include_pattern_subquery.values("match_pattern"), output_field=models.CharField() ), - match_pattern_id=models.Subquery( - include_pattern_subquery.values('id'), - output_field=models.IntegerField() - ) + 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/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index 80c60927..f65b7605 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -6,14 +6,17 @@ $(document).ready(function () { handleAjaxStartAndStop(); initializeDataTable(); // Conditionally add the button based on patternType - if (patternType == "Exclude" ) { - $("#affectedURLsTable").DataTable().button().add(0, { - text: "Add Include Pattern", - className: "addPattern", - action: function () { - $modal = $("#includePatternModal").modal(); - }, - }); + if (patternType == "Exclude") { + $("#affectedURLsTable") + .DataTable() + .button() + .add(0, { + text: "Add Include Pattern", + className: "addPattern", + action: function () { + $modal = $("#includePatternModal").modal(); + }, + }); } setupClickHandlers(); }); @@ -28,6 +31,11 @@ function initializeDataTable() { pageLength: 100, colReorder: true, stateSave: true, + serverSide: true, + orderCellsTop: true, + pagingType: "input", + paging: true, + rowId: "url", layout: { bottomEnd: "inputPaging", topEnd: null, @@ -41,21 +49,14 @@ function initializeDataTable() { }, buttons: [], }, - serverSide: true, - orderCellsTop: true, - pagingType: "input", - paging: true, - rowId: "url", }, columnDefs: [ { orderable: true, targets: "_all" }, { orderable: false, targets: "filter-row" }, ], orderCellsTop: true, - // ajax: `/api/exclude-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`, - // ajax: `/api/candidate-urls/?format=datatables&collection_id=${collection_id}&length=1000`, ajax: { - url: (function() { + url: (function () { let url = null; if (patternType === "Exclude") { url = `/api/exclude-pattern-affected-urls/?format=datatables&pattern_id=${pattern_id}`; @@ -68,35 +69,26 @@ function initializeDataTable() { } return url; })(), - data: function (d) { - console.log(d); - // d.is_excluded = $("#filter-checkbox").is(":checked") ? false : null; - }, - complete: function(xhr, status) { - console.log(xhr.responseText); // Log the response from the server - } - + data: function (d) {}, + complete: function (xhr, status) {}, }, - createdRow: function(row, data, dataIndex) { + createdRow: function (row, data, dataIndex) { // Set data-sort attribute based on the included property - const dataSortValue = data.included ? '1' : '0'; - $(row).find('td').eq(2).attr('data-sort', dataSortValue); + const dataSortValue = data.included ? "1" : "0"; + $(row).find("td").eq(2).attr("data-sort", dataSortValue); + if (patternType === "Exclude" && data["included"]) { + $(row).attr( + "style", + "background-color: rgba(255, 61, 87, 0.26) !important" + ); + } }, columns: [ - { - data: null, // No data source, we will generate the number - render: function (data, type, row, meta) { - return meta.row + 1; // Return the serial number starting from 1 - }, - searchable: false, // Not searchable - class: "whiteText text-center" // Add any class you need - }, - getURLColumn(), - // Conditionally add the excluded column based on patternType - ...(patternType === "Exclude" ? [getIncludeURLColumn()] : []) - ] - + { data: "id", searchable: false, class: "whiteText text-center" }, + getURLColumn(), + ...getConditionalColumns(patternType), + ], }); $("#affectedURLsFilter").on( @@ -122,19 +114,38 @@ function getURLColumn() { function getIncludeURLColumn() { return { - data: "url", + data: "included", width: "30%", render: function (data, type, row) { - return ` - ${row["included"] ? - 'check' : - 'close'} + return ` + ${ + data + ? 'check' + : 'close' + } `; }, - class: "col-3 text-center" + 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(); @@ -142,7 +153,6 @@ function setupClickHandlers() { } function handleIncludeIndividualUrlClick() { - $("#affectedURLsTable").on("click", ".include-url-btn", function () { const inclusion_status = this.querySelector("i"); if (inclusion_status.classList.contains("cross-mark")) { @@ -164,7 +174,7 @@ function handleIncludeIndividualUrlClick() { if (included_by_pattern === remove_protocol(url)) { currentURLtoDelete = `/api/include-patterns/${match_pattern_id}/`; - deletePattern(currentURLtoDelete, (data_type = "Include Pattern")) + deletePattern(currentURLtoDelete, (data_type = "Include Pattern")); toastr.success("URL excluded successfully"); } else { toastr.error( @@ -214,25 +224,25 @@ function deletePattern( 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); - }, + $.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() { @@ -300,15 +310,14 @@ $("#include_pattern_form").on("submit", function (e) { 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); + 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 { + } else { // if pattern does not exist, create a new pattern inputs = {}; input_serialized.forEach((field) => { @@ -318,17 +327,19 @@ $("#include_pattern_form").on("submit", function (e) { postIncludePatterns( (match_pattern = inputs.match_pattern), (match_pattern_type = 2) - ).then(() => { + ) + .then(() => { // Reload the DataTable after the successful postIncludePatterns call $("#affectedURLsTable").DataTable().ajax.reload(null, false); - }).catch((error) => { - toastr.error("Error posting include patterns:", error); - }); - } + }) + .catch((error) => { + toastr.error("Error posting include patterns:", error); + }); + } }, - error: function(xhr, status, error) { + error: function (xhr, status, error) { toastr.error("An error occurred while checking existing patterns"); - } + }, }); // close the modal if it is open diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 7ac46a6a..64024493 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -27,11 +27,10 @@

- - + {% if pattern_type == "Exclude" %} @@ -45,33 +44,6 @@

{% endif %}

-
#ID URLInclude URL
From 37bdb7ce4a2724b1cafbd271a7a7475c263d7f6a Mon Sep 17 00:00:00 2001 From: Kiran Dawadi Date: Fri, 18 Oct 2024 21:23:16 -0500 Subject: [PATCH 20/26] add 'include pattern' feature on right mouse click --- sde_collections/serializers.py | 6 +- .../static/css/affected_urls.css | 24 ++++++++ .../static/js/affected_urls.js | 61 ++++++++++++++++++- .../sde_collections/affected_urls.html | 8 ++- 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/sde_collections/serializers.py b/sde_collections/serializers.py index 1723a1b7..147c06fb 100644 --- a/sde_collections/serializers.py +++ b/sde_collections/serializers.py @@ -107,9 +107,9 @@ class AffectedURLSerializer(serializers.ModelSerializer): candidate_urls_count = serializers.SerializerMethodField(read_only=True) # New fields for annotated parameters - included = serializers.BooleanField(read_only=True) # Assuming it's a boolean - included_by_pattern = serializers.CharField(read_only=True) # Assuming it's a string - match_pattern_id = serializers.IntegerField(read_only=True) # Assuming it's an integer + included = serializers.BooleanField(read_only=True) + included_by_pattern = serializers.CharField(read_only=True) + match_pattern_id = serializers.IntegerField(read_only=True) def get_candidate_urls_count(self, obj): titlepattern = obj.titlepattern_urls.last() diff --git a/sde_indexing_helper/static/css/affected_urls.css b/sde_indexing_helper/static/css/affected_urls.css index a596daf7..3d1aba80 100644 --- a/sde_indexing_helper/static/css/affected_urls.css +++ b/sde_indexing_helper/static/css/affected_urls.css @@ -69,6 +69,7 @@ .asterik { color: #C3001A; } + .customizeColumns { margin-left: 10px !important; } @@ -249,3 +250,26 @@ body { .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; +} \ No newline at end of file diff --git a/sde_indexing_helper/static/js/affected_urls.js b/sde_indexing_helper/static/js/affected_urls.js index f65b7605..9934c188 100644 --- a/sde_indexing_helper/static/js/affected_urls.js +++ b/sde_indexing_helper/static/js/affected_urls.js @@ -1,6 +1,7 @@ var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); var INDIVIDUAL_URL = 1; var MULTI_URL_PATTERN = 2; +var selected_text = ""; $(document).ready(function () { handleAjaxStartAndStop(); @@ -85,16 +86,16 @@ function initializeDataTable() { }, columns: [ - { data: "id", searchable: false, class: "whiteText text-center" }, getURLColumn(), ...getConditionalColumns(patternType), + { data: "id", visible: false, searchable: false }, ], }); $("#affectedURLsFilter").on( "beforeinput", DataTable.util.debounce(function (val) { - affected_urls_table.columns(1).search(this.value).draw(); + affected_urls_table.columns(0).search(this.value).draw(); }, 1000) ); } @@ -172,7 +173,7 @@ function handleIncludeIndividualUrlClick() { var included_by_pattern = $(this).attr("included_by_pattern"); var match_pattern_id = $(this).attr("match_pattern_id"); - if (included_by_pattern === remove_protocol(url)) { + 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"); @@ -345,3 +346,57 @@ $("#include_pattern_form").on("submit", function (e) { // 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 () { + 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); +}); diff --git a/sde_indexing_helper/templates/sde_collections/affected_urls.html b/sde_indexing_helper/templates/sde_collections/affected_urls.html index 64024493..a89e963d 100644 --- a/sde_indexing_helper/templates/sde_collections/affected_urls.html +++ b/sde_indexing_helper/templates/sde_collections/affected_urls.html @@ -30,14 +30,14 @@

- + {% if pattern_type == "Exclude" %} {% endif %} - + {% if pattern_type == "Exclude" %} @@ -47,6 +47,10 @@

ID URLInclude URL
+
    +
  • Create Include Pattern
  • +
+