From 4f834621db1b0faaafcba4649e9e21f310ee01ec Mon Sep 17 00:00:00 2001 From: suecarmol Date: Tue, 4 Jun 2024 13:00:54 -0600 Subject: [PATCH 01/15] Make links count and editor count async --- .../templates/common/statistics_table.html | 15 +-- .../organisation_charts_include.html | 58 +++++++++++- extlinks/organisations/urls.py | 26 ++++-- extlinks/organisations/views.py | 91 ++++++++++++++----- 4 files changed, 146 insertions(+), 44 deletions(-) diff --git a/extlinks/common/templates/common/statistics_table.html b/extlinks/common/templates/common/statistics_table.html index cfc9ef68..68996610 100644 --- a/extlinks/common/templates/common/statistics_table.html +++ b/extlinks/common/templates/common/statistics_table.html @@ -6,22 +6,15 @@

Statistics

Total added: - {{ collection.total_added }} + Total removed: - {{ collection.total_removed }} + - + - {% else %} - color: red;"> - {% endif %} - {{ collection.total_diff }} - + {% if collection.linksearch_total_start %} @@ -52,7 +45,7 @@

Statistics

Total editors: - {{ collection.total_editors }} + Total projects: diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index a6e4fa15..c500baae 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -139,6 +139,13 @@

Latest link events

}, }, }); + + // Click all Link Events chart section TESTETETETETETETETET + document.getElementById('{{ collection_name }}_linkEvents_button').click(); + var form_data = {{ form_data|safe }}; + var collection_id = {{ collection.collection_id|safe }}; + getLinksCount(collection_id, form_data); + getEditorCount(collection_id, form_data); {% endfor %} function openGraph(evt, collection, graphName) { @@ -166,8 +173,51 @@

Latest link events

evt.currentTarget.className += " active"; } - // Click all Link Events buttons - {% for collection_name, collection in collections.items %} - document.getElementById('{{ collection_name }}_linkEvents_button').click(); - {% endfor %} + function getLinksCount(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:links_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + document.getElementById(collection_id + "-links-added").innerHTML = "Loading..."; + document.getElementById(collection_id + "-links-removed").innerHTML = "Loading..."; + document.getElementById(collection_id + "-links-diff").innerHTML = "Loading..."; + }, + // on success + success: function(response) { + document.getElementById(collection_id + "-links-added").innerHTML = response.links_added; + document.getElementById(collection_id + "-links-removed").innerHTML = response.links_removed; + + if (response.links_diff > 0) { + document.getElementById(collection_id + "-links-diff").innerHTML = "+" + response.links_diff; + document.getElementById(collection_id + "-links-diff").style.color = "green"; + } + else{ + document.getElementById("links-diff").innerHTML = response.links_diff; + document.getElementById("links-diff").style.color = "red"; + } + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors); + } + }); + } + + function getEditorCount(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:editor_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + document.getElementById(collection_id + "-total-editors").innerHTML = "Loading..."; + }, + // on success + success: function(response) { + document.getElementById(collection_id + "-total-editors").innerHTML = response.editor_count; + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } diff --git a/extlinks/organisations/urls.py b/extlinks/organisations/urls.py index c8b84569..a5f02e3d 100644 --- a/extlinks/organisations/urls.py +++ b/extlinks/organisations/urls.py @@ -2,16 +2,28 @@ from extlinks.common.views import CSVPageTotals from extlinks.common.urls import urlpatterns as shared_urls -from .views import (OrganisationDetailView, - OrganisationListView) +from .views import ( + OrganisationDetailView, + OrganisationListView, + get_editor_count, + get_project_count, + get_links_count, + get_top_organisations, + get_top_projects, + get_top_users, +) urlpatterns = [ - path('', OrganisationListView.as_view(), name='list'), - path('', OrganisationDetailView.as_view(), name='detail'), - + path("", OrganisationListView.as_view(), name="list"), + path("", OrganisationDetailView.as_view(), name="detail"), + path("editor_count/", get_editor_count, name="editor_count"), + path("project_count/", get_project_count, name="project_count"), + path("links_count/", get_links_count, name="links_count"), + path("top_organisations/", get_top_organisations, name="top_organisations"), + path("top_projects/", get_top_projects, name="top_projects"), + path("top_users/", get_top_users, name="top_users"), # CSV downloads - path('/csv/page_totals', CSVPageTotals.as_view(), - name='csv_page_totals'), + path("/csv/page_totals", CSVPageTotals.as_view(), name="csv_page_totals"), ] urlpatterns += shared_urls diff --git a/extlinks/organisations/views.py b/extlinks/organisations/views.py index 1176b766..30ed8a39 100644 --- a/extlinks/organisations/views.py +++ b/extlinks/organisations/views.py @@ -1,7 +1,9 @@ from datetime import datetime, date, timedelta +import json import re from django.db.models import Count, Sum, Q +from django.http import JsonResponse from django.views.generic import ListView, DetailView from django.views.decorators.cache import cache_page from django.utils.decorators import method_decorator @@ -33,7 +35,6 @@ def get_queryset(self, **kwargs): return queryset -# @method_decorator(cache_page(60 * 60), name="dispatch") class OrganisationDetailView(DetailView): model = Organisation form_class = FilterForm @@ -57,9 +58,9 @@ def get_context_data(self, **kwargs): # each collection has its own dictionary of data. context["collections"] = {} for collection in organisation_collections: - this_collection_linksearchtotals = LinkSearchTotal.objects.filter( - url__collection=collection - ) + this_collection_linksearchtotals = LinkSearchTotal.objects.prefetch_related( + "url" + ).filter(url__collection=collection) form_data = None if form.is_valid(): @@ -67,6 +68,7 @@ def get_context_data(self, **kwargs): this_collection_linksearchtotals = filter_linksearchtotals( this_collection_linksearchtotals, form_data ) + context["form_data"] = json.dumps(form_data, default=str) # Replace all special characters that might confuse JS with an # underscore. @@ -74,12 +76,13 @@ def get_context_data(self, **kwargs): context["collections"][collection_key] = {} context["collections"][collection_key]["object"] = collection + context["collections"][collection_key]["collection_id"] = collection.pk context["collections"][collection_key]["urls"] = collection.url.all() - context["collections"][ - collection_key - ] = self._build_collection_context_dictionary( - collection, context["collections"][collection_key], form_data + context["collections"][collection_key] = ( + self._build_collection_context_dictionary( + collection, context["collections"][collection_key], form_data + ) ) # LinkSearchTotal chart data @@ -246,20 +249,6 @@ def _fill_statistics_table_context(self, context, queryset_filter): ------- dict : The context dictionary with the relevant statistics """ - links_added_removed = LinkAggregate.objects.filter(queryset_filter).aggregate( - links_added=Sum("total_links_added"), - links_removed=Sum("total_links_removed"), - links_diff=Sum("total_links_added") - Sum("total_links_removed"), - ) - context["total_added"] = links_added_removed["links_added"] - context["total_removed"] = links_added_removed["links_removed"] - context["total_diff"] = links_added_removed["links_diff"] - - editor_count = UserAggregate.objects.filter(queryset_filter).aggregate( - editor_count=Count("username", distinct=True) - ) - context["total_editors"] = editor_count["editor_count"] - project_count = PageProjectAggregate.objects.filter(queryset_filter).aggregate( project_count=Count("project_name", distinct=True) ) @@ -347,3 +336,61 @@ def _fill_latest_linkevents(self, collection, context, form_data): ) return context + + +def get_editor_count(request): + """ + request : dict + Ajax request for editor count (found in the Statistics table) + """ + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + editor_count = UserAggregate.objects.filter(queryset_filter).aggregate( + editor_count=Count("username", distinct=True) + ) + response = {"editor_count": editor_count["editor_count"]} + + return JsonResponse(response) + + +def get_project_count(request): + return + + +def get_links_count(request): + """ + request : dict + Ajax request for links count (found in the Statistics table) + """ + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + links_added_removed = LinkAggregate.objects.filter(queryset_filter).aggregate( + links_added=Sum("total_links_added"), + links_removed=Sum("total_links_removed"), + links_diff=Sum("total_links_added") - Sum("total_links_removed"), + ) + response = { + "links_added": links_added_removed["links_added"], + "links_removed": links_added_removed["links_removed"], + "links_diff": links_added_removed["links_diff"], + } + + return JsonResponse(response) + + +def get_top_organisations(request): + return + + +def get_top_projects(request): + return + + +def get_top_users(request): + return From 09cde1b335744164ffc49209a54169595d0af257 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Tue, 4 Jun 2024 13:07:34 -0600 Subject: [PATCH 02/15] Add async project count in organisations --- .../templates/common/statistics_table.html | 2 +- .../organisation_charts_include.html | 20 ++++++++++ extlinks/organisations/views.py | 39 ++++++------------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/extlinks/common/templates/common/statistics_table.html b/extlinks/common/templates/common/statistics_table.html index 68996610..65967265 100644 --- a/extlinks/common/templates/common/statistics_table.html +++ b/extlinks/common/templates/common/statistics_table.html @@ -49,7 +49,7 @@

Statistics

Total projects: - {{ collection.total_projects }} + {% else %} diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index c500baae..9d32ae54 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -146,6 +146,7 @@

Latest link events

var collection_id = {{ collection.collection_id|safe }}; getLinksCount(collection_id, form_data); getEditorCount(collection_id, form_data); + getProjectCount(collection_id, form_data); {% endfor %} function openGraph(evt, collection, graphName) { @@ -220,4 +221,23 @@

Latest link events

} }); } + + function getProjectCount(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:project_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + document.getElementById(collection_id + "-total-projects").innerHTML = "Loading..."; + }, + // on success + success: function(response) { + document.getElementById(collection_id + "-total-projects").innerHTML = response.project_count; + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } + diff --git a/extlinks/organisations/views.py b/extlinks/organisations/views.py index 30ed8a39..e2dbb4d5 100644 --- a/extlinks/organisations/views.py +++ b/extlinks/organisations/views.py @@ -150,7 +150,6 @@ def _build_collection_context_dictionary(self, collection, context, form_data): queryset_filter = Q(collection=collection) context = self._fill_chart_context(collection, context, queryset_filter) - context = self._fill_statistics_table_context(context, queryset_filter) context = self._fill_totals_tables(context, queryset_filter) context = self._fill_latest_linkevents(collection, context, form_data) @@ -230,32 +229,6 @@ def _fill_chart_context(self, collection, context, queryset_filter): return context - def _fill_statistics_table_context(self, context, queryset_filter): - """ - This function adds the Statistics table information to the context - dictionary to display in OrganisationDetailView - - Parameters - ---------- - context : dict - The context dictionary that the function will be adding information to - - queryset_filter: Q - If the information is filtered, this set of filters will filter it. - The default is only filtering by the collection that is part of - the organisation - - Returns - ------- - dict : The context dictionary with the relevant statistics - """ - project_count = PageProjectAggregate.objects.filter(queryset_filter).aggregate( - project_count=Count("project_name", distinct=True) - ) - context["total_projects"] = project_count["project_count"] - - return context - def _fill_totals_tables(self, context, queryset_filter): """ This function adds the information for the Totals tables to the context @@ -357,7 +330,17 @@ def get_editor_count(request): def get_project_count(request): - return + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + project_count = PageProjectAggregate.objects.filter(queryset_filter).aggregate( + project_count=Count("project_name", distinct=True) + ) + response = {"project_count": project_count["project_count"]} + + return JsonResponse(response) def get_links_count(request): From 551beb537b6a6aa30617f083f2df23012493aea4 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Tue, 4 Jun 2024 21:29:21 -0600 Subject: [PATCH 03/15] Add async top pages for organisations --- .../templates/common/top_pages_table.html | 17 --- .../templates/common/top_projects_table.html | 29 ----- .../templates/common/top_users_table.html | 30 ----- .../organisation_charts_include.html | 62 ++++++++- extlinks/organisations/urls.py | 4 +- extlinks/organisations/views.py | 120 ++++++++++-------- 6 files changed, 128 insertions(+), 134 deletions(-) delete mode 100644 extlinks/common/templates/common/top_pages_table.html delete mode 100644 extlinks/common/templates/common/top_projects_table.html delete mode 100644 extlinks/common/templates/common/top_users_table.html diff --git a/extlinks/common/templates/common/top_pages_table.html b/extlinks/common/templates/common/top_pages_table.html deleted file mode 100644 index 24146c04..00000000 --- a/extlinks/common/templates/common/top_pages_table.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load common_filters %} - - - - - - {% for page in collection.top_pages %} - - - - - {% endfor %} -
PageAdded Links
- {{ page.page_name|replace_underscores|truncatechars:20 }} - - {{ page.links_diff }} -
diff --git a/extlinks/common/templates/common/top_projects_table.html b/extlinks/common/templates/common/top_projects_table.html deleted file mode 100644 index dfe7c833..00000000 --- a/extlinks/common/templates/common/top_projects_table.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - {% if collections %} - {% for project in collection.top_projects %} - - - - - {% endfor %} - {% else %} - {% for project in top_projects %} - - - - - {% endfor %} - {% endif %} -
ProjectAdded Links
- {{ project.project_name }} - - {{ project.links_diff }} -
- {{ project.project_name }} - - {{ project.links_diff }} -
diff --git a/extlinks/common/templates/common/top_users_table.html b/extlinks/common/templates/common/top_users_table.html deleted file mode 100644 index 99e56f48..00000000 --- a/extlinks/common/templates/common/top_users_table.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - {% if collections %} - {% for user in collection.top_users %} - - - - - {% endfor %} - {% else %} - {% for user in top_users %} - - - - - {% endfor %} - {% endif %} - -
UsernameAdded Links
- {{ user.username|truncatechars:15 }} - - {{ user.links_diff }} -
- {{ user.username|truncatechars:15 }} - - {{ user.links_diff }} -
diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index 9d32ae54..f140907f 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -36,19 +36,34 @@

Totals

- {% include "common/top_pages_table.html" %} + + + + + +
PageAdded Links
- {% include "common/top_projects_table.html" %} + + + + + +
ProjectAdded Links
- {% include "common/top_users_table.html" %} + + + + + +
UsernameAdded Links
@@ -147,6 +162,7 @@

Latest link events

getLinksCount(collection_id, form_data); getEditorCount(collection_id, form_data); getProjectCount(collection_id, form_data); + getTopPages(collection_id, form_data); {% endfor %} function openGraph(evt, collection, graphName) { @@ -240,4 +256,44 @@

Latest link events

}); } + function getTopPages(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:top_pages' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + // Show loading spinner + var loadingSpinner = document.createElement("div"); + loadingSpinner.id = "loading-spinner-pages"; + loadingSpinner.classList.add("spinner-border"); + loadingSpinner.role = "status"; + loadingSpinner.innerHTML = 'Loading...'; + document.getElementById(collection_id + "-top-pages-table").appendChild(loadingSpinner); + }, + // on success + success: function(response) { + document.getElementById("loading-spinner-pages").style.display = "none"; + // Building table data + pages = JSON.parse(response.top_pages) + for (var i = 0; i < pages.length; i++) { + var tr = document.createElement("tr"); + var tdPageName = document.createElement("td"); + var a = document.createElement("a"); + a.href = "https://" + pages[i].project_name+"/wiki/" + pages[i].page_name; + a.appendChild(document.createTextNode(pages[i].page_name)); + tdPageName.appendChild(a); + var tdLinks = document.createElement("td"); + tdLinks.innerHTML = pages[i].links_diff; + tr.appendChild(tdPageName); + tr.appendChild(tdLinks); + document.getElementById(collection_id + "-top-pages-table").appendChild(tr); + } + + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } + diff --git a/extlinks/organisations/urls.py b/extlinks/organisations/urls.py index a5f02e3d..13050632 100644 --- a/extlinks/organisations/urls.py +++ b/extlinks/organisations/urls.py @@ -8,7 +8,7 @@ get_editor_count, get_project_count, get_links_count, - get_top_organisations, + get_top_pages, get_top_projects, get_top_users, ) @@ -19,7 +19,7 @@ path("editor_count/", get_editor_count, name="editor_count"), path("project_count/", get_project_count, name="project_count"), path("links_count/", get_links_count, name="links_count"), - path("top_organisations/", get_top_organisations, name="top_organisations"), + path("top_pages/", get_top_pages, name="top_pages"), path("top_projects/", get_top_projects, name="top_projects"), path("top_users/", get_top_users, name="top_users"), # CSV downloads diff --git a/extlinks/organisations/views.py b/extlinks/organisations/views.py index e2dbb4d5..75f8d800 100644 --- a/extlinks/organisations/views.py +++ b/extlinks/organisations/views.py @@ -150,7 +150,6 @@ def _build_collection_context_dictionary(self, collection, context, form_data): queryset_filter = Q(collection=collection) context = self._fill_chart_context(collection, context, queryset_filter) - context = self._fill_totals_tables(context, queryset_filter) context = self._fill_latest_linkevents(collection, context, form_data) return context @@ -229,54 +228,6 @@ def _fill_chart_context(self, collection, context, queryset_filter): return context - def _fill_totals_tables(self, context, queryset_filter): - """ - This function adds the information for the Totals tables to the context - dictionary to display in OrganisationDetailView - - Parameters - ---------- - context : dict - The context dictionary that the function will be adding information to - - queryset_filter: Q - If the information is filtered, this set of filters will filter it. - The default is only filtering by the collection that is part of - the organisation - - Returns - ------- - dict : The context dictionary with the relevant statistics - """ - context["top_projects"] = ( - PageProjectAggregate.objects.filter(queryset_filter) - .values("project_name") - .annotate( - links_diff=Sum("total_links_added") - Sum("total_links_removed"), - ) - .order_by("-links_diff") - )[:5] - - context["top_pages"] = ( - PageProjectAggregate.objects.filter(queryset_filter) - .values("project_name", "page_name") - .annotate( - links_diff=Sum("total_links_added") - Sum("total_links_removed"), - ) - .order_by("-links_diff") - )[:5] - - context["top_users"] = ( - UserAggregate.objects.filter(queryset_filter) - .values("username") - .annotate( - links_diff=Sum("total_links_added") - Sum("total_links_removed"), - ) - .order_by("-links_diff") - )[:5] - - return context - def _fill_latest_linkevents(self, collection, context, form_data): """ This function gets the latest linkevents @@ -367,13 +318,76 @@ def get_links_count(request): return JsonResponse(response) -def get_top_organisations(request): - return +def get_top_pages(request): + """ + request : dict + Ajax request for the top pages table for a given collection + """ + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + top_pages = ( + PageProjectAggregate.objects.filter(queryset_filter) + .values("project_name", "page_name") + .annotate( + links_diff=Sum("total_links_added") - Sum("total_links_removed"), + ) + .order_by("-links_diff") + )[:5] + + serialized_pages = json.dumps(list(top_pages)) + response = {"top_pages": serialized_pages} + + return JsonResponse(response) def get_top_projects(request): - return + """ + request : dict + Ajax request for the top projects table for a given collection + """ + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + top_projects = ( + PageProjectAggregate.objects.filter(queryset_filter) + .values("project_name") + .annotate( + links_diff=Sum("total_links_added") - Sum("total_links_removed"), + ) + .order_by("-links_diff") + )[:5] + + serialized_projects = json.dumps(list(top_projects)) + response = {"top_projects": serialized_projects} + + return JsonResponse(response) def get_top_users(request): - return + """ + request : dict + Ajax request for the top users table for a given collection + """ + form_data = json.loads(request.GET.get("form_data", None)) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + queryset_filter = build_queryset_filters(form_data, {"collection": collection}) + top_users = ( + UserAggregate.objects.filter(queryset_filter) + .values("username") + .annotate( + links_diff=Sum("total_links_added") - Sum("total_links_removed"), + ) + .order_by("-links_diff") + )[:5] + + serialized_users = json.dumps(list(top_users)) + response = {"top_users": serialized_users} + + return JsonResponse(response) From 78977872b8568e805755f0ea435fe51e797cbc35 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Tue, 4 Jun 2024 21:34:34 -0600 Subject: [PATCH 04/15] Add async top projects on organisation view --- .../organisation_charts_include.html | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index f140907f..bbc3a94a 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -163,6 +163,7 @@

Latest link events

getEditorCount(collection_id, form_data); getProjectCount(collection_id, form_data); getTopPages(collection_id, form_data); + getTopProjects(collection_id, form_data); {% endfor %} function openGraph(evt, collection, graphName) { @@ -296,4 +297,41 @@

Latest link events

}); } + function getTopProjects(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:top_projects' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + // Show loading spinner + var loadingSpinner = document.createElement("div"); + loadingSpinner.id = "loading-spinner-projects"; + loadingSpinner.classList.add("spinner-border"); + loadingSpinner.role = "status"; + loadingSpinner.innerHTML = 'Loading...'; + document.getElementById(collection_id + "-top-projects-table").appendChild(loadingSpinner); + }, + // on success + success: function(response) { + document.getElementById("loading-spinner-projects").style.display = "none"; + // Building table data + projects = JSON.parse(response.top_projects) + for (var i = 0; i < projects.length; i++) { + var tr = document.createElement("tr"); + var tdProjectName = document.createElement("td"); + tdProjectName.appendChild(document.createTextNode(projects[i].project_name)); + var tdLinks = document.createElement("td"); + tdLinks.innerHTML = projects[i].links_diff; + tr.appendChild(tdProjectName); + tr.appendChild(tdLinks); + document.getElementById(collection_id + "-top-projects-table").appendChild(tr); + } + + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } + From bd816bcef38adf5cf8cabcce6952e53cfa2436e6 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Tue, 4 Jun 2024 21:47:52 -0600 Subject: [PATCH 05/15] Make top users asyn in organisations --- .../organisation_charts_include.html | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index bbc3a94a..828aa87b 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -164,6 +164,7 @@

Latest link events

getProjectCount(collection_id, form_data); getTopPages(collection_id, form_data); getTopProjects(collection_id, form_data); + getTopUsers(collection_id, form_data); {% endfor %} function openGraph(evt, collection, graphName) { @@ -334,4 +335,43 @@

Latest link events

}); } + function getTopUsers(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:top_users' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + //Show loading spinner + var loadingSpinner = document.createElement("div"); + loadingSpinner.id = "loading-spinner-users"; + loadingSpinner.classList.add("spinner-border"); + loadingSpinner.role = "status"; + loadingSpinner.innerHTML = 'Loading...'; + document.getElementById(collection_id + "-top-users-table").appendChild(loadingSpinner); + }, + // on success + success: function(response) { + document.getElementById("loading-spinner-users").style.display = "none"; + // Building table data + users = JSON.parse(response.top_users) + for (var i = 0; i < users.length; i++) { + var tr = document.createElement("tr"); + var tdUsername = document.createElement("td"); + var a = document.createElement("a"); + a.href = "https://meta.wikimedia.org/wiki/User:" + users[i].username; + a.appendChild(document.createTextNode(users[i].username)); + tdUsername.appendChild(a); + var tdLinks = document.createElement("td"); + tdLinks.innerHTML = users[i].links_diff; + tr.appendChild(tdUsername); + tr.appendChild(tdLinks); + document.getElementById(collection_id + "-top-users-table").appendChild(tr); + } + + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } From ab91e7ce83cd3c242ea43b688c6749c75f075281 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Wed, 5 Jun 2024 12:33:29 -0600 Subject: [PATCH 06/15] Add DummyCache for local dev envs --- extlinks/settings/local.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extlinks/settings/local.py b/extlinks/settings/local.py index 4be799e1..9a962370 100644 --- a/extlinks/settings/local.py +++ b/extlinks/settings/local.py @@ -31,3 +31,9 @@ def show_toolbar(request): DEBUG_TOOLBAR_CONFIG = { "SHOW_TOOLBAR_CALLBACK": show_toolbar, } + # Dummy Cache + CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.dummy.DummyCache", + } + } From 88a146e8504bef67ed904722037a206d811c364f Mon Sep 17 00:00:00 2001 From: suecarmol Date: Wed, 5 Jun 2024 13:39:38 -0600 Subject: [PATCH 07/15] Fix tests --- extlinks/organisations/tests.py | 87 ++++++++++++++++----------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/extlinks/organisations/tests.py b/extlinks/organisations/tests.py index 3c6efc92..96d37930 100644 --- a/extlinks/organisations/tests.py +++ b/extlinks/organisations/tests.py @@ -1,4 +1,5 @@ from datetime import datetime +import json from django.core.management import call_command from django.test import TestCase, RequestFactory @@ -131,82 +132,80 @@ def test_organisation_detail_links_added(self): Test that we're counting the correct total number of added links for this organisation. """ - factory = RequestFactory() - - request = factory.get(self.url1) - response = OrganisationDetailView.as_view()(request, pk=self.organisation1.pk) + form_data = "{}" + collection_id = self.collection1.id - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_added"], 3 + url = reverse("organisations:links_count") + url_with_params = "{url}?collection={collection}&form_data={form_data}".format( + url=url, collection=collection_id, form_data=form_data ) + response = self.client.get(url_with_params) + + self.assertEqual(json.loads(response.content)["links_added"], 3) def test_organisation_detail_links_removed(self): """ Test that we're counting the correct total number of removed links for this organisation. """ - factory = RequestFactory() + form_data = "{}" + collection_id = self.collection1.id - request = factory.get(self.url1) - response = OrganisationDetailView.as_view()(request, pk=self.organisation1.pk) - - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_removed"], - 1, + url = reverse("organisations:links_count") + url_with_params = "{url}?collection={collection}&form_data={form_data}".format( + url=url, collection=collection_id, form_data=form_data ) + response = self.client.get(url_with_params) + + self.assertEqual(json.loads(response.content)["links_removed"], 1) def test_organisation_detail_total_editors(self): """ Test that we're counting the correct total number of editors for this organisation. """ - factory = RequestFactory() - - request = factory.get(self.url1) - response = OrganisationDetailView.as_view()(request, pk=self.organisation1.pk) + form_data = "{}" + collection_id = self.collection1.id - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_editors"], - 3, + url = reverse("organisations:editor_count") + url_with_params = "{url}?collection={collection}&form_data={form_data}".format( + url=url, collection=collection_id, form_data=form_data ) + response = self.client.get(url_with_params) + + self.assertEqual(json.loads(response.content)["editor_count"], 3) def test_organisation_detail_date_form(self): """ Test that the date limiting form works on the organisation detail page. """ - factory = RequestFactory() + form_data = '{"start_date": "2019-01-01", "end_date": "2019-02-01"}' + collection_id = self.collection1.id - data = {"start_date": "2019-01-01", "end_date": "2019-02-01"} - - request = factory.get(self.url1, data) - response = OrganisationDetailView.as_view()(request, pk=self.organisation1.pk) - - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_added"], 2 - ) - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_removed"], - 0, + url = reverse("organisations:links_count") + url_with_params = "{url}?collection={collection}&form_data={form_data}".format( + url=url, collection=collection_id, form_data=form_data ) + response = self.client.get(url_with_params) + + self.assertEqual(json.loads(response.content)["links_added"], 2) + self.assertEqual(json.loads(response.content)["links_removed"], 0) def test_organisation_detail_user_list_form(self): """ Test that the user list limiting form works on the organisation detail page. """ - factory = RequestFactory() - - data = {"limit_to_user_list": True} - - request = factory.get(self.url1, data) - response = OrganisationDetailView.as_view()(request, pk=self.organisation1.pk) + form_data = '{"limit_to_user_list": True}' + collection_id = self.collection1.id - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_added"], 1 - ) - self.assertEqual( - response.context_data["collections"][self.collection1_key]["total_removed"], - 0, + url = reverse("organisations:links_count") + url_with_params = "{url}?collection={collection}&form_data={form_data}".format( + url=url, collection=collection_id, form_data=form_data ) + response = self.client.get(url_with_params) + + self.assertEqual(json.loads(response.content)["links_added"], 1) + self.assertEqual(json.loads(response.content)["links_removed"], 0) def test_organisation_detail_namespace_form(self): """ From 16dd99d24e8449335bf1a4cc7f01e84b2ba3e7c9 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Wed, 5 Jun 2024 18:08:40 -0600 Subject: [PATCH 08/15] Fix test --- extlinks/organisations/tests.py | 2 +- extlinks/organisations/views.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extlinks/organisations/tests.py b/extlinks/organisations/tests.py index 96d37930..ef1f1095 100644 --- a/extlinks/organisations/tests.py +++ b/extlinks/organisations/tests.py @@ -195,7 +195,7 @@ def test_organisation_detail_user_list_form(self): """ Test that the user list limiting form works on the organisation detail page. """ - form_data = '{"limit_to_user_list": True}' + form_data = '{"limit_to_user_list": true}' collection_id = self.collection1.id url = reverse("organisations:links_count") diff --git a/extlinks/organisations/views.py b/extlinks/organisations/views.py index 75f8d800..ce0808c1 100644 --- a/extlinks/organisations/views.py +++ b/extlinks/organisations/views.py @@ -267,7 +267,7 @@ def get_editor_count(request): request : dict Ajax request for editor count (found in the Statistics table) """ - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) @@ -281,7 +281,7 @@ def get_editor_count(request): def get_project_count(request): - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) @@ -299,7 +299,7 @@ def get_links_count(request): request : dict Ajax request for links count (found in the Statistics table) """ - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) @@ -323,7 +323,7 @@ def get_top_pages(request): request : dict Ajax request for the top pages table for a given collection """ - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) @@ -348,7 +348,7 @@ def get_top_projects(request): request : dict Ajax request for the top projects table for a given collection """ - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) @@ -373,7 +373,7 @@ def get_top_users(request): request : dict Ajax request for the top users table for a given collection """ - form_data = json.loads(request.GET.get("form_data", None)) + form_data = json.loads(request.GET.get("form_data", "{}")) collection_id = int(request.GET.get("collection", None)) collection = Collection.objects.get(id=collection_id) From 41da0083c45cd98f3c454e786784373f7c3f736f Mon Sep 17 00:00:00 2001 From: suecarmol Date: Thu, 6 Jun 2024 13:44:53 -0600 Subject: [PATCH 09/15] Make latest link events async query It will be loaded only when the Load button is clicked --- .../organisation_charts_include.html | 115 ++++++++++++++---- extlinks/organisations/urls.py | 2 + extlinks/organisations/views.py | 81 ++++++------ 3 files changed, 136 insertions(+), 62 deletions(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index 828aa87b..a376245d 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -71,27 +71,17 @@

Totals

Latest link events

- - +
+ + + + - {% for linkevent in collection.latest_links %} - {% if linkevent.change == 1 %} - - {% else %} - - {% endif %} - - - - - - - {% endfor %}
Download {% now "Y" %} CSV @@ -159,12 +149,18 @@

Latest link events

document.getElementById('{{ collection_name }}_linkEvents_button').click(); var form_data = {{ form_data|safe }}; var collection_id = {{ collection.collection_id|safe }}; - getLinksCount(collection_id, form_data); - getEditorCount(collection_id, form_data); - getProjectCount(collection_id, form_data); - getTopPages(collection_id, form_data); - getTopProjects(collection_id, form_data); - getTopUsers(collection_id, form_data); + document.body.onload = function() { + getLinksCount(collection_id, form_data); + getEditorCount(collection_id, form_data); + getProjectCount(collection_id, form_data); + getTopPages(collection_id, form_data); + getTopProjects(collection_id, form_data); + getTopUsers(collection_id, form_data); + // Do not display table header because there is no data on load + document.getElementById(collection_id + "-link-events-table-header").style.display = "none"; + document.getElementById(collection_id + "-loading-spinner-latest-link-events").style.display = "none"; + } + document.getElementById(collection_id + "-link-events-load-button").addEventListener("click", getLatestLinkEvents(collection_id, form_data)); {% endfor %} function openGraph(evt, collection, graphName) { @@ -365,7 +361,6 @@

Latest link events

tr.appendChild(tdLinks); document.getElementById(collection_id + "-top-users-table").appendChild(tr); } - }, // on error error: function(response) { @@ -374,4 +369,80 @@

Latest link events

} }); } + + function getLatestLinkEvents(collection_id, form_data){ + $.ajax({ + url: "{% url 'organisations:latest_link_events' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), + beforeSend: function() { + //Show loading spinner + var loadingSpinner = document.createElement("div"); + loadingSpinner.id = collection_id + "-loading-spinner-latest-link-events"; + loadingSpinner.classList.add("spinner-border"); + loadingSpinner.role = "status"; + loadingSpinner.innerHTML = 'Loading...'; + document.getElementById(collection_id + "-link-events-table").appendChild(loadingSpinner); + }, + // on success + success: function(response) { + document.getElementById(collection_id + "-loading-spinner-latest-link-events").style.display = "none"; + document.getElementById(collection_id + "-link-events-button-container").style.display = "none"; + document.getElementById(collection_id + "-link-events-table-header").style.display = "block"; + // Building table data + latest_link_events = JSON.parse(response.latest_link_events) + for (var i = 0; i < latest_link_events.length; i++) { + var tr = document.createElement("tr"); + if(latest_link_events[i].change === 1){ + tr.style.backgroundColor = "rgba(5,255,57,0.12)"; + } else{ + tr.style.backgroundColor = "rgba(255,46,44,0.12)"; + } + var tdLink = document.createElement("td"); + var aLink = document.createElement("a"); + aLink.href=latest_link_events[i].link; + aLink.appendChild(document.createTextNode(truncateString(latest_link_events[i].link, 50))); + tdLink.appendChild(aLink); + var tdUsername = document.createElement("td"); + var aUsername = document.createElement("a"); + aUsername.href = "https://"+latest_link_events[i].domain+"/wiki/User:" + latest_link_events[i].username__username; + aUsername.appendChild(document.createTextNode(truncateString(latest_link_events[i].username__username, 12))); + tdUsername.appendChild(aUsername); + var tdPageTitle = document.createElement("td"); + var aPageTitle = document.createElement("a"); + aPageTitle.href = "https://"+latest_link_events[i].domain+"/wiki/" + latest_link_events[i].page_title; + var transformedPageTitle = truncateString(latest_link_events[i].page_title, 20).replace("_", " "); + aPageTitle.appendChild(document.createTextNode(transformedPageTitle)); + tdPageTitle.appendChild(aPageTitle); + var tdDomain = document.createElement("td"); + tdDomain.innerHTML = latest_link_events[i].domain; + var tdRev = document.createElement("td"); + var aRev = document.createElement("a"); + aRev.href = "https://"+latest_link_events[i].domain+"/wiki/Special:Diff" + latest_link_events[i].rev_id; + aRev.appendChild(document.createTextNode(latest_link_events[i].date)); + tdRev.appendChild(aRev); + tr.appendChild(tdLink); + tr.appendChild(tdUsername); + tr.appendChild(tdPageTitle); + tr.appendChild(tdDomain); + tr.appendChild(tdRev); + document.getElementById(collection_id + "-link-events-table").appendChild(tr); + } + }, + // on error + error: function(response) { + // alert the error if any error occured + console.log(response.responseJSON.errors) + } + }); + } + + function truncateString(str, num){ + // If the length of str is less than or equal to num + // just return str--don't truncate it. + if (str.length <= num) { + return str + } + // Return str truncated with '...' concatenated to the end of str. + // Subtract 3 to account for the ... + return str.slice(0, num-3) + '...' + } diff --git a/extlinks/organisations/urls.py b/extlinks/organisations/urls.py index 13050632..2ac8409d 100644 --- a/extlinks/organisations/urls.py +++ b/extlinks/organisations/urls.py @@ -11,6 +11,7 @@ get_top_pages, get_top_projects, get_top_users, + get_latest_link_events, ) urlpatterns = [ @@ -22,6 +23,7 @@ path("top_pages/", get_top_pages, name="top_pages"), path("top_projects/", get_top_projects, name="top_projects"), path("top_users/", get_top_users, name="top_users"), + path("latest_link_events/", get_latest_link_events, name="latest_link_events"), # CSV downloads path("/csv/page_totals", CSVPageTotals.as_view(), name="csv_page_totals"), ] diff --git a/extlinks/organisations/views.py b/extlinks/organisations/views.py index ce0808c1..5f598cc4 100644 --- a/extlinks/organisations/views.py +++ b/extlinks/organisations/views.py @@ -2,7 +2,8 @@ import json import re -from django.db.models import Count, Sum, Q +from django.db.models import Count, Sum, Q, Prefetch, CharField +from django.db.models.functions import TruncDate, Cast from django.http import JsonResponse from django.views.generic import ListView, DetailView from django.views.decorators.cache import cache_page @@ -149,21 +150,17 @@ def _build_collection_context_dictionary(self, collection, context, form_data): else: queryset_filter = Q(collection=collection) - context = self._fill_chart_context(collection, context, queryset_filter) - context = self._fill_latest_linkevents(collection, context, form_data) + context = self._fill_chart_context(context, queryset_filter) return context - def _fill_chart_context(self, collection, context, queryset_filter): + def _fill_chart_context(self, context, queryset_filter): """ This function adds the chart information to the context dictionary to display in ProgramDetailView Parameters ---------- - collection : Collection - A collection that the aggregate data will be filtered from - context : dict The context dictionary that the function will be adding information to @@ -228,39 +225,6 @@ def _fill_chart_context(self, collection, context, queryset_filter): return context - def _fill_latest_linkevents(self, collection, context, form_data): - """ - This function gets the latest linkevents - - Parameters - ---------- - collection : Collection - A collection that the aggregate data will be filtered from - - context : dict - The context dictionary that the function will be adding information to - - form_data: dict|None - If the filter form has valid filters, then there will be a dictionary - to filter the linkevents table by dates or by user list - - Returns - ------- - dict : The context dictionary with the relevant statistics - """ - linkevents = collection.get_linkevents() - if form_data: - linkevents_filter = build_queryset_filters(form_data, {"linkevents": ""}) - context["latest_links"] = ( - linkevents.prefetch_related( - "username", "url", "url__collection", "url__collection__organisation" - ) - .filter(linkevents_filter) - .order_by("-timestamp")[:10] - ) - - return context - def get_editor_count(request): """ @@ -391,3 +355,40 @@ def get_top_users(request): response = {"top_users": serialized_users} return JsonResponse(response) + + +def get_latest_link_events(request): + """ + request : dict + Ajax request for the latest link events for a given collection + """ + form_data = json.loads(request.GET.get("form_data", "{}")) + collection_id = int(request.GET.get("collection", None)) + collection = Collection.objects.get(id=collection_id) + + linkevents = collection.get_linkevents() + if form_data: + linkevents_filter = build_queryset_filters(form_data, {"linkevents": ""}) + latest_link_events = ( + linkevents.select_related("username") + .prefetch_related( + "url", + Prefetch( + "url__collection", + queryset=Collection.objects.select_related("organisation").filter( + id=collection.id + ), + ), + ) + .filter(linkevents_filter) + .values( + "link", "domain", "page_title", "rev_id", "username__username", "change" + ) + .annotate(date=Cast("timestamp", CharField())) + .order_by("-date")[:10] + ) + + serialized_latest_link_events = json.dumps(list(latest_link_events)) + response = {"latest_link_events": serialized_latest_link_events} + + return JsonResponse(response) From 16904e01144d5cd69d05b6aaf5e12a45cfd7e553 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Thu, 6 Jun 2024 18:24:19 -0600 Subject: [PATCH 10/15] Change css rule --- .../templates/organisations/organisation_charts_include.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index a376245d..7f22aedf 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -386,7 +386,7 @@

Latest link events

success: function(response) { document.getElementById(collection_id + "-loading-spinner-latest-link-events").style.display = "none"; document.getElementById(collection_id + "-link-events-button-container").style.display = "none"; - document.getElementById(collection_id + "-link-events-table-header").style.display = "block"; + document.getElementById(collection_id + "-link-events-table-header").style.display = "table-row"; // Building table data latest_link_events = JSON.parse(response.latest_link_events) for (var i = 0; i < latest_link_events.length; i++) { From bb455ffd606e62d0e63d9979406e72dd2f27c74e Mon Sep 17 00:00:00 2001 From: suecarmol Date: Thu, 6 Jun 2024 19:01:34 -0600 Subject: [PATCH 11/15] Fix debug toolbar error on local testing --- extlinks/settings/local.py | 19 +++++++++++-------- extlinks/urls.py | 9 +++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/extlinks/settings/local.py b/extlinks/settings/local.py index 9a962370..4b4785f2 100644 --- a/extlinks/settings/local.py +++ b/extlinks/settings/local.py @@ -1,5 +1,6 @@ from .base import * from .logging import * +import sys DEBUG = True @@ -14,14 +15,16 @@ # so we can disable it by not passing a REQUIREMENTS_FILE variable when building # the docker containers if os.environ["REQUIREMENTS_FILE"] == "local.txt": - INSTALLED_APPS += [ - "debug_toolbar", - "django_extensions", - ] - - MIDDLEWARE += [ - "debug_toolbar.middleware.DebugToolbarMiddleware", - ] + TESTING = "test" in sys.argv + if not TESTING: + INSTALLED_APPS += [ + "debug_toolbar", + "django_extensions", + ] + + MIDDLEWARE += [ + "debug_toolbar.middleware.DebugToolbarMiddleware", + ] INTERNAL_IPS = ["127.0.0.1", "localhost", "0.0.0.0"] diff --git a/extlinks/urls.py b/extlinks/urls.py index cacf4a5e..e0643b18 100644 --- a/extlinks/urls.py +++ b/extlinks/urls.py @@ -25,8 +25,9 @@ ] if settings.DEBUG and os.environ["REQUIREMENTS_FILE"] == "local.txt": - import debug_toolbar + if not settings.TESTING: + import debug_toolbar - urlpatterns += [ - path("__debug__/", include(debug_toolbar.urls)), - ] + urlpatterns += [ + path("__debug__/", include(debug_toolbar.urls)), + ] From 228ea0f53bc6a7a2068f10d3f51af7a80660a606 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Thu, 6 Jun 2024 19:40:06 -0600 Subject: [PATCH 12/15] Fix bugs --- .../organisation_charts_include.html | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index 7f22aedf..41cc4fce 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -73,7 +73,9 @@

Totals

Latest link events

- + @@ -149,18 +151,15 @@

Latest link events

document.getElementById('{{ collection_name }}_linkEvents_button').click(); var form_data = {{ form_data|safe }}; var collection_id = {{ collection.collection_id|safe }}; - document.body.onload = function() { - getLinksCount(collection_id, form_data); - getEditorCount(collection_id, form_data); - getProjectCount(collection_id, form_data); - getTopPages(collection_id, form_data); - getTopProjects(collection_id, form_data); - getTopUsers(collection_id, form_data); - // Do not display table header because there is no data on load - document.getElementById(collection_id + "-link-events-table-header").style.display = "none"; - document.getElementById(collection_id + "-loading-spinner-latest-link-events").style.display = "none"; - } - document.getElementById(collection_id + "-link-events-load-button").addEventListener("click", getLatestLinkEvents(collection_id, form_data)); + console.log("Loading information for collection " + collection_id); + getLinksCount(collection_id, form_data); + getEditorCount(collection_id, form_data); + getProjectCount(collection_id, form_data); + getTopPages(collection_id, form_data); + getTopProjects(collection_id, form_data); + getTopUsers(collection_id, form_data); + // Do not display table header because there is no data on load + document.getElementById(collection_id + "-link-events-table-header").style.display = "none"; {% endfor %} function openGraph(evt, collection, graphName) { @@ -260,7 +259,7 @@

Latest link events

beforeSend: function() { // Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = "loading-spinner-pages"; + loadingSpinner.id = collection_id + "-loading-spinner-pages"; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; @@ -268,7 +267,7 @@

Latest link events

}, // on success success: function(response) { - document.getElementById("loading-spinner-pages").style.display = "none"; + document.getElementById(collection_id + "-loading-spinner-pages").style.display = "none"; // Building table data pages = JSON.parse(response.top_pages) for (var i = 0; i < pages.length; i++) { @@ -300,7 +299,7 @@

Latest link events

beforeSend: function() { // Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = "loading-spinner-projects"; + loadingSpinner.id = collection_id + "-loading-spinner-projects"; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; @@ -308,7 +307,7 @@

Latest link events

}, // on success success: function(response) { - document.getElementById("loading-spinner-projects").style.display = "none"; + document.getElementById(collection_id + "-loading-spinner-projects").style.display = "none"; // Building table data projects = JSON.parse(response.top_projects) for (var i = 0; i < projects.length; i++) { @@ -337,7 +336,7 @@

Latest link events

beforeSend: function() { //Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = "loading-spinner-users"; + loadingSpinner.id = collection_id + "-loading-spinner-users"; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; @@ -345,7 +344,7 @@

Latest link events

}, // on success success: function(response) { - document.getElementById("loading-spinner-users").style.display = "none"; + document.getElementById(collection_id + "-loading-spinner-users").style.display = "none"; // Building table data users = JSON.parse(response.top_users) for (var i = 0; i < users.length; i++) { @@ -388,7 +387,10 @@

Latest link events

document.getElementById(collection_id + "-link-events-button-container").style.display = "none"; document.getElementById(collection_id + "-link-events-table-header").style.display = "table-row"; // Building table data - latest_link_events = JSON.parse(response.latest_link_events) + latest_link_events = JSON.parse(response.latest_link_events); + if(latest_link_events.length < 1){ + document.getElementById(collection_id + "-link-events-table").appendChild(document.createTextNode("No data available")); + } for (var i = 0; i < latest_link_events.length; i++) { var tr = document.createElement("tr"); if(latest_link_events[i].change === 1){ From 843def97e47f13f8b8ecd64422e94e5caac598f1 Mon Sep 17 00:00:00 2001 From: suecarmol Date: Fri, 7 Jun 2024 12:19:22 -0600 Subject: [PATCH 13/15] Fix bugs and delete console.logs --- .../organisation_charts_include.html | 21 +++++++++---------- .../programs/program_charts_include.html | 12 +++++------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index 41cc4fce..6df72a12 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -147,11 +147,10 @@

Latest link events

}, }); - // Click all Link Events chart section TESTETETETETETETETET + // Click all Link Events chart section document.getElementById('{{ collection_name }}_linkEvents_button').click(); var form_data = {{ form_data|safe }}; var collection_id = {{ collection.collection_id|safe }}; - console.log("Loading information for collection " + collection_id); getLinksCount(collection_id, form_data); getEditorCount(collection_id, form_data); getProjectCount(collection_id, form_data); @@ -205,14 +204,14 @@

Latest link events

document.getElementById(collection_id + "-links-diff").style.color = "green"; } else{ - document.getElementById("links-diff").innerHTML = response.links_diff; - document.getElementById("links-diff").style.color = "red"; + document.getElementById(collection_id + "-links-diff").innerHTML = response.links_diff; + document.getElementById(collection_id + "-links-diff").style.color = "red"; } }, // on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors); + console.error(response.responseJSON.errors); } }); } @@ -230,7 +229,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -248,7 +247,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -288,7 +287,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -325,7 +324,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -364,7 +363,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -432,7 +431,7 @@

Latest link events

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } diff --git a/extlinks/programs/templates/programs/program_charts_include.html b/extlinks/programs/templates/programs/program_charts_include.html index c5836cb1..3c4c9a72 100644 --- a/extlinks/programs/templates/programs/program_charts_include.html +++ b/extlinks/programs/templates/programs/program_charts_include.html @@ -108,7 +108,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -126,7 +126,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -144,7 +144,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -184,7 +184,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -221,7 +221,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } @@ -261,7 +261,7 @@

Totals

// on error error: function(response) { // alert the error if any error occured - console.log(response.responseJSON.errors) + console.error(response.responseJSON.errors) } }); } From 99c44ea65935888726e6962a37bc6a6a1c9c9c2e Mon Sep 17 00:00:00 2001 From: suecarmol Date: Fri, 7 Jun 2024 12:34:01 -0600 Subject: [PATCH 14/15] Change variables from var to const --- .../templates/organisations/organisation_charts_include.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index 6df72a12..b9d1e27a 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -149,8 +149,8 @@

Latest link events

// Click all Link Events chart section document.getElementById('{{ collection_name }}_linkEvents_button').click(); - var form_data = {{ form_data|safe }}; - var collection_id = {{ collection.collection_id|safe }}; + const form_data = {{ form_data|safe }}; + const collection_id = {{ collection.collection_id|safe }}; getLinksCount(collection_id, form_data); getEditorCount(collection_id, form_data); getProjectCount(collection_id, form_data); From 28d870ded2d3adbca0d7e06f779f43f7dba1cc3a Mon Sep 17 00:00:00 2001 From: suecarmol Date: Fri, 7 Jun 2024 19:45:30 -0600 Subject: [PATCH 15/15] Add variables for DRY-ness --- .../organisation_charts_include.html | 88 ++++++++++++------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/extlinks/organisations/templates/organisations/organisation_charts_include.html b/extlinks/organisations/templates/organisations/organisation_charts_include.html index b9d1e27a..0346308b 100644 --- a/extlinks/organisations/templates/organisations/organisation_charts_include.html +++ b/extlinks/organisations/templates/organisations/organisation_charts_include.html @@ -187,25 +187,29 @@

Latest link events

} function getLinksCount(collection_id, form_data){ + const idLinksAdded = collection_id + "-links-added"; + const idLinksRemoved = collection_id + "-links-removed"; + const idLinksDiff = collection_id + "-links-diff"; + $.ajax({ url: "{% url 'organisations:links_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { - document.getElementById(collection_id + "-links-added").innerHTML = "Loading..."; - document.getElementById(collection_id + "-links-removed").innerHTML = "Loading..."; - document.getElementById(collection_id + "-links-diff").innerHTML = "Loading..."; + document.getElementById(idLinksAdded).innerHTML = "Loading..."; + document.getElementById(idLinksRemoved).innerHTML = "Loading..."; + document.getElementById(idLinksDiff).innerHTML = "Loading..."; }, // on success success: function(response) { - document.getElementById(collection_id + "-links-added").innerHTML = response.links_added; - document.getElementById(collection_id + "-links-removed").innerHTML = response.links_removed; + document.getElementById(idLinksAdded).innerHTML = response.links_added; + document.getElementById(idLinksRemoved).innerHTML = response.links_removed; if (response.links_diff > 0) { - document.getElementById(collection_id + "-links-diff").innerHTML = "+" + response.links_diff; - document.getElementById(collection_id + "-links-diff").style.color = "green"; + document.getElementById(idLinksDiff).innerHTML = "+" + response.links_diff; + document.getElementById(idLinksDiff).style.color = "green"; } else{ - document.getElementById(collection_id + "-links-diff").innerHTML = response.links_diff; - document.getElementById(collection_id + "-links-diff").style.color = "red"; + document.getElementById(idLinksDiff).innerHTML = response.links_diff; + document.getElementById(idLinksDiff).style.color = "red"; } }, // on error @@ -217,14 +221,16 @@

Latest link events

} function getEditorCount(collection_id, form_data){ + const idTotalEditors = collection_id + "-total-editors"; + $.ajax({ url: "{% url 'organisations:editor_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { - document.getElementById(collection_id + "-total-editors").innerHTML = "Loading..."; + document.getElementById(idTotalEditors).innerHTML = "Loading..."; }, // on success success: function(response) { - document.getElementById(collection_id + "-total-editors").innerHTML = response.editor_count; + document.getElementById(idTotalEditors).innerHTML = response.editor_count; }, // on error error: function(response) { @@ -235,14 +241,16 @@

Latest link events

} function getProjectCount(collection_id, form_data){ + const idTotalProjects = collection_id + "-total-projects"; + $.ajax({ url: "{% url 'organisations:project_count' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { - document.getElementById(collection_id + "-total-projects").innerHTML = "Loading..."; + document.getElementById(idTotalProjects).innerHTML = "Loading..."; }, // on success success: function(response) { - document.getElementById(collection_id + "-total-projects").innerHTML = response.project_count; + document.getElementById(idTotalProjects).innerHTML = response.project_count; }, // on error error: function(response) { @@ -253,34 +261,37 @@

Latest link events

} function getTopPages(collection_id, form_data){ + const idSpinnerPages = collection_id + "-loading-spinner-pages"; + const idTopPagesTable = collection_id + "-top-pages-table"; + $.ajax({ url: "{% url 'organisations:top_pages' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { // Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = collection_id + "-loading-spinner-pages"; + loadingSpinner.id = idSpinnerPages; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; - document.getElementById(collection_id + "-top-pages-table").appendChild(loadingSpinner); + document.getElementById(idTopPagesTable).appendChild(loadingSpinner); }, // on success success: function(response) { - document.getElementById(collection_id + "-loading-spinner-pages").style.display = "none"; + document.getElementById(idSpinnerPages).style.display = "none"; // Building table data pages = JSON.parse(response.top_pages) for (var i = 0; i < pages.length; i++) { var tr = document.createElement("tr"); var tdPageName = document.createElement("td"); var a = document.createElement("a"); - a.href = "https://" + pages[i].project_name+"/wiki/" + pages[i].page_name; + a.href = "https://" + pages[i].project_name + "/wiki/" + pages[i].page_name; a.appendChild(document.createTextNode(pages[i].page_name)); tdPageName.appendChild(a); var tdLinks = document.createElement("td"); tdLinks.innerHTML = pages[i].links_diff; tr.appendChild(tdPageName); tr.appendChild(tdLinks); - document.getElementById(collection_id + "-top-pages-table").appendChild(tr); + document.getElementById(idTopPagesTable).appendChild(tr); } }, @@ -293,20 +304,23 @@

Latest link events

} function getTopProjects(collection_id, form_data){ + const idSpinnerProjects = collection_id + "-loading-spinner-projects"; + const idTopProjectsTable = collection_id + "-top-projects-table"; + $.ajax({ url: "{% url 'organisations:top_projects' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { // Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = collection_id + "-loading-spinner-projects"; + loadingSpinner.id = idSpinnerProjects; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; - document.getElementById(collection_id + "-top-projects-table").appendChild(loadingSpinner); + document.getElementById(idTopProjectsTable).appendChild(loadingSpinner); }, // on success success: function(response) { - document.getElementById(collection_id + "-loading-spinner-projects").style.display = "none"; + document.getElementById(idSpinnerProjects).style.display = "none"; // Building table data projects = JSON.parse(response.top_projects) for (var i = 0; i < projects.length; i++) { @@ -317,7 +331,7 @@

Latest link events

tdLinks.innerHTML = projects[i].links_diff; tr.appendChild(tdProjectName); tr.appendChild(tdLinks); - document.getElementById(collection_id + "-top-projects-table").appendChild(tr); + document.getElementById(idTopProjectsTable).appendChild(tr); } }, @@ -330,20 +344,23 @@

Latest link events

} function getTopUsers(collection_id, form_data){ + const idSpinnerUsers = collection_id + "-loading-spinner-users"; + const idTopUsersTable = collection_id + "-top-users-table"; + $.ajax({ url: "{% url 'organisations:top_users' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { //Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = collection_id + "-loading-spinner-users"; + loadingSpinner.id = idSpinnerUsers; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; - document.getElementById(collection_id + "-top-users-table").appendChild(loadingSpinner); + document.getElementById(idTopUsersTable).appendChild(loadingSpinner); }, // on success success: function(response) { - document.getElementById(collection_id + "-loading-spinner-users").style.display = "none"; + document.getElementById(idSpinnerUsers).style.display = "none"; // Building table data users = JSON.parse(response.top_users) for (var i = 0; i < users.length; i++) { @@ -357,7 +374,7 @@

Latest link events

tdLinks.innerHTML = users[i].links_diff; tr.appendChild(tdUsername); tr.appendChild(tdLinks); - document.getElementById(collection_id + "-top-users-table").appendChild(tr); + document.getElementById(idTopUsersTable).appendChild(tr); } }, // on error @@ -369,26 +386,31 @@

Latest link events

} function getLatestLinkEvents(collection_id, form_data){ + const idSpinnerLinkEvents = collection_id + "-loading-spinner-latest-link-events"; + const idLinkEventsTable = collection_id + "-link-events-table"; + const idLinkEventsButtonContainer = collection_id + "-link-events-button-container"; + const idLinkEventsTableHeader = collection_id + "-link-events-table-header"; + $.ajax({ url: "{% url 'organisations:latest_link_events' %}?collection=" + collection_id + "&form_data=" + JSON.stringify(form_data), beforeSend: function() { //Show loading spinner var loadingSpinner = document.createElement("div"); - loadingSpinner.id = collection_id + "-loading-spinner-latest-link-events"; + loadingSpinner.id = idSpinnerLinkEvents; loadingSpinner.classList.add("spinner-border"); loadingSpinner.role = "status"; loadingSpinner.innerHTML = 'Loading...'; - document.getElementById(collection_id + "-link-events-table").appendChild(loadingSpinner); + document.getElementById(idLinkEventsTable).appendChild(loadingSpinner); }, // on success success: function(response) { - document.getElementById(collection_id + "-loading-spinner-latest-link-events").style.display = "none"; - document.getElementById(collection_id + "-link-events-button-container").style.display = "none"; - document.getElementById(collection_id + "-link-events-table-header").style.display = "table-row"; + document.getElementById(idSpinnerLinkEvents).style.display = "none"; + document.getElementById(idLinkEventsButtonContainer).style.display = "none"; + document.getElementById(idLinkEventsTableHeader).style.display = "table-row"; // Building table data latest_link_events = JSON.parse(response.latest_link_events); if(latest_link_events.length < 1){ - document.getElementById(collection_id + "-link-events-table").appendChild(document.createTextNode("No data available")); + document.getElementById(idLinkEventsTable).appendChild(document.createTextNode("No data available")); } for (var i = 0; i < latest_link_events.length; i++) { var tr = document.createElement("tr"); @@ -425,7 +447,7 @@

Latest link events

tr.appendChild(tdPageTitle); tr.appendChild(tdDomain); tr.appendChild(tdRev); - document.getElementById(collection_id + "-link-events-table").appendChild(tr); + document.getElementById(idLinkEventsTable).appendChild(tr); } }, // on error