From 09d416f7d6c1a09b828870b7377a776b370f5b1d Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 12 Dec 2025 00:31:03 +0000 Subject: [PATCH 1/5] Remove day from dates --- package-lock.json | 12 +++++++++++- src/js/techreport/tableLinked.js | 2 +- src/js/techreport/timeseries.js | 6 +++--- src/js/techreport/utils/ui.js | 14 ++++++++++++++ src/js/timeseries.js | 12 ++++++++++-- src/js/utils.js | 11 ++++++++++- 6 files changed, 49 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe7abf4a..72f5d07a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -344,6 +344,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -369,6 +370,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -476,6 +478,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -1611,6 +1614,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -1659,6 +1663,7 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -2048,7 +2053,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true + "dev": true, + "peer": true }, "acorn-import-phases": { "version": "1.0.3", @@ -2062,6 +2068,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2129,6 +2136,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", "dev": true, + "peer": true, "requires": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -2908,6 +2916,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, + "peer": true, "requires": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -2941,6 +2950,7 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, + "peer": true, "requires": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", diff --git a/src/js/techreport/tableLinked.js b/src/js/techreport/tableLinked.js index 5be37525..09d69c75 100644 --- a/src/js/techreport/tableLinked.js +++ b/src/js/techreport/tableLinked.js @@ -81,7 +81,7 @@ class TableLinked { } if(timestamp) { - timestamp.textContent = this.dataArray[1]?.[0]?.date; + timestamp.textContent = UIUtils.printMonthYear(this.dataArray[1]?.[0]?.date); } this.dataArray.forEach(technology => { diff --git a/src/js/techreport/timeseries.js b/src/js/techreport/timeseries.js index 5415002e..07dd3a3f 100644 --- a/src/js/techreport/timeseries.js +++ b/src/js/techreport/timeseries.js @@ -131,7 +131,7 @@ class Timeseries { container.innerHTML = ''; /* Update the date to the most recent timestamp in the dataset */ - viz.querySelector('[data-slot="timestamp"]').innerHTML = sorted?.[0]?.date; + viz.querySelector('[data-slot="timestamp"]').innerHTML = UIUtils.printMonthYear(sorted?.[0]?.date); /* For each of the breakdowns, add a component with the latest data */ config.series.values.forEach(breakdown => { @@ -239,7 +239,7 @@ class Timeseries { value.classList.add('undefined'); value.textContent = 'No data'; } - timestamp.textContent = latest.date; + timestamp.textContent = UIUtils.printMonthYear(latest.date); const techColor = UIUtils.getAppColor(app, this.pageFilters.app, this.pageConfig.colors); const fallback = this.pageConfig.colors.app[index]; card.style.setProperty('--breakdown-color', techColor || fallback); @@ -314,7 +314,7 @@ class Timeseries { const wrapper = document.createElement('div'); wrapper.className = 'tooltip-wrapper'; - const d = Highcharts.dateFormat('%b %e, %Y', this.x); + const d = Highcharts.dateFormat('%b %Y', this.x); const dateEl = document.createElement('p'); dateEl.innerHTML = d; diff --git a/src/js/techreport/utils/ui.js b/src/js/techreport/utils/ui.js index 244aafef..30b5675b 100644 --- a/src/js/techreport/utils/ui.js +++ b/src/js/techreport/utils/ui.js @@ -61,9 +61,23 @@ function capitalizeFirstLetter(theString) { return theString && typeof theString === 'string' ? theString.charAt(0)?.toUpperCase() + theString.slice(1) : theString; } +function printMonthYear(theDate) { + if (!theDate || theDate.length != 10) return; + + const [year, month, day] = theDate.split('-'); + const date = new Date(year, month - 1); + const formattedDate = date.toLocaleString('default', { + month: 'long', + year: 'numeric' + }); + + return formattedDate; +} + export const UIUtils = { getAppColor, updateReportComponents, getChangeStatus, capitalizeFirstLetter, + printMonthYear, } diff --git a/src/js/timeseries.js b/src/js/timeseries.js index b949c2e6..1ff1d8f7 100644 --- a/src/js/timeseries.js +++ b/src/js/timeseries.js @@ -317,7 +317,14 @@ function drawChart(options, series) { } const changelog = flags[this.x]; - const tooltip = `

${Highcharts.dateFormat('%b %e, %Y', this.x)}

`; + + // Drop the 1st day from dates as data is for month, rather than + // specific date. However, include the non-01 dates from when we + // used to run an early (01) and late (15 typically) crawl. + const formattedDate = Highcharts.dateFormat('%e', this.x) === ' 1' ? + Highcharts.dateFormat('%b %Y', this.x) : + Highcharts.dateFormat('%b %e, %Y', this.x); + const tooltip = `

${formattedDate}

`; // Handle changelog tooltips first. if (!this.points) { @@ -390,7 +397,8 @@ function drawChart(options, series) { }, { type: 'all', text: 'All' - }] + }], + inputDateFormat: '%b %Y', }, xAxis: { type: 'datetime', diff --git a/src/js/utils.js b/src/js/utils.js index efbd86bb..121530e8 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -7,10 +7,19 @@ const prettyDateFormatter = new Intl.DateTimeFormat(undefined, { timeZone: 'UTC' }); +const prettyShortDateFormatter = new Intl.DateTimeFormat(undefined, { + month: 'short', + year: 'numeric', + timeZone: 'UTC' +}); + export const prettyDate = YYYY_MM_DD => { const [YYYY, MM, DD] = YYYY_MM_DD.split('_'); const d = new Date(Date.UTC(YYYY, MM - 1, DD)); - return getFullDate(d); + const formattedDate = DD === '01' ? + prettyShortDateFormatter.format(d) : + prettyDateFormatter.format(d); + return formattedDate; }; export const getFullDate = d => { From 076cb00814aa56318305aacee664ec5dbd0500b5 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 12 Dec 2025 01:01:21 +0000 Subject: [PATCH 2/5] Potential fix for code scanning alert no. 72: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/js/techreport/utils/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/techreport/utils/ui.js b/src/js/techreport/utils/ui.js index 30b5675b..ba765f8c 100644 --- a/src/js/techreport/utils/ui.js +++ b/src/js/techreport/utils/ui.js @@ -64,7 +64,7 @@ function capitalizeFirstLetter(theString) { function printMonthYear(theDate) { if (!theDate || theDate.length != 10) return; - const [year, month, day] = theDate.split('-'); + const [year, month] = theDate.split('-'); const date = new Date(year, month - 1); const formattedDate = date.toLocaleString('default', { month: 'long', From a2b0d9000e2dc4a2aa0b38b3feb4bf7c5ef2e6c1 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 12 Dec 2025 15:04:33 +0000 Subject: [PATCH 3/5] Update src/js/timeseries.js Co-authored-by: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> --- src/js/timeseries.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/timeseries.js b/src/js/timeseries.js index 1ff1d8f7..c984abd6 100644 --- a/src/js/timeseries.js +++ b/src/js/timeseries.js @@ -318,11 +318,11 @@ function drawChart(options, series) { const changelog = flags[this.x]; - // Drop the 1st day from dates as data is for month, rather than - // specific date. However, include the non-01 dates from when we - // used to run an early (01) and late (15 typically) crawl. - const formattedDate = Highcharts.dateFormat('%e', this.x) === ' 1' ? - Highcharts.dateFormat('%b %Y', this.x) : + // Use short format (month + year) for dates from 2019 onwards when + // we switched to monthly crawls. Show full date for older data that + // may have had mid-month crawls. + const formattedDate = this.x >= Date.UTC(2019, 0, 1) ? + Highcharts.dateFormat('%b %Y', this.x) : Highcharts.dateFormat('%b %e, %Y', this.x); const tooltip = `

${formattedDate}

`; From c8a36a9518c57247ffa04c19ed78b18b1b78d6e7 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 12 Dec 2025 18:06:54 +0000 Subject: [PATCH 4/5] Fixes --- server/dates.py | 3 --- src/js/utils.js | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server/dates.py b/server/dates.py index cd996860..f6911ccb 100644 --- a/server/dates.py +++ b/server/dates.py @@ -38,9 +38,6 @@ def mock_get_dates(start_year, end_year, today, month_delta=0): current_month_in_year = "0{}".format(current_month_in_year) months.append("{}_{}_01".format(year, current_month_in_year)) - if year == end_year and current_month_in_year == month_delta and today < 15: - continue - months.append("{}_{}_15".format(year, current_month_in_year)) if month % 12 == 0: year = year + 1 diff --git a/src/js/utils.js b/src/js/utils.js index 121530e8..818680e9 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -15,11 +15,13 @@ const prettyShortDateFormatter = new Intl.DateTimeFormat(undefined, { export const prettyDate = YYYY_MM_DD => { const [YYYY, MM, DD] = YYYY_MM_DD.split('_'); - const d = new Date(Date.UTC(YYYY, MM - 1, DD)); - const formattedDate = DD === '01' ? - prettyShortDateFormatter.format(d) : - prettyDateFormatter.format(d); - return formattedDate; + if (YYYY > '2018') { + const d = new Date(Date.UTC(YYYY, MM - 1)); + return prettyShortDateFormatter.format(d); + } else { + const d = new Date(Date.UTC(YYYY, MM - 1, DD)); + return prettyDateFormatter.format(d); + } }; export const getFullDate = d => { From 36b80b9258066fa3e5a8bd2c1fb9ddc225a8047e Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Fri, 12 Dec 2025 18:13:16 +0000 Subject: [PATCH 5/5] Fix mock dates --- server/dates.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/dates.py b/server/dates.py index f6911ccb..0f1f3d6d 100644 --- a/server/dates.py +++ b/server/dates.py @@ -38,6 +38,8 @@ def mock_get_dates(start_year, end_year, today, month_delta=0): current_month_in_year = "0{}".format(current_month_in_year) months.append("{}_{}_01".format(year, current_month_in_year)) + if year < 2019: + months.append("{}_{}_15".format(year, current_month_in_year)) if month % 12 == 0: year = year + 1