From 71dacd527cedb5efcb3e62146c9a50c294642b76 Mon Sep 17 00:00:00 2001 From: Deepak Yadav Date: Mon, 2 Sep 2024 13:23:33 +0530 Subject: [PATCH] fix: changes in updateProjects() cronjob for pypi packages --- src/app/packages/page.tsx | 345 +++++++++++++---------------- src/app/projects/assets/stats.json | 11 + updateProject.mjs | 69 ++++-- 3 files changed, 224 insertions(+), 201 deletions(-) diff --git a/src/app/packages/page.tsx b/src/app/packages/page.tsx index 8a38f3f1..e247e98c 100644 --- a/src/app/packages/page.tsx +++ b/src/app/packages/page.tsx @@ -4,6 +4,7 @@ import React, { useEffect, useState, Fragment } from "react"; import statsList from "../projects/assets/stats.json"; import Link from "next/link"; import npm from "../../../public/images/social-media/npm-svgrepo-com.svg"; +import pypi from "../../../public/images/social-media/pypi-svg.svg"; import filter from "../../../public/images/social-media/bx-filter-alt.svg"; import download from "../../../public/images/bxs-download.svg"; import github from "../../../public/images/bxl-github.svg"; @@ -11,31 +12,39 @@ import Image from "next/image"; import { Dialog, Transition } from "@headlessui/react"; import moment from "moment"; -type stats = { - downloads: download[]; +type Package = { + name: string; + type: "npm" | "pypi"; + day?: number; + week?: number; + year?: number; + total?: number; + last_day?: number; + last_week?: number; + last_month?: number; }; -type download = { - downloads: number; - day: string; +type NpmStats = { + downloads: { downloads: number; day: string }[]; }; const Stats = () => { const [startDate, setStartDate] = useState(moment().format("YYYY-MM-DD")); const [endDate, setEndDate] = useState(moment().format("YYYY-MM-DD")); - const [loading, setLoading] = useState(false); // State to track loading status + const [loading, setLoading] = useState(false); const [count, setCount] = useState(0); const [selectedRange, setSelectedRange] = useState(false); const [isOpen, setIsOpen] = useState(false); - const [npmPackage, setNpmPackage] = useState({ + const [selectedPackage, setSelectedPackage] = useState({ name: "fmdapi-node-weaver", + type: "npm", day: 0, week: 3, year: 70, total: 70, }); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [packages, setPackages] = useState(statsList); + const [packages, setPackages] = useState(statsList as Package[]); function closeModal() { setIsOpen(false); @@ -43,18 +52,28 @@ const Stats = () => { } useEffect(() => { - if (npmPackage) { - setCount(npmPackage.total); //update total count when npmPackage is updated + if (selectedPackage) { + setCount( + selectedPackage.type === "npm" + ? selectedPackage.total || 0 + : selectedPackage.last_month || 0 + ); //update total count when npmPackage is updated } - }, [npmPackage]); + }, [selectedPackage]); function openModal() { setIsOpen(true); - setCount(npmPackage.total); + setCount( + selectedPackage.type === "npm" + ? selectedPackage.total || 0 + : selectedPackage.last_month || 0 + ); } - // Function to fetch download statistics for a given package and period - async function fetchDownloadStats(packageName: string, period: string) { + async function fetchNpmStats( + packageName: string, + period: string + ): Promise { setLoading(true); const url = `https://api.npmjs.org/downloads/range/${period}/@mindfiredigital/${packageName}`; const response = await fetch(url); @@ -64,6 +83,7 @@ const Stats = () => { `Failed to fetch download stats for ${packageName} (${period}): ${response.statusText}` ); setLoading(false); + throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); @@ -71,67 +91,37 @@ const Stats = () => { return data; } - function calculateDownloads(stats: stats): number { + function calculateDownloads(stats: NpmStats): number { if (!stats || !stats.downloads) { - return 0; // Return 0 if stats or stats.downloads is undefined + return 0; } return stats.downloads.reduce( - (accumulator, download) => accumulator + download.downloads, + (acc, download) => acc + download.downloads, 0 ); } - // Function to fetch and process statistics for a package and period - async function getStats(packageName: string, period: string) { - try { - // Fetch download statistics - const stats = await fetchDownloadStats(packageName, period); + function handleChange(event: React.ChangeEvent) { + if (!selectedPackage) return; - // Check if stats exist - if (!stats || !stats.package) return 0; + const range = getDateRange(event.target.value); - // Calculate average downloads - return stats; - } catch (error) { - // Log and handle errors - console.error(`${packageName} not present`); - return 0; - } - } - function handleChange( - event: React.ChangeEvent, - _package: { - name: string; - day: number; - week: number; - year: number; - total: number; - } - ) { - const range: { start: string; end: string } = getDateRange( - event.target.value as string - ); - - getStats(_package.name, `${range?.start}:${range?.end}`).then((res) => { - setLoading(true); - const count = calculateDownloads(res); - // console.log(count); - - packages.map((npmPackage) => { - if (npmPackage.name === _package.name) { + if (selectedPackage.type === "npm") { + fetchNpmStats(selectedPackage.name, `${range.start}:${range.end}`).then( + (res) => { + setLoading(true); + const count = calculateDownloads(res); setCount(count); setLoading(false); } - return npmPackage; - }); - }); + ); + } else if (selectedPackage.type === "pypi") { + setCount(Number(event.target.value) || 0); + } } function formatDate(date: Date) { - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, "0"); - const day = String(date.getDate()).padStart(2, "0"); - return `${year}-${month}-${day}`; + return date.toISOString().split("T")[0]; } function getDateRange(range: string) { @@ -141,13 +131,12 @@ const Stats = () => { const currentDay = currentDate.getDate(); switch (range.toLowerCase()) { - case "today": { + case "today": setSelectedRange(false); return { start: formatDate(new Date(currentYear, currentMonth, currentDay)), end: formatDate(new Date(currentYear, currentMonth, currentDay)), }; - } case "yesterday": { setSelectedRange(false); const yesterdayDate = new Date( @@ -169,9 +158,17 @@ const Stats = () => { end: formatDate(lastMonthEndDate), }; } + case "this month": { + setSelectedRange(false); + const thisMonthStartDate = new Date(currentYear, currentMonth, 1); + return { + start: formatDate(thisMonthStartDate), + end: formatDate(currentDate), + }; + } case "last quarter": { setSelectedRange(false); - const quarterStartMonth = Math.floor(currentMonth / 3) * 3; // Get the start month of the current quarter + const quarterStartMonth = Math.floor(currentMonth / 3) * 3; const lastQuarterStartDate = new Date( currentYear, quarterStartMonth - 3, @@ -191,14 +188,6 @@ const Stats = () => { end: formatDate(currentDate), }; } - case "this month": { - setSelectedRange(false); - const thisMonthStartDate = new Date(currentYear, currentMonth, 1); - return { - start: formatDate(thisMonthStartDate), - end: formatDate(currentDate), - }; - } case "custom": { setSelectedRange(true); setCount(0); @@ -207,26 +196,30 @@ const Stats = () => { end: formatDate(new Date(currentYear, currentMonth, currentDay)), }; } - default: + default: { setSelectedRange(false); return { start: "1000-01-01", end: "3000-01-01", }; + } } } - const generateChart = async () => { - const stats = await fetchDownloadStats( - npmPackage.name, - `${startDate}:${endDate}` - ); - setCount(calculateDownloads(stats)); + if (selectedPackage.type === "npm") { + const stats = await fetchNpmStats( + selectedPackage.name, + `${startDate}:${endDate}` + ); + setCount(calculateDownloads(stats)); + } }; useEffect(() => { - generateChart(); - }, [startDate, endDate]); + if (selectedRange && selectedPackage.type === "npm") { + generateChart(); + } + }, [startDate, endDate, selectedRange, selectedPackage]); return (
@@ -238,16 +231,16 @@ const Stats = () => { Elevate your projects with Mindfire's game-changing open-source packages.

-
- {packages.map((package_list) => ( +
+ {packages.map((package_item) => (

- {package_list.name.replaceAll("-", " ")} + {package_item.name.replaceAll("-", " ")}

@@ -255,7 +248,7 @@ const Stats = () => {
@@ -298,15 +293,19 @@ const Stats = () => {
npm_img @@ -314,7 +313,7 @@ const Stats = () => {
@@ -322,7 +321,7 @@ const Stats = () => { src={github} height={30} width={30} - alt='npm_img' + alt='github_img' loading='lazy' quality={75} /> @@ -385,31 +384,45 @@ const Stats = () => { as='h1' className='text-lg font-large leading-6 text-gray-900 capitalize text-center mb-4 font-extrabold' > - {npmPackage.name.replaceAll("-", " ")} + {selectedPackage.name.replaceAll("-", " ")} -
+

Select

- + {selectedPackage.type === "npm" ? ( + + ) : ( + + )}
{
- {selectedRange === true ? ( -
+ {selectedRange && selectedPackage.type === "npm" ? ( +
-
-
-
-
- expand_img -
- {loading ? ( - // Render loading indicator while count is being fetched -
- - {" "} - -
- ) : ( - // Render count when it is available -
- {count} -
- )} +
+ ) : null} +
+
+
+
+ expand_img +
+ {loading ? ( +
+ + {" "} +
-
-
-

- Downloads -

-
+ ) : ( +
+ {count} +
+ )}
-
-
- ) : ( -
-
-
-
- expand_img -
- {loading ? ( - // Render loading indicator while count is being fetched -
- - {" "} - -
- ) : ( - // Render count when it is available -
- {count} -
- )} -
-
-
-

- Downloads -

-
+
+

+ Downloads +

- )} +
diff --git a/src/app/projects/assets/stats.json b/src/app/projects/assets/stats.json index e03009ca..36f756e8 100644 --- a/src/app/projects/assets/stats.json +++ b/src/app/projects/assets/stats.json @@ -1,6 +1,7 @@ [ { "name": "fmdapi-node-weaver", + "type": "npm", "day": 0, "week": 3, "year": 70, @@ -8,6 +9,7 @@ }, { "name": "react-canvas-editor", + "type": "npm", "day": 1, "week": 22, "year": 947, @@ -15,9 +17,18 @@ }, { "name": "canvas-editor", + "type": "npm", "day": 0, "week": 7, "year": 1063, "total": 1063 + }, + { + "name": "neo-pusher", + "type": "pypi", + "last_day": 46, + "last_week": 935, + "last_month": 1011, + "total": 1011 } ] diff --git a/updateProject.mjs b/updateProject.mjs index 40569664..496d79dd 100644 --- a/updateProject.mjs +++ b/updateProject.mjs @@ -188,8 +188,13 @@ async function updateProjects() { JSON.stringify(sortedContributions, null, 2) ); console.log("Contributors list updated successfully."); - - getAllStats(repoNames) + const npmPackages = [ + "fmdapi-node-weaver", + "react-canvas-editor", + "canvas-editor", + ]; + const pypiPackages = ["neo-pusher"]; + getAllStats(npmPackages, pypiPackages) .then((statsMap) => { fs.writeFileSync( path.join(__dirname, "src/app/projects/assets/stats.json"), @@ -221,6 +226,22 @@ async function fetchDownloadStats(packageName, period) { return data; } +// Function to fetch PyPI download statistics for a given package +async function fetchPyPIDownloadStats(packageName) { + const url = `https://pypistats.org/api/packages/${packageName}/recent`; + const response = await fetch(url); + + if (!response.ok) { + console.log( + `Failed to fetch download stats for ${packageName} (PyPI): ${response.statusText}` + ); + return null; + } + + const data = await response.json(); + return data.data; // { last_day, last_week, last_month } +} + // Function to calculate average downloads from the statistics function calculateAverageDownloads(stats) { return stats.downloads.reduce( @@ -229,8 +250,8 @@ function calculateAverageDownloads(stats) { ); } -// Function to fetch and process statistics for a package and period -async function getStats(packageName, period) { +// Function to fetch and process statistics for a package and period (npm) +async function getNpmStats(packageName, period) { try { // Fetch download statistics const stats = await fetchDownloadStats(packageName, period); @@ -247,26 +268,25 @@ async function getStats(packageName, period) { } } -// Function to fetch and aggregate statistics for all packages and periods -async function getAllStats(npmPackages) { +// Function to fetch and aggregate statistics for all npm and PyPI packages +async function getAllStats(npmPackages, pypiPackages) { const statsMap = []; - // Fetch stats for each package and period + // Fetch stats for npm packages await Promise.all( npmPackages.map(async (packageName) => { try { - // Fetch stats for different periods const [dayStats, weekStats, yearStats, totalStats] = await Promise.all([ - getStats(packageName, "last-day"), - getStats(packageName, "last-week"), - getStats(packageName, "last-year"), - getStats(packageName, "1000-01-01:3000-01-01"), + getNpmStats(packageName, "last-day"), + getNpmStats(packageName, "last-week"), + getNpmStats(packageName, "last-year"), + getNpmStats(packageName, "1000-01-01:3000-01-01"), ]); - // If any stats exist, add to the map if (dayStats !== 0 || weekStats !== 0 || yearStats !== 0) { statsMap.push({ name: packageName, + type: "npm", day: dayStats, week: weekStats, year: yearStats, @@ -274,12 +294,33 @@ async function getAllStats(npmPackages) { }); } } catch (error) { - // Log and handle errors console.error(`Error fetching stats for ${packageName}:`, error); } }) ); + // Fetch stats for PyPI packages + await Promise.all( + pypiPackages.map(async (packageName) => { + try { + const stats = await fetchPyPIDownloadStats(packageName); + + if (stats) { + statsMap.push({ + name: packageName, + type: "pypi", + last_day: stats.last_day, + last_week: stats.last_week, + last_month: stats.last_month, + total: stats.last_month, + }); + } + } catch (error) { + console.error(`Error fetching stats for ${packageName} (PyPI):`, error); + } + }) + ); + return statsMap; }