117 lines
4.1 KiB
TypeScript
117 lines
4.1 KiB
TypeScript
// app/components/publication-stats.tsx
|
|
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import * as cheerio from "cheerio";
|
|
import { PublicationChartClient } from "./publication-chart"; // <-- IMPORT THE NEW CLIENT COMPONENT
|
|
|
|
// Define the structure for our scraped data (can be shared or defined here)
|
|
interface ScholarStats {
|
|
citations: { all: number };
|
|
hIndex: { all: number };
|
|
i10Index: { all: number };
|
|
citationsPerYear: { year: number; count: number }[];
|
|
}
|
|
|
|
// Function to fetch and parse data from Google Scholar (no changes needed here)
|
|
async function getScholarStats(): Promise<ScholarStats> {
|
|
const url = "https://scholar.google.de/citations?user=NODAd94AAAAJ&hl=en";
|
|
try {
|
|
const response = await fetch(url, {
|
|
next: { revalidate: 86400 }, // Revalidate once a day
|
|
});
|
|
|
|
if (!response.ok) throw new Error("Failed to fetch Google Scholar page");
|
|
|
|
const html = await response.text();
|
|
const $ = cheerio.load(html);
|
|
|
|
const citations = parseInt($("#gsc_rsb_st > tbody > tr:nth-child(1) > td:nth-child(2)").text() || "0");
|
|
const hIndex = parseInt($("#gsc_rsb_st > tbody > tr:nth-child(2) > td:nth-child(2)").text() || "0");
|
|
const i10Index = parseInt($("#gsc_rsb_st > tbody > tr:nth-child(3) > td:nth-child(2)").text() || "0");
|
|
|
|
const citationsPerYear: { year: number; count: number }[] = [];
|
|
const yearElements = $(".gsc_g_t");
|
|
const citationElements = $(".gsc_g_a");
|
|
|
|
yearElements.each((index, element) => {
|
|
const year = parseInt($(element).text());
|
|
const count = parseInt($(citationElements[index]).find('.gsc_g_al').text() || "0");
|
|
if (!isNaN(year) && !isNaN(count)) {
|
|
citationsPerYear.push({ year, count });
|
|
}
|
|
});
|
|
|
|
return {
|
|
citations: { all: citations },
|
|
hIndex: { all: hIndex },
|
|
i10Index: { all: i10Index },
|
|
citationsPerYear: citationsPerYear,
|
|
};
|
|
} catch (error) {
|
|
console.error("Error scraping Google Scholar:", error);
|
|
return { citations: { all: 0 }, hIndex: { all: 0 }, i10Index: { all: 0 }, citationsPerYear: [] };
|
|
}
|
|
}
|
|
|
|
|
|
// The main component that renders the statistics
|
|
export async function PublicationStats() {
|
|
const stats = await getScholarStats();
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
const lastFiveYearsData = stats.citationsPerYear
|
|
.filter((entry) => entry.year >= currentYear - 4)
|
|
.sort((a, b) => a.year - b.year);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<h2 className="font-bold text-2xl">Publication Statistics</h2>
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
<div className="flex flex-col gap-4">
|
|
<Card>
|
|
<CardContent className="px-4 py-0 flex flex-col gap-1">
|
|
<p className="text-sm font-medium text-muted-foreground">
|
|
Citations
|
|
</p>
|
|
<p className="text-2xl font-bold">{stats.citations.all}</p>
|
|
</CardContent>
|
|
</Card>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<Card>
|
|
<CardContent className="px-4 py-0 flex flex-col gap-1">
|
|
<p className="text-sm font-medium text-muted-foreground">
|
|
h-index
|
|
</p>
|
|
<p className="text-2xl font-bold">{stats.hIndex.all}</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="px-4 py-0 flex flex-col gap-1">
|
|
<p className="text-sm font-medium text-muted-foreground">
|
|
i10-index
|
|
</p>
|
|
<p className="text-2xl font-bold">{stats.i10Index.all}</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-sm font-medium">
|
|
Citations per Year
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="h-32">
|
|
<PublicationChartClient data={lastFiveYearsData} />
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |