From 2bfab6c4ab0173420e413ef9ebc09318e40406bb Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Wed, 31 Dec 2025 11:59:25 -0500 Subject: [PATCH 1/5] feat: fetch language from cookie set on Google translate selected language --- web-app/django/VIM/apps/main/views.py | 40 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/web-app/django/VIM/apps/main/views.py b/web-app/django/VIM/apps/main/views.py index 25987f9d..8bdfa207 100644 --- a/web-app/django/VIM/apps/main/views.py +++ b/web-app/django/VIM/apps/main/views.py @@ -47,21 +47,41 @@ def home(request): .order_by("-language_count")[:5] ) + # Get language from cookie (default 'en') + try: + user_language = request.COOKIES.get("googtrans", "/en/en") + user_language_code = user_language.split("/")[-1] + target_language_obj = Language.objects.filter( + wikidata_code=user_language_code + ).first() + target_language_label = ( + target_language_obj.en_label if target_language_obj else "English" + ) + except: + target_language_label = "English" + instruments_chart_data = [] for instrument in top_instruments_by_languages: - # Get the English name if available, otherwise use the first available name - try: - english_name = instrument.instrumentname_set.filter( + # Try user-selected language + instrument_name_obj = instrument.instrumentname_set.filter( + language__en_label=target_language_label + ).first() + + # Fallback to English + if not instrument_name_obj: + instrument_name_obj = instrument.instrumentname_set.filter( language__en_label="English" ).first() - name = ( - english_name.name - if english_name - else instrument.instrumentname_set.first().name - ) - except: - name = f"Instrument {instrument.wikidata_id}" + # Fallback to first available + if not instrument_name_obj: + instrument_name_obj = instrument.instrumentname_set.first() + + name = ( + instrument_name_obj.name + if instrument_name_obj + else f"Instrument {instrument.wikidata_id}" + ) instruments_chart_data.append( {"name": name, "count": instrument.language_count} ) From 0d5fda4374fb76d4926f23cff74c853b87413c93 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Wed, 31 Dec 2025 12:01:31 -0500 Subject: [PATCH 2/5] feat: re-fetch stat bar data when language changes via Google Translate --- web-app/frontend/src/GoogleTranslate.ts | 20 ++++++++++++++++++++ web-app/frontend/src/stats/BarCharts.ts | 9 +++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/web-app/frontend/src/GoogleTranslate.ts b/web-app/frontend/src/GoogleTranslate.ts index a9de2bfe..7839f1c8 100644 --- a/web-app/frontend/src/GoogleTranslate.ts +++ b/web-app/frontend/src/GoogleTranslate.ts @@ -1,4 +1,5 @@ import { setCookie, readCookie, deleteCookie } from './utils/cookies'; +import { barChartsInstance, ChartData } from './stats/BarCharts'; declare namespace google { namespace translate { @@ -92,6 +93,25 @@ function customizeGoogleTranslate() { // Set up observer to watch for Google Translate widget changes watchGTDefaultText(googleSelect); + + googleSelect.addEventListener('change', () => + setTimeout(async () => { + const doc = new DOMParser().parseFromString( + await ( + await fetch(location.pathname, { credentials: 'same-origin' }) + ).text(), + 'text/html', + ); + (window as any).barChartsInstance.update( + JSON.parse( + doc.getElementById('instruments-chart-data')?.textContent || '[]', + ), + JSON.parse( + doc.getElementById('languages-chart-data')?.textContent || '[]', + ), + ); + }, 500), + ); } // Watch for the default text of the Google Translate widget to be "Select Language" diff --git a/web-app/frontend/src/stats/BarCharts.ts b/web-app/frontend/src/stats/BarCharts.ts index 75d1bc0b..49525466 100644 --- a/web-app/frontend/src/stats/BarCharts.ts +++ b/web-app/frontend/src/stats/BarCharts.ts @@ -347,7 +347,12 @@ class BarCharts { .delay((d: ChartData, i: number) => labelStartDelay + i * 50) .style('opacity', 1); } + public update(instruments: ChartData[], languages: ChartData[]): void { + this.createInstrumentsChart(instruments); + this.createLanguagesChart(languages); + this.setupIntersectionObserver(); + } } -// Initialize the charts when this module is loaded -new BarCharts(); +export const barChartsInstance = new BarCharts(); +(window as any).barChartsInstance = barChartsInstance; From 0b9a7710738577f02f5c18c540084d2324cda919 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Wed, 31 Dec 2025 14:00:15 -0500 Subject: [PATCH 3/5] feat: add responsive chart resize handler --- web-app/frontend/src/stats/BarCharts.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/web-app/frontend/src/stats/BarCharts.ts b/web-app/frontend/src/stats/BarCharts.ts index 49525466..9eb4d2c1 100644 --- a/web-app/frontend/src/stats/BarCharts.ts +++ b/web-app/frontend/src/stats/BarCharts.ts @@ -18,9 +18,13 @@ class BarCharts { private init(): void { // Wait for DOM to be ready if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => this.createCharts()); + document.addEventListener('DOMContentLoaded', () => { + this.createCharts(); + this.setupResizeObserver(); + }); } else { this.createCharts(); + this.setupResizeObserver(); } } @@ -347,10 +351,23 @@ class BarCharts { .delay((d: ChartData, i: number) => labelStartDelay + i * 50) .style('opacity', 1); } + public update(instruments: ChartData[], languages: ChartData[]): void { this.createInstrumentsChart(instruments); this.createLanguagesChart(languages); this.setupIntersectionObserver(); + this.setupResizeObserver(); + } + + private setupResizeObserver(): void { + let resizeTimeout: number; + + window.addEventListener('resize', () => { + clearTimeout(resizeTimeout); + resizeTimeout = window.setTimeout(() => { + this.createCharts(); + }, 250); + }); } } From 4037651d0eaecaf2988d426ea3e46b77c247ec92 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Wed, 31 Dec 2025 14:03:26 -0500 Subject: [PATCH 4/5] feat: add RTL support to text alignment of charts --- web-app/django/VIM/templates/main/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web-app/django/VIM/templates/main/index.html b/web-app/django/VIM/templates/main/index.html index f17b6c18..495e130a 100644 --- a/web-app/django/VIM/templates/main/index.html +++ b/web-app/django/VIM/templates/main/index.html @@ -144,11 +144,11 @@

{{ total_users|default:"0" }}

-
-
+
+
-
+

Top Instruments by Language Diversity

Discover which musical instruments have names in the most languages, @@ -158,7 +158,7 @@

Top Instruments by Language Diversity

-
+

Top Languages by Instrument Coverage

Explore which languages contribute the most instrument names to our lexicon, @@ -166,8 +166,8 @@

Top Languages by Instrument Coverage

-
-
+
+
From 58ea6a8fe92203d5f804f1d3ccaaac4aeaff6878 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Wed, 31 Dec 2025 19:09:01 -0500 Subject: [PATCH 5/5] fix: only count the verified instruments names for stats --- web-app/django/VIM/apps/main/views.py | 40 +++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/web-app/django/VIM/apps/main/views.py b/web-app/django/VIM/apps/main/views.py index 8bdfa207..0462d09a 100644 --- a/web-app/django/VIM/apps/main/views.py +++ b/web-app/django/VIM/apps/main/views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.contrib.auth import authenticate, login from django.contrib.auth.models import User -from django.db.models import Count +from django.db.models import Count, Q from django.shortcuts import redirect, render from django.urls import reverse from django.utils.http import url_has_allowed_host_and_scheme @@ -33,17 +33,19 @@ def home(request): # Fetch statistics from database total_instruments = Instrument.objects.count() total_languages = Language.objects.count() - total_names = InstrumentName.objects.count() - total_users = User.objects.filter( - is_active=True - ).count() # Users who have completed email registration + total_names = InstrumentName.objects.filter(verification_status="verified").count() + total_editors = User.objects.count() - # Chart data: Top 5 instruments with most languages + # Chart data: Top 5 instruments with most languages (only verified names) top_instruments_by_languages = ( Instrument.objects.annotate( - language_count=Count("instrumentname__language", distinct=True) + language_count=Count( + "instrumentname__language", + filter=Q(instrumentname__verification_status="verified"), + distinct=True, + ) ) - .filter(language_count__gt=0) # Only instruments with at least one language + .filter(language_count__gt=0) .order_by("-language_count")[:5] ) @@ -64,18 +66,20 @@ def home(request): for instrument in top_instruments_by_languages: # Try user-selected language instrument_name_obj = instrument.instrumentname_set.filter( - language__en_label=target_language_label + language__en_label=target_language_label, verification_status="verified" ).first() # Fallback to English if not instrument_name_obj: instrument_name_obj = instrument.instrumentname_set.filter( - language__en_label="English" + language__en_label="English", verification_status="verified" ).first() - # Fallback to first available + # Fallback to first available (must be verified) if not instrument_name_obj: - instrument_name_obj = instrument.instrumentname_set.first() + instrument_name_obj = instrument.instrumentname_set.filter( + verification_status="verified" + ).first() name = ( instrument_name_obj.name @@ -86,14 +90,16 @@ def home(request): {"name": name, "count": instrument.language_count} ) - # Chart data: Top 5 languages with most instrument names + # Chart data: Top 5 languages with most instrument names (only verified instrument names) top_languages_by_names = ( Language.objects.annotate( - instrument_count=Count("instrumentname", distinct=True) + instrument_count=Count( + "instrumentname", + filter=Q(instrumentname__verification_status="verified"), + distinct=True, + ) ) - .filter( - instrument_count__gt=0 - ) # Only languages with at least one instrument name + .filter(instrument_count__gt=0) .order_by("-instrument_count")[:5] )