diff --git a/public/images/social-media/pypi-svg.svg b/public/images/social-media/pypi-svg.svg new file mode 100644 index 00000000..884887dc --- /dev/null +++ b/public/images/social-media/pypi-svg.svg @@ -0,0 +1,78 @@ + + + Pypi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/packages/page.tsx b/src/app/packages/page.tsx index 8a38f3f1..55f4a710 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,32 @@ 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({ - name: "fmdapi-node-weaver", - day: 0, - week: 3, - year: 70, - total: 70, - }); + const [selectedPackage, setSelectedPackage] = useState(null); // 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 +45,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 +76,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 +84,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 +124,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 +151,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 +181,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 +189,31 @@ 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 +225,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 +242,7 @@ const Stats = () => {
@@ -298,15 +287,19 @@ const Stats = () => {
npm_img @@ -314,7 +307,7 @@ const Stats = () => {
@@ -322,7 +315,7 @@ const Stats = () => { src={github} height={30} width={30} - alt='npm_img' + alt='github_img' loading='lazy' quality={75} /> @@ -385,31 +378,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..d02b9782 100644 --- a/src/app/projects/assets/stats.json +++ b/src/app/projects/assets/stats.json @@ -19,5 +19,12 @@ "week": 7, "year": 1063, "total": 1063 + }, + { + "name": "neo-pusher", + "type": "pypi", + "last_day": 1, + "last_week": 70, + "last_month": 1000 } ] diff --git a/updateProject.mjs b/updateProject.mjs index 40569664..78695a72 100644 --- a/updateProject.mjs +++ b/updateProject.mjs @@ -221,6 +221,39 @@ async function fetchDownloadStats(packageName, period) { return data; } +// Function to fetch download statistics for a PyPI 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}: ${response.statusText}` + ); + return null; + } + + const data = await response.json(); + return data.data; +} + +// Function to fetch and process statistics for a PyPI package +async function getPypiStats(packageName) { + try { + const stats = await fetchPypiDownloadStats(packageName); + if (!stats) return null; + + return { + last_day: stats.last_day, + last_week: stats.last_week, + last_month: stats.last_month, + }; + } catch (error) { + console.error(`Error fetching PyPI stats for ${packageName}:`, error); + return null; + } +} + // Function to calculate average downloads from the statistics function calculateAverageDownloads(stats) { return stats.downloads.reduce( @@ -253,29 +286,40 @@ async function getAllStats(npmPackages) { // Fetch stats for each package and period await Promise.all( - npmPackages.map(async (packageName) => { + npmPackages.map(async (packageData) => { 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"), - ]); - - // If any stats exist, add to the map - if (dayStats !== 0 || weekStats !== 0 || yearStats !== 0) { - statsMap.push({ - name: packageName, + let stats; + if (packageData.type === "npm") { + const [dayStats, weekStats, yearStats, totalStats] = + await Promise.all([ + getStats(packageData.name, "last-day"), + getStats(packageData.name, "last-week"), + getStats(packageData.name, "last-year"), + getStats(packageData.name, "1000-01-01:3000-01-01"), + ]); + + stats = { + name: packageData.name, + type: "npm", day: dayStats, week: weekStats, year: yearStats, total: totalStats, - }); + }; + } else if (packageData.type === "pypi") { + const pypiStats = await getPypiStats(packageData.name); + stats = { + name: packageData.name, + type: "pypi", + ...pypiStats, + }; + } + + if (stats) { + statsMap.push(stats); } } catch (error) { - // Log and handle errors - console.error(`Error fetching stats for ${packageName}:`, error); + console.error(`Error fetching stats for ${packageData.name}:`, error); } }) );