Browse Source

added temporary changes like repository heat map

repositoryPage
jrtechs 4 years ago
parent
commit
463ae3a2fe
4 changed files with 287 additions and 18 deletions
  1. +242
    -0
      public/calendar.html
  2. +14
    -0
      public/downloadEvents.html
  3. +8
    -0
      public/js/githubAPI.js
  4. +23
    -18
      routes/api/v2.js

+ 242
- 0
public/calendar.html View File

@ -0,0 +1,242 @@
<html>
<head>
<title>Stack Overflow - Calendar Heatmap</title>
<style>
.body {
height: 97%;
}
svg {
height: 1800;
width: 97%;
}
</style>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
<script src="js/githubAPI.js"></script>
<script src="js/utilities.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.2/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-color/1.2.1/d3-color.js"></script>
</head>
<body>
<h3>Stack Overflow - Calendar Heatmap</h3>
<h4>Daily Commits to RITlug/teleirc</h4>
<svg id="svg"></svg>
<script>
function countCommitsPerDay(commits)
{
var reduce = [];
console.log(commits);
var current_date = new Date(commits[0].date);
current_date.setHours(0,0,0,0);
var current_count = 0;
for(var i = 1; i < commits.length; i++)
{
var d = new Date(commits[i].date);
d.setHours(0,0,0,0);
console.log(d);
if(current_date.getTime() == d.getTime())
{
console.log("Stonks");
current_count++;
}
else
{
reduce.push({Date:current_date.toISOString(), AnswerCount: current_count});
current_count = 1;
current_date = d;
}
reduce.push({Date:current_date.toISOString(), AnswerCount: current_count});
}
console.log(reduce);
return reduce;
}
function createHeatmapGraph(commits, containerID)
{
var sample = countCommitsPerDay(commits);
sample.sort((a, b) => new Date(a.Date) - new Date(b.Date));
const dateValues = sample.map(dv => ({
date: d3.timeDay(new Date(dv.Date)),
value: Number(dv.AnswerCount)
}));
const svg = d3.select("#" + containerID);
const { width, height } = document
.getElementById(containerID)
.getBoundingClientRect();
function draw() {
const years = d3
.nest()
.key(d => d.date.getUTCFullYear())
.entries(dateValues)
.reverse();
const values = dateValues.map(c => c.value);
const maxValue = d3.max(values);
const minValue = d3.min(values);
const cellSize = 15;
const yearHeight = cellSize * 7;
const group = svg.append("g");
const year = group
.selectAll("g")
.data(years)
.join("g")
.attr(
"transform",
(d, i) => `translate(50, ${yearHeight * i + cellSize * 1.5})`
);
year
.append("text")
.attr("x", -5)
.attr("y", -30)
.attr("text-anchor", "end")
.attr("font-size", 16)
.attr("font-weight", 550)
.attr("transform", "rotate(270)")
.text(d => d.key);
const formatDay = d =>
["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"][d.getUTCDay()];
const countDay = d => d.getUTCDay();
const timeWeek = d3.utcSunday;
const formatDate = d3.utcFormat("%x");
const colorFn = d3
.scaleSequential(d3.interpolateBuGn)
.domain([Math.floor(minValue) -1, Math.ceil(maxValue)]);
const format = d3.format("+.2%");
year
.append("g")
.attr("text-anchor", "end")
.selectAll("text")
.data(d3.range(7).map(i => new Date(1995, 0, i)))
.join("text")
.attr("x", -5)
.attr("y", d => (countDay(d) + 0.5) * cellSize)
.attr("dy", "0.31em")
.attr("font-size", 12)
.text(formatDay);
year
.append("g")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr("width", cellSize - 1.5)
.attr("height", cellSize - 1.5)
.attr(
"x",
(d, i) => timeWeek.count(d3.utcYear(d.date), d.date) * cellSize + 10
)
.attr("y", d => countDay(d.date) * cellSize + 0.5)
.attr("fill", d => colorFn(d.value))
.append("title")
.text(d => `${formatDate(d.date)}: ${d.value.toFixed(2)}`);
const legend = group
.append("g")
.attr(
"transform",
`translate(10, ${years.length * yearHeight + cellSize * 4})`
);
const categoriesCount = 10;
const categories = [...Array(categoriesCount)].map((_, i) => {
const upperBound = (maxValue / categoriesCount) * (i + 1);
const lowerBound = (maxValue / categoriesCount) * i;
return {
upperBound,
lowerBound,
color: d3.interpolateBuGn(upperBound / maxValue),
selected: true
};
});
const legendWidth = 60;
function toggle(legend) {
const { lowerBound, upperBound, selected } = legend;
legend.selected = !selected;
const highlightedDates = years.map(y => ({
key: y.key,
values: y.values.filter(
v => v.value > lowerBound && v.value <= upperBound
)
}));
year
.data(highlightedDates)
.selectAll("rect")
.data(d => d.values, d => d.date)
.transition()
.duration(500)
.attr("fill", d => (legend.selected ? colorFn(d.value) : "white"));
}
legend
.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("fill", d => d.color)
.attr("x", (d, i) => legendWidth * i)
.attr("width", legendWidth)
.attr("height", 15)
.on("click", toggle);
legend
.selectAll("text")
.data(categories)
.join("text")
.attr("transform", "rotate(90)")
.attr("y", (d, i) => -legendWidth * i)
.attr("dy", -30)
.attr("x", 18)
.attr("text-anchor", "start")
.attr("font-size", 11)
.text(d => `${d.lowerBound.toFixed(2)} - ${d.upperBound.toFixed(2)}`);
legend
.append("text")
.attr("dy", -5)
.attr("font-size", 14)
.attr("text-decoration", "underline");
}
draw();
}
getRepoCommits("torvalds/linux", (data)=>
{
console.log(data);
createHeatmapGraph(data, "svg");
},
(error)=>
{
console.log(error);
})
</script>
</body>
</html>

+ 14
- 0
public/downloadEvents.html View File

@ -0,0 +1,14 @@
<html>
<script>
function downloadObjectAsJson(exportObj, exportName)
{
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
var downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
</script>
</html>

+ 8
- 0
public/js/githubAPI.js View File

@ -90,6 +90,14 @@ function getOrganizationMembers(orgName, suc, err)
}
function getRepoCommits(repository, suc, err)
{
const urlpath = APIROOT + "/repositories/commits/" + repository;
runAjax(urlpath, suc, err);
}
/**
* Queries github API end points with the backend
* proxy server for github graphs.

+ 23
- 18
routes/api/v2.js View File

@ -9,9 +9,9 @@ const API_FOLLOWING = "/following";
const API_FOLLOWERS = "/followers";
const API_USER_PATH = "/users/";
const API_ORGS_PATH = "/orgs/";
const API_PAGINATION_SIZE = 100; // 100 is the max, 30 is the default
const API_PAGINATION_SIZE = 5; // 100 is the max, 30 is the default
// if this is too large, it would be infeasible to make graphs for people following popular people
const API_MAX_PAGES = 2;
const API_MAX_PAGES = 30;
const API_PAGINATION = "&per_page=" + API_PAGINATION_SIZE;
const REPOS_PATH = "/repos";
@ -21,20 +21,21 @@ const REPOS_PATH = "/repos";
* Queries data from the github APi server and returns it as
* a json object in a promise.
*
* This makes no attempt to cache
* This makes **no attempt to cache**
*
* @param {*} requestURL endpoint on githubapi: ex: /users/jrtechs/following
*/
const queryGithubAPIRaw = async requestURL => {
let queryURL = requestURL.includes("?page=") ? `${GITHUB_API}${requestURL}&${authenticate}` :`${GITHUB_API}${requestURL}?${authenticate}`;
console.log(queryURL);
try {
try
{
const req = await got(queryURL, { json: true });
cache.put(requestURL, req);
return req;
} catch (error) {
return req.body;
}
catch (error)
{
console.log(error);
cache.put(requestURL, `${error.statusCode} - ${error.statusMessage}`);
}
}
@ -52,8 +53,11 @@ const queryGitHubAPI = async requestURL => {
return apiData
}
try {
return await queryGithubAPIRaw(requestURL);
try
{
let d = await queryGithubAPIRaw(requestURL);
return d;
cache.put(requestURL, d);
} catch (error) {
console.log(error);
}
@ -72,12 +76,12 @@ const queryGitHubAPI = async requestURL => {
const fetchAllWithPagination = async (apiPath, page, lst) => {
try {
const req = await queryGithubAPIRaw(`${apiPath}?page=${page}${API_PAGINATION}`);
if (req.body.hasOwnProperty("length"))
if (req.hasOwnProperty("length"))
{
const list = lst.concat(req.body);
console.log(req.body.length);
const list = lst.concat(req);
console.log(req.length);
//console.log(req.body);
if(page < API_MAX_PAGES && req.body.length === API_PAGINATION_SIZE) {
if(page < API_MAX_PAGES && req.length === API_PAGINATION_SIZE) {
const redo = await fetchAllWithPagination(apiPath, page + 1, list);
return redo;
}
@ -268,7 +272,7 @@ const minimizeCommits = commits => {
}
cList.push(obj);
});
console.log("returning " + cList.length + " commits.")
return cList;
}
@ -303,7 +307,7 @@ routes.get("/repositories/events/:name/:repository", async (req, res)=>
try
{
const query = await queryRepositoryEvents(req.params.name, req.params.repository);
res.json(query);
res.json(query).end();
} catch (error) {
res.status(500).json({error: 'API error fetching repository events'});
}
@ -316,7 +320,8 @@ routes.get("/repositories/commits/:name/:repository", async (req, res)=>
try
{
const query = await queryRepositoryCommits(req.params.name, req.params.repository);
res.json(query);
console.log("finished");
res.json(query).end();
} catch (error) {
res.status(500).json({error: 'API error fetching repository events'});
}
@ -329,7 +334,7 @@ routes.get("/repositories/commits/:name/:repository", async (req, res)=>
routes.get("/friends/:name", async (req, res)=> {
try {
const query = await queryFriends(req.params.name);
res.json(query);
res.json(query).end();
} catch (error) {
res.status(500).json({error: 'API error fetching friends'});
}

Loading…
Cancel
Save