From 73109d2f64e0b3b06696432c569e101dd5162da5 Mon Sep 17 00:00:00 2001 From: Richard Abrich Date: Sun, 16 Jun 2024 09:54:59 -0400 Subject: [PATCH] add DownloadStats.js --- components/Developers.js | 3 ++ components/DownloadStats.js | 91 +++++++++++++++++++++++++++++++++++++ package-lock.json | 78 +++++++++++++++++++++++++++++++ package.json | 3 ++ 4 files changed, 175 insertions(+) create mode 100644 components/DownloadStats.js diff --git a/components/Developers.js b/components/Developers.js index de71823..07cd10a 100644 --- a/components/Developers.js +++ b/components/Developers.js @@ -4,6 +4,8 @@ import { faWindows, faApple } from '@fortawesome/free-brands-svg-icons'; import Link from 'next/link'; import styles from './Developers.module.css'; import { getReleasesDownloadCount } from 'utils/githubStats'; +import DownloadStats from './DownloadStats'; + export default function Developers() { const [latestRelease, setLatestRelease] = useState({ version: null, date: null }); @@ -162,6 +164,7 @@ export default function Developers() { style={{ border: '0', borderRadius: '6px' }} className="mx-auto mb-4" > +

Don't forget to{' '} Register for Updates. diff --git a/components/DownloadStats.js b/components/DownloadStats.js new file mode 100644 index 0000000..fa82b94 --- /dev/null +++ b/components/DownloadStats.js @@ -0,0 +1,91 @@ +import React, { useState, useEffect } from 'react'; +import { Line } from 'react-chartjs-2'; +import 'chart.js/auto'; + +const DownloadStats = () => { + const [chartData, setChartData] = useState({ + labels: [], + datasets: [ + { + label: 'Daily Downloads', + data: [], + borderColor: 'rgb(75, 192, 192)', + backgroundColor: 'rgba(75, 192, 192, 0.5)', + }, + { + label: 'Cumulative Total Downloads', + data: [], + borderColor: 'rgb(255, 99, 132)', + backgroundColor: 'rgba(255, 99, 132, 0.5)', + }, + ], + }); + + const fetchReleaseData = async (page = 1) => { + const queryParams = new URLSearchParams({ + per_page: 30, + page, + }); + const response = await fetch(`https://api.github.com/repos/OpenAdaptAI/OpenAdapt/releases?${queryParams.toString()}`); + return response.json(); + }; + + useEffect(() => { + let cumulativeTotalDownloads = 0; + let allReleases = []; + + const processReleases = async (page = 1) => { + const releaseData = await fetchReleaseData(page); + if (releaseData.length === 0) return; + + allReleases = [...allReleases, ...releaseData]; + if (releaseData.length === 30) { + await processReleases(page + 1); + } else { + // Sort releases by published date + allReleases.sort((a, b) => new Date(a.published_at) - new Date(b.published_at)); + + const labels = []; + const dailyDownloads = []; + const cumulativeDownloads = []; + + allReleases.forEach(release => { + const date = new Date(release.published_at).toLocaleDateString(); + labels.push(date); + + // Filter assets that are ZIP files and accumulate download counts + const dailyTotalDownloads = release.assets.reduce((acc, asset) => { + if (asset.name.endsWith('.zip')) { + return acc + asset.download_count; + } + return acc; + }, 0); + + cumulativeTotalDownloads += dailyTotalDownloads; + dailyDownloads.push(dailyTotalDownloads); + cumulativeDownloads.push(cumulativeTotalDownloads); + }); + + setChartData({ + labels, + datasets: [ + { ...chartData.datasets[0], data: dailyDownloads }, + { ...chartData.datasets[1], data: cumulativeDownloads }, + ], + }); + } + }; + + processReleases(); + }, []); + + return ( +

+

Download Statistics

+ +
+ ); +}; + +export default DownloadStats; + diff --git a/package-lock.json b/package-lock.json index d0c4897..7cb47c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "chart.js": "^4.4.3", + "chartjs-adapter-date-fns": "^3.0.0", "cypress": "^10.0.3", "daisyui": "^4.7.2", "eslint-config-next": "^13.3.4", @@ -20,6 +22,7 @@ "next": "^14.1.2", "p5": "^1.9.1", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-fontawesome": "^1.7.1" }, @@ -365,6 +368,11 @@ "node": ">= 8.0.0" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@next/env": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.2.tgz", @@ -1665,6 +1673,26 @@ "node": ">=8" } }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-adapter-date-fns": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz", + "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==", + "peerDependencies": { + "chart.js": ">=2.8.0", + "date-fns": ">=2.0.0" + } + }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -2243,6 +2271,16 @@ "node": ">=0.10" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", @@ -6517,6 +6555,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -8380,6 +8427,11 @@ "vary": "^1.1.2" } }, + "@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "@next/env": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.2.tgz", @@ -9280,6 +9332,20 @@ } } }, + "chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "requires": { + "@kurkle/color": "^0.3.0" + } + }, + "chartjs-adapter-date-fns": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz", + "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==", + "requires": {} + }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -9725,6 +9791,12 @@ "assert-plus": "^1.0.0" } }, + "date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "peer": true + }, "dayjs": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", @@ -12816,6 +12888,12 @@ "loose-envify": "^1.1.0" } }, + "react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "requires": {} + }, "react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", diff --git a/package.json b/package.json index 57c7c3e..68cac34 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "chart.js": "^4.4.3", + "chartjs-adapter-date-fns": "^3.0.0", "cypress": "^10.0.3", "daisyui": "^4.7.2", "eslint-config-next": "^13.3.4", @@ -30,6 +32,7 @@ "next": "^14.1.2", "p5": "^1.9.1", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-fontawesome": "^1.7.1" },