Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions api-go/tools/puppeteer/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const moment = require('moment');
const axios = require('axios');

const isSameDay = (d1, d2) => {
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
};

const fetchContributorsData = (repo) => {
if (repo === "null" || repo === null) {
repo = "apache/apisix";
}
return new Promise((resolve, reject) => {
axios.get(
`https://contributor-overtime-api.apiseven.com/contributors?repo=${repo}`
).then(response => {
return response.data;
}).then(data => {
const { Contributors = [] } = data;
const sortContributors = Contributors.map(item => ({
...item,
date: item.date.substring(0, 10)
})).sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
);
if (
!isSameDay(
new Date(sortContributors[sortContributors.length - 1].date),
new Date()
)
) {
sortContributors.push({
repo,
idx: sortContributors[sortContributors.length - 1].idx,
date: moment(new Date()).format("YYYY-MM-DD")
});
};

const processContributors = [];
sortContributors.forEach((item, index) => {
processContributors.push(item);

if (index !== sortContributors.length - 1) {
const diffDays = moment(sortContributors[index + 1].date).diff(
item.date,
"days"
);
if (diffDays > 1) {
for (let index = 1; index < diffDays; index++) {
processContributors.push({
...item,
date: moment(item.date)
.add(index, "days")
.format()
.substring(0, 10)
});
}
}
}
});

const filterData = processContributors.filter(
(item, index) =>
index === 0 ||
index === processContributors.length - 1 ||
new Date(item.date).getDate() % 10 === 5
);

resolve({ repo, ...{ Contributors: filterData } });
}).catch(error => {
reject(error);
})
})
};

const fetchMonthlyData = (repo) => {
if (repo === "null" || repo === null) {
repo = "apache/apisix";
}
return new Promise((resolve, reject) => {
axios.get(
`https://contributor-overtime-api.apiseven.com/monthly-contributor?repo=${repo}`
)
.then(response => {
return response.data;
})
.then(myJson => {
resolve({ repo, ...myJson });
})
.catch(e => {
reject(e);
});
});
};

const fetchMergeContributor = (repo) => {
return new Promise((resolve, reject) => {
axios.get(
`https://contributor-overtime-api.apiseven.com/contributors-multi?repo=${repo}`
)
.then(response => {
return response.data;
})
.then(myJson => {
console.log('myJson: ', myJson);
resolve({ repo, ...myJson });
})
.catch(e => {
reject(e);
});
});
};

module.exports = {
fetchContributorsData,
fetchMonthlyData,
fetchMergeContributor,
}
128 changes: 88 additions & 40 deletions api-go/tools/puppeteer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,99 @@
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/
const puppeteer = require('puppeteer');
const querystring = require('querystring');
const echarts = require("echarts");
const { createCanvas } = require('canvas');
const jsdom = require("jsdom");
const { fetchContributorsData, fetchMonthlyData, fetchMergeContributor } = require('./fetch');
const { updateSeries } = require('./utils');
const { JSDOM } = jsdom;

const config = {
width: 896,
height: 550,
};

const mergeRepoList = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we could maintain a json or yaml file to store this configuration?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both config and mergeRepoList ? @juzhiyuan

I found js-yaml, after trying it feels good, we can use the YAML file.

"apache/apisix",
"apache/skywalking",
"apache/openwhisk",
"apache/dubbo"
];

exports.svg = async (req, res) => {
const repo = req.query.repo;
const merge = req.query.merge;
const chart = req.query.chart;
const chartType = req.query.chart;

const window = (new JSDOM(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div style="width:${config.width}px;height:${config.height}px" id='echarts'></div>
</body>
</html>
`)).window;

const { document } = window;
global.document = document;

const browser = await puppeteer.launch({
args: ['--no-sandbox',]
const ctx = createCanvas(config.width, config.height);
echarts.setCanvasCreator(function () {
return ctx;
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });

graphUrl = "https://contributor-graph-git-no-animation-apiseven.vercel.app/?repo=" + repo;
if (merge) {
graphUrl += "&merge=true"
}
if (chart) {
graphUrl += "&chart=" + chart
}

await page.goto(graphUrl);

var getSVG = function () {
return window.echartInstance.getDataURL();
};

var svgReady = function () {
return window.echartsRenderFinished;
}

function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}

sleep(1000).then(() => {
var _flagCheck = setInterval(async function () {
if (await page.evaluate(svgReady) === true) {
clearInterval(_flagCheck);
var image = await page.evaluate(getSVG);
res.set('Content-Type', 'image/svg+xml');
res.send(querystring.unescape(image).split("data:image/svg+xml;charset=UTF-8,")[1]);
await browser.close();
let repoList = repo.split(",");

Promise.all(repoList.map(item => {
if (chartType === "contributorMonthlyActivity") {
console.log(`render contributorMonthlyActivity for ${repo}`);
return fetchMonthlyData(item);
};
if (merge === "true" && repoList.length === 1 && mergeRepoList.includes(repo)) {
console.log(`render merge contributor for ${repo}`);
return fetchMergeContributor(repo);
}
console.log(`render contributorData for ${repo}`);
return fetchContributorsData(item);
})).then(data => {
const tmpDataSouce = {};
data.forEach(item => {
const { Contributors = [], repo } = item;
const data = Contributors.map(item => {
if (chartType === "contributorMonthlyActivity") {
return {
repo,
contributorNum: item.Num,
date: item.Month
}
} else {
return {
repo,
contributorNum: item.idx,
date: item.date
}
}
});

if (!tmpDataSouce[item.repo]) {
tmpDataSouce[repo] = data;
}
}, 100);
});
});

const title = chartType === "contributorMonthlyActivity" ? "Monthly Active Contributors" : "Contributor Over Time";
const option = updateSeries(["1970-01-01"], tmpDataSouce, title);
let chart = echarts.init(document.getElementById('echarts'), {}, {renderer: "svg"});
chart.setOption(option, true);
const image = chart.getSvgDataURL();

res.set('Content-Type', 'image/svg+xml');
res.send(decodeURIComponent(image).split("data:image/svg+xml;charset=UTF-8,")[1]);
}).catch(error => {
console.log(`generate file of ${repo} error`, error);
})
};
7 changes: 6 additions & 1 deletion api-go/tools/puppeteer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"puppeteer": "8.0.0"
"axios": "^0.21.1",
"canvas": "^2.8.0",
"echarts": "5.1.2",
"lodash.clonedeep": "4.5.0",
"moment": "^2.29.1",
"jsdom": "^17.0.0"
}
}
Loading