diff --git a/package-lock.json b/package-lock.json index bac8be1..378a250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "docusaurus-plugin-image-zoom": "^2.0.0", "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", + "jquery": "^3.7.1", "katex": "^0.16.11", "prism-react-renderer": "^2.3.1", "react": "^18.3.1", @@ -8732,6 +8733,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index d117f73..6c06686 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "docusaurus-plugin-image-zoom": "^2.0.0", "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", + "jquery": "^3.7.1", "katex": "^0.16.11", "prism-react-renderer": "^2.3.1", "react": "^18.3.1", diff --git a/src/components/DevLeaderboard/Details.js b/src/components/DevLeaderboard/Details.js new file mode 100644 index 0000000..465f078 --- /dev/null +++ b/src/components/DevLeaderboard/Details.js @@ -0,0 +1,101 @@ +import React, { useEffect, useState } from "react"; +import "./devLeaderboard.css"; + +const Details = ({ graph, id, month, typeMap, platform }) => { + const [details, setDetails] = useState([]); + + useEffect(() => { + if (graph && id) { + const data = graph.data[month]; + const selfNode = data.nodes.find( + (node) => graph.meta.nodes[node[0]][0] === id + ); + const detailsData = []; + + if (selfNode) { + detailsData.push([ + "Self", + graph.meta.retentionFactor, + selfNode[1], + (graph.meta.retentionFactor * selfNode[1]).toFixed(3), + ]); + + const otherDetails = data.links + .filter((link) => graph.meta.nodes[link[1]][0] === id) + .map((link) => { + const sourceIndex = link[0]; + const sourceNode = graph.meta.nodes[sourceIndex]; + const sourceValue = data.nodes.find( + (node) => node[0] === sourceIndex + ); + const type = typeMap.get(sourceNode[0][0]); + let name = sourceNode[1]; + if (type === "pull") + name = "#" + sourceNode[0].slice(1) + " " + sourceNode[1]; + else if (type === "issue") + name = + "#" + + (platform === "gitee" + ? parseInt(sourceNode[0].slice(1)).toString(36).toUpperCase() + : sourceNode[0].slice(1)) + + " " + + sourceNode[1]; + return [ + name, + parseFloat((1 - graph.meta.retentionFactor) * link[2]).toFixed(3), + sourceValue[2], + parseFloat( + ( + (1 - graph.meta.retentionFactor) * + link[2] * + sourceValue[2] + ).toFixed(3) + ), + ]; + }) + .sort((a, b) => b[3] - a[3]); + + const repoNode = data.nodes.find((node) => node[0] === 0); + otherDetails.push([ + graph.meta.repoName, + (1 / (data.nodes.length - 1)).toFixed(3), + repoNode[2], + ((1 / (data.nodes.length - 1)) * repoNode[2]).toFixed(3), + ]); + + setDetails([...detailsData, ...otherDetails]); + } + } + }, [graph, id, month, typeMap, platform]); + + return ( +
+
+

Details

+
+
+ + + + + + + + + + + {details.map((detail, index) => ( + + {detail.map((d, i) => ( + + ))} + + ))} + +
FromRatioValueOpenRank
{d}
+
+
+ ); +}; + +export default Details; \ No newline at end of file diff --git a/src/components/DevLeaderboard/Graph.js b/src/components/DevLeaderboard/Graph.js new file mode 100644 index 0000000..0e56156 --- /dev/null +++ b/src/components/DevLeaderboard/Graph.js @@ -0,0 +1,103 @@ +import React, { useEffect, useState } from "react"; +import * as echarts from "echarts"; +import "./devLeaderboard.css"; + +const Graph = ({ + graph, + month, + repoName, + platform, + typeMap, + onNodeDblClick, +}) => { + useEffect(() => { + if (graph) { + const container = document.getElementById("graph"); + const chart = echarts.init(container); + + const nodes = graph.data[month].nodes.map((node) => { + const id = graph.meta.nodes[node[0]][0]; + const type = typeMap.get(id[0]); + let name = graph.meta.nodes[node[0]][1]; + if (type === "pull") name = "#" + id.slice(1); + else if (type === "issue") + name = + "#" + + (platform === "gitee" + ? parseInt(id.slice(1)).toString(36).toUpperCase() + : id.slice(1)); + return { + id, + initialValue: node[1], + value: node[2], + name, + symbolSize: Math.log(node[2] + 1) * 10, + category: type, + }; + }); + + const links = graph.data[month].links.map((link) => ({ + source: graph.meta.nodes[link[0]][0], + target: graph.meta.nodes[link[1]][0], + value: link[2], + })); + + nodes.forEach((node) => { + if (node.category === "issue" || node.category === "pull") { + links.push({ + source: graph.meta.nodes[0][0], + target: node.id, + value: 0.05, + }); + } + }); + + const categories = Array.from(typeMap.values()); + + const option = { + title: { + text: `Community OpenRank for ${repoName} in ${month}`, + top: "bottom", + left: "right", + }, + legend: [{ data: categories }], + tooltip: { trigger: "item" }, + series: [ + { + name: "Collaborative graph", + type: "graph", + layout: "force", + nodes, + links, + categories: categories.map((c) => ({ name: c })), + roam: true, + label: { + position: "right", + show: true, + }, + force: { + layoutAnimation: false, + repulsion: 300, + }, + }, + ], + }; + + chart.setOption(option); + chart.on("dblclick", (params) => onNodeDblClick(params.data.id)); + + return () => { + chart.dispose(); + }; + } + }, [graph, month, repoName, platform, typeMap, onNodeDblClick]); + + return ( +
+ ); +}; + +export default Graph; diff --git a/src/components/DevLeaderboard/Leaderboard.js b/src/components/DevLeaderboard/Leaderboard.js new file mode 100644 index 0000000..820c29b --- /dev/null +++ b/src/components/DevLeaderboard/Leaderboard.js @@ -0,0 +1,51 @@ +import React, { useEffect, useState } from "react"; +import * as echarts from "echarts"; +import $ from "jquery"; +import "./devLeaderboard.css"; + +const Leaderboard = ({ graph, month }) => { + const [users, setUsers] = useState([]); + + useEffect(() => { + if (graph) { + const sortedUsers = graph.data[month].nodes + .map((node) => ({ + id: graph.meta.nodes[node[0]][0], + login: graph.meta.nodes[node[0]][1], + value: node[2], + })) + .filter((user) => user.id[0] === "u") + .sort((a, b) => b.value - a.value); + + setUsers(sortedUsers); + } + }, [graph, month]); + + return ( +
+
+

Leaderboard

+
+
+ + + + + + + + + {users.map((user, index) => ( + + + + + ))} + +
LoginOpenRank
{user.login}{user.value}
+
+
+ ); +}; + +export default Leaderboard; diff --git a/src/components/DevLeaderboard/devLeaderboard.css b/src/components/DevLeaderboard/devLeaderboard.css new file mode 100644 index 0000000..add22ba --- /dev/null +++ b/src/components/DevLeaderboard/devLeaderboard.css @@ -0,0 +1,68 @@ +#main { + display: flex; +} + +#graph { + height: 800px; +} + +.graph-container { + width: 800px; +} + +#control { + width: 320px; + height: 800px; +} + +#list { + width: 300px; + height: 300px; + margin: 10px; +} + +#details { + width: 300px; + height: 440px; + margin: 10px; +} + +#title { + text-align: center; + font-size: 12px; +} + +#leaderboard_table { + width: 95%; + margin: 10px; +} + +#details_table { + width: 95%; + margin: 10px; +} + +.bordered { + border: 2px solid grey; +} + +#leaderboard_div { + height: 240px; +} + +#details_div { + height: 380px; +} + +.scrollit { + overflow-x: hidden; + overflow-y: auto; +} + +tr:nth-child(even) { + background-color: #D6EEEE; +} + +table, th, td { + border: 1px solid black; +} diff --git a/src/components/DevLeaderboard/index.js b/src/components/DevLeaderboard/index.js new file mode 100644 index 0000000..f918330 --- /dev/null +++ b/src/components/DevLeaderboard/index.js @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from "react"; +import * as echarts from "echarts"; +import $ from "jquery"; + +import Leaderboard from "./Leaderboard"; +import Details from "./Details"; +import Graph from "./Graph"; +import "./devLeaderboard.css"; + +const DevLeaderboard = () => { + const [graph, setGraph] = useState(null); + const [selectedNodeId, setSelectedNodeId] = useState(null); + + const baseUrl = "https://oss.x-lab.info/open_digger/"; + const platform = "github"; // Replace with dynamic fetching if needed + const repoName = "hypertrons/hypertrons-crx"; // Replace with dynamic fetching if needed + const month = "202407"; // Replace with dynamic fetching if needed + const typeMap = new Map([ + ["r", "repo"], + ["i", "issue"], + ["p", "pull"], + ["u", "user"], + ]); + + useEffect(() => { + const loadData = async () => { + const data = await $.getJSON( + `${baseUrl}${platform}/${repoName}/community_openrank.json` + ); + setGraph(data); + }; + + loadData(); + }, [platform, repoName]); + + const handleNodeDblClick = (id) => { + setSelectedNodeId(id); + }; + + return ( +
+ +
+ +
+
+
+ ); +}; + +export default DevLeaderboard; diff --git a/src/pages/leaderboard.mdx b/src/pages/leaderboard.mdx index ccbcf22..f19f431 100644 --- a/src/pages/leaderboard.mdx +++ b/src/pages/leaderboard.mdx @@ -1,5 +1,4 @@ -这是 leaderboard 页面 - import MetricChartComponent from '@site/src/components/OpenDiggerMetricCharts'; +import DevLeaderboard from '@site/src/components/DevLeaderboard'; - \ No newline at end of file + \ No newline at end of file