@ -0,0 +1,14 @@ | |||||
# Build/Maven # | |||||
############### | |||||
**/target/ | |||||
# Eclipse # | |||||
########### | |||||
.project | |||||
.classpath | |||||
.settings | |||||
**/bin/ | |||||
.metadata/ | |||||
# IntelliJ # | |||||
############ | |||||
.idea/ | |||||
*.iml |
@ -1,9 +1,8 @@ | |||||
package net.jrtechs.www.server; | |||||
package net.jrtechs.www.model; | |||||
import org.json.JSONObject; | import org.json.JSONObject; | ||||
import javax.json.JsonObject; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
@ -1,4 +1,4 @@ | |||||
package net.jrtechs.www.server; | |||||
package net.jrtechs.www.model; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; |
@ -0,0 +1,58 @@ | |||||
package net.jrtechs.www.server; | |||||
import com.google.gson.Gson; | |||||
import com.google.gson.reflect.TypeToken; | |||||
import net.jrtechs.www.graphDB.SteamGraph; | |||||
import net.jrtechs.www.model.Game; | |||||
import net.jrtechs.www.model.Player; | |||||
import java.lang.reflect.Type; | |||||
import java.util.List; | |||||
import static spark.Spark.*; | |||||
/** | |||||
* Quick and dirty web server to serve as an API backend to | |||||
* the graph interface. | |||||
* | |||||
* @author Jeffery Russell 7-12-20 | |||||
*/ | |||||
public class WebServer | |||||
{ | |||||
private SteamGraph graph; | |||||
private Gson gson; | |||||
public static String GET_PLAYER = "/player"; | |||||
public static String GET_GAMES = "/games"; | |||||
public WebServer() | |||||
{ | |||||
this.graph = new SteamGraph(); | |||||
this.gson = new Gson(); | |||||
int maxThreads = 8; | |||||
int minThreads = 2; | |||||
int timeOutMillis = 3000000; | |||||
threadPool(maxThreads, minThreads, timeOutMillis); | |||||
Type typePlayer = new TypeToken<Player>(){}.getType(); | |||||
Type typeGames = new TypeToken<List<Game>>(){}.getType(); | |||||
staticFiles.externalLocation("./website"); | |||||
get("/player/:id", (req, res) -> | |||||
gson.toJson( | |||||
graph.getPlayer(req.params(":id")), typePlayer)); | |||||
get("/games/:id", (req, res) -> | |||||
gson.toJson( | |||||
graph.getGameList(req.params(":id")), typeGames)); | |||||
System.out.println("Finished starting web server"); | |||||
} | |||||
public static void main(String[] arguments) | |||||
{ | |||||
new WebServer(); | |||||
} | |||||
} |
@ -1,6 +1,7 @@ | |||||
package net.jrtechs.www.server; | |||||
package net.jrtechs.www.server.old; | |||||
import net.jrtechs.www.graphDB.SteamGraph; | import net.jrtechs.www.graphDB.SteamGraph; | ||||
import net.jrtechs.www.model.Player; | |||||
import org.java_websocket.WebSocket; | import org.java_websocket.WebSocket; | ||||
import org.json.JSONObject; | import org.json.JSONObject; | ||||
@ -1,4 +1,4 @@ | |||||
package net.jrtechs.www.server; | |||||
package net.jrtechs.www.server.old; | |||||
import org.java_websocket.WebSocket; | import org.java_websocket.WebSocket; | ||||
import org.java_websocket.handshake.ClientHandshake; | import org.java_websocket.handshake.ClientHandshake; |
@ -0,0 +1,100 @@ | |||||
function profileGen(username, container) | |||||
{ | |||||
queryAPIByUser("", username, (user) => | |||||
{ | |||||
if(!user.hasOwnProperty("id")) | |||||
{ | |||||
alert("User Does Not Exist"); | |||||
window.location.href = "./GraphGenerator.html"; | |||||
} | |||||
parseOrgs(user.login).then( (orgsReturn) => { | |||||
if (user.blog) { | |||||
const rx = new RegExp("^(http|https)://", "i"); | |||||
const match = rx.test(user.blog); | |||||
user.blog = match ? user.blog : `http://${user.blog}`; | |||||
} | |||||
let html = ` | |||||
<div class="card shadow-sm" style="font-size: 16px;"> | |||||
<div class="card-img-top" style="position: relative;"> | |||||
<img src="${user.avatar_url}" alt="${user.avatar_url}" class="img-fluid"/> | |||||
<div class="actions"> | |||||
<a href="${graphUrl(user.login)}" class="btns" title="View Friends Graph"> | |||||
<svg viewBox="0 0 24 24"> | |||||
<path d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" /> | |||||
</svg> | |||||
</a> | |||||
<a href="${timelineUrl(user.login)}" class="btns" title="View Timeline Graph"> | |||||
<svg viewBox="0 0 24 24"> | |||||
<path d="M2,2H4V20H22V22H2V2M7,10H17V13H7V10M11,15H21V18H11V15M6,4H22V8H20V6H8V8H6V4Z" /> | |||||
</svg> | |||||
</a> | |||||
</div> | |||||
</div> | |||||
<div class="card-body"> | |||||
${user.name ? `<h5 class="card-title mb-1">${user.name}</h5>` : ""} | |||||
<a href="${user.html_url}" class="card-subtitle text-muted">${user.login}</a> | |||||
${user.bio != null ? `<p class="my-2">${user.bio}</p>` : ""} | |||||
<ul class="list-unstyled"> | |||||
${user.blog != null ? `<a href="${user.blog}"><li>${user.blog}</li></a>` : ""} | |||||
${user.email != null ? `<li>Email: ${user.email}</li>` : ""} | |||||
${user.location != null ? `<li>Location: ${user.location}</li>` : ""} | |||||
${user.company != null ? `<li>Company: ${user.company}</li>` : ""} | |||||
</ul> | |||||
<hr /> | |||||
<ul class="list-unstyled"> | |||||
<li>Followers: ${user.followers}</li> | |||||
<li>Following: ${user.following}</li> | |||||
<li>Repositories: ${user.public_repos}</li> | |||||
</ul> | |||||
${orgsReturn != [] ? `<hr /> <p>Organizations</p> ${orgsReturn}` : ""} | |||||
</div> | |||||
</div> | |||||
`; | |||||
$("#"+container).html(html); | |||||
}) | |||||
}, () => | |||||
{ | |||||
alert("User Does Not Exist"); | |||||
window.location.href = "./GraphGenerator.html"; | |||||
}); | |||||
} | |||||
function parseOrgs(name) { | |||||
return new Promise( (resolve, reject) => { | |||||
let urlpath = `api/users/${name}/orgs`; | |||||
let orgs_final = []; | |||||
queryUrl(urlpath, (orgs) => { | |||||
var prom= []; | |||||
for(var i = 0;i < orgs.length; i++) { | |||||
prom.push( new Promise( (res, rej) => { | |||||
url = orgs[i].url; | |||||
queryUrl(url, (orgData) => { | |||||
console.log(orgData); | |||||
orgs_final.push("<a href=\"OrgRepoGraph.html?name="+orgData.login+"\"><img src=\""+orgData.avatar_url+"\" class=\"img-fluid\" style=\"max-width:35px\"></img></a>"); | |||||
res(); | |||||
}, (error) => { | |||||
console.log(error); | |||||
rej(error); | |||||
console.error("error getting org info"); | |||||
}); | |||||
}) | |||||
) | |||||
} | |||||
Promise.all(prom).then(function() { | |||||
resolve(orgs_final.join(" ")); | |||||
}) | |||||
}, (error) => { | |||||
resolve([]); | |||||
}); | |||||
}) | |||||
} | |||||
function graphUrl(user) { | |||||
return "/FriendsGraph.html?name="+user; | |||||
} | |||||
function timelineUrl(user) { | |||||
return "/TimeLineGraph.html?name="+user; | |||||
} |
@ -0,0 +1,107 @@ | |||||
<!doctype html> | |||||
<html lang="en"> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |||||
<title>Friends - Steam Graphs</title> | |||||
<link rel="icon" href="./favicon.ico" type="image/x-icon" /> | |||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" /> | |||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | |||||
<link rel="stylesheet" href="./style.css" /> | |||||
<script | |||||
src="https://code.jquery.com/jquery-3.3.1.min.js" | |||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" | |||||
crossorigin="anonymous"> | |||||
</script> | |||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" | |||||
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" | |||||
crossorigin="anonymous"> | |||||
</script> | |||||
<script src="js/steamAPI.js"></script> | |||||
<script src="js/friendsGraph.js"></script> | |||||
<script src="js/profileGen.js"></script> | |||||
<script src="js/utilities.js"></script> | |||||
<script type="text/javascript" src="js/vis/vis.js"></script> | |||||
<link href="js/vis/vis-network.min.css" rel="stylesheet" type="text/css" /> | |||||
</head> | |||||
<body class="friends-graph-page"> | |||||
<nav class="navbar navbar-dark bg-dark navbar-expand-md"> | |||||
<a class="navbar-brand" href="/"><img src="img/githubgraph-logo.svg" alt=""></a> | |||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#main-menu" aria-controls="main-menu" aria-expanded="false" aria-label="Toggle navigation"> | |||||
<span class="navbar-toggler-icon"></span> | |||||
</button> | |||||
<div id="main-menu" class="collapse navbar-collapse"> | |||||
<ul class="navbar-nav flex-fill justify-content-end"> | |||||
<li class="nav-item"> | |||||
<a class="nav-link" href="./GraphGenerator.html">Generate graphs</a> | |||||
</li> | |||||
<li class="nav-item"> | |||||
<a class="nav-link" href="https://github.com/jrtechs/github-graphs/">View on GitHub</a> | |||||
</li> | |||||
<li class="nav-item"> | |||||
<a class="nav-link" href="./about.html">About</a> | |||||
</li> | |||||
</ul> | |||||
</div> | |||||
</nav> | |||||
<div class="main"> | |||||
<div class="container-fluid container-xl"> | |||||
<div class="row pt-5"> | |||||
<div class="col-lg-3 col-md-4 col-12"></div> | |||||
<div class="col-lg-9 col-md-8 col-12"> | |||||
<div class="d-flex align-items-baseline justify-content-between my-4"> | |||||
<div class="d-flex align-items-center"> | |||||
<svg class="mr-3" width="36" height="36" viewBox="0 0 36 36" fill="none" | |||||
xmlns="http://www.w3.org/2000/svg"> | |||||
<path | |||||
d="M18 0C8.064 0 0 8.064 0 18C0 27.936 8.064 36 18 36C27.936 36 36 27.936 36 18C36 8.064 27.936 0 18 0ZM18 5.4C20.988 5.4 23.4 7.812 23.4 10.8C23.4 13.788 20.988 16.2 18 16.2C15.012 16.2 12.6 13.788 12.6 10.8C12.6 7.812 15.012 5.4 18 5.4ZM18 30.96C15.8613 30.96 13.7559 30.4308 11.8715 29.4194C9.98707 28.4081 8.3822 26.9462 7.2 25.164C7.254 21.582 14.4 19.62 18 19.62C21.582 19.62 28.746 21.582 28.8 25.164C27.6178 26.9462 26.0129 28.4081 24.1285 29.4194C22.2441 30.4308 20.1387 30.96 18 30.96Z" | |||||
fill="white" /> | |||||
</svg> | |||||
<h1 class="text-white font-weight-bold">Interactive friend chart</h1> | |||||
</div> | |||||
<div> | |||||
<a id="TimelineLink" class="text-light" href="#">View repo timeline</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="row pb-4"> | |||||
<div class="col-lg-3 col-md-4 col-12"> | |||||
<div id="profileGen"></div> | |||||
</div> | |||||
<div class="col-lg-9 col-md-8 col-12"> | |||||
<div class="card shadow text-white bg-dark border-white"> | |||||
<h2 id="graphLoading"></h2> | |||||
<div id="myGraph" class="w-100"></div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</body> | |||||
<script> | |||||
$(function () { | |||||
$('[data-toggle="tooltip"]').tooltip(); | |||||
}); | |||||
function createGraphs(id) | |||||
{ | |||||
options.width = $("#myGraph").width() + "px"; | |||||
options.height = "700px"; | |||||
createFriendsGraph(id, "myGraph", "graphLoading"); | |||||
profileGen(id, "profileGen"); | |||||
} | |||||
if(findGetParameter("id") !== null) | |||||
{ | |||||
createGraphs(findGetParameter("id")) | |||||
} | |||||
$('#TimelineLink').attr("href", "TimeLineGraph.html?name=" + findGetParameter("name")); | |||||
</script> | |||||
</html> |
@ -0,0 +1,28 @@ | |||||
function createOrgInfo(orgName, container) { | |||||
queryAPIByOrg("", orgName, (orgData) => { | |||||
var html = ` | |||||
<div class="card shadow py-4 px-3"> | |||||
<div class="row align-items-center"> | |||||
<div class="col-md-2 text-center"> | |||||
<img src="${orgData.avatar_url}" alt="${orgData.name}" class="img-fluid" style="max-width: 100px"/> | |||||
</div> | |||||
<div class="col-md-10"> | |||||
<div class="media-body"> | |||||
<h1 class="h4">${orgData.name}</h1> | |||||
<p class="text-muted">${orgData.description}</p> | |||||
<ul class="d-flex list-unstyled mb-0"> | |||||
<li>${orgData.location}</li> | |||||
</ul> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
$("#" + container).html(html); | |||||
}, function(error) { | |||||
alert("Organization Does Not Exist"); | |||||
window.location.href = "./GraphGenerator.html"; | |||||
}); | |||||
} |
@ -0,0 +1,65 @@ | |||||
/** | |||||
* Add's all the members of the organization into the graphs | |||||
* node objects | |||||
* | |||||
* @param orgname | |||||
* @param page | |||||
* @returns {Promise<any>} | |||||
*/ | |||||
function addOrgUsers(orgname) | |||||
{ | |||||
return new Promise(function(resolve, reject) | |||||
{ | |||||
getOrganizationMembers(orgname, (data)=> | |||||
{ | |||||
for(var i = 0;i < data.length; i++) | |||||
{ | |||||
addPersonToGraph(data[i]); | |||||
} | |||||
total = data.length; | |||||
resolve(); | |||||
}, (error)=> | |||||
{ | |||||
console.log(error); | |||||
resolve(); | |||||
}) | |||||
}) | |||||
} | |||||
/** | |||||
* Creates a graph | |||||
* @param username | |||||
* @param containerName | |||||
* @param graphsTitle | |||||
*/ | |||||
function createOrgRepoGraph(orgname, containerName, graphsTitle) | |||||
{ | |||||
progressID = graphsTitle; | |||||
nodes = []; | |||||
edges = []; | |||||
addOrgUsers(orgname).then(function() | |||||
{ | |||||
createConnections().then( () => { | |||||
var container = document.getElementById(containerName); | |||||
var data = { | |||||
nodes: nodes, | |||||
edges: edges | |||||
}; | |||||
var network = new vis.Network(container, data, options); | |||||
network.on("click", function (params) { | |||||
params.event = "[original event]"; | |||||
if(this.getNodeAt(params.pointer.DOM) !== NaN) | |||||
{ | |||||
bringUpProfileView(this.getNodeAt(params.pointer.DOM)); | |||||
} | |||||
}); | |||||
$("#graphLoading").html(""); | |||||
}); | |||||
}).catch(function(error) { | |||||
alert("Invalid Organization"); | |||||
}); | |||||
} |
@ -0,0 +1,79 @@ | |||||
function generateHtmlRow(repoData) { | |||||
var html = ` | |||||
<tr> | |||||
<td> | |||||
${repoData.language === 'null' | |||||
? '<div class="bg-light d-inline-block" style="height: 14px; width: 14px; border-radius: 7px"></div>' | |||||
: `<i class="devicon-${repoData.language}-plain colored"></i>`} | |||||
<a class="text-reset ml-1" href="${repoData.html_url}" target="_blank">${repoData.name}</a> | |||||
</td> | |||||
<td class="text-right">${repoData.forks}</td> | |||||
</tr> | |||||
`; | |||||
return html; | |||||
} | |||||
var repos = []; | |||||
function fetchAllRepositories(orgName) | |||||
{ | |||||
console.log("Going for it"); | |||||
return new Promise((resolve, reject)=> | |||||
{ | |||||
getOrganizationRepositories(orgName, | |||||
(data)=> | |||||
{ | |||||
console.log("Dam did got it"); | |||||
repos.push(...data); | |||||
resolve(); | |||||
}, | |||||
(error)=> | |||||
{ | |||||
console.log("Unable to load table data"); | |||||
reject("Error fetching repositories"); | |||||
}); | |||||
}); | |||||
} | |||||
function createOrgTable(orgName, tableContainer) | |||||
{ | |||||
var html = ""; | |||||
fetchAllRepositories(orgName).then(function() { | |||||
for (var i=0; i < repos.length; i++) { | |||||
let icon = repos[i].language; | |||||
icon === null | |||||
? icon = 'null' | |||||
: icon = icon.toLowerCase(); | |||||
icon === 'c++' | |||||
? icon = 'cplusplus' | |||||
: null; | |||||
icon === 'c#' | |||||
? icon = 'csharp' | |||||
: null; | |||||
repos[i].language = icon; | |||||
html += generateHtmlRow(repos[i]); | |||||
} | |||||
$("#" + tableContainer).html(html); | |||||
setTimeout(function() { | |||||
$('#dataTable').DataTable({ | |||||
pageLength: 15, | |||||
pagingType: 'simple', | |||||
bLengthChange : false, | |||||
"bFilter" : false | |||||
}); | |||||
}, 1500); | |||||
}).catch(function(error) | |||||
{ | |||||
console.log("Unable to create table"); | |||||
}); | |||||
} |
@ -0,0 +1,307 @@ | |||||
/** Nodes in the vis js graph */ | |||||
var nodes; | |||||
/** Edges used to make the Vis JS graph*/ | |||||
var edges; | |||||
/** Used for the loading bar */ | |||||
var total = 1; | |||||
var indexed = 0; | |||||
var progressID; | |||||
/** Github id of the user being indexed */ | |||||
var baseID; | |||||
/** | |||||
* Vis js graph options | |||||
*/ | |||||
var options = { | |||||
nodes: { | |||||
borderWidth:4, | |||||
size:30, | |||||
color: { | |||||
border: '#222222', | |||||
background: '#666666' | |||||
}, | |||||
font:{color:'#eeeeee'} | |||||
}, | |||||
edges: { | |||||
color: 'lightgray' | |||||
} | |||||
}; | |||||
/** | |||||
* Checks if a user is a node in the graph | |||||
* | |||||
* @param userID | |||||
* @returns {boolean} | |||||
*/ | |||||
function alreadyInGraph(username) | |||||
{ | |||||
for(var i = 0; i < nodes.length; i++) | |||||
{ | |||||
if(nodes[i].id === username) | |||||
{ | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* adds a person to the nodes list | |||||
* | |||||
* @param profileData | |||||
*/ | |||||
function addPersonToGraph(profileData) | |||||
{ | |||||
addManualToGraph(profileData.id, profileData.avatar); | |||||
for(var i = 0; i < profileData.friends.length; i++) | |||||
{ | |||||
addManualToGraph(profileData.friends[i].id, | |||||
profileData.friends[i].avatar); | |||||
} | |||||
} | |||||
function addManualToGraph(id,avatar) | |||||
{ | |||||
nodes.push( | |||||
{ | |||||
id:id, | |||||
shape: 'circularImage', | |||||
image:avatar | |||||
}); | |||||
} | |||||
/** | |||||
* Adds the followers/following of a person | |||||
* to the graph | |||||
* | |||||
* @param username | |||||
* @param apiPath | |||||
* @returns {Promise<any>} | |||||
*/ | |||||
function addFriends(username) | |||||
{ | |||||
updateProgress(); | |||||
return new Promise((resolve, reject)=> | |||||
{ | |||||
getPersonAPI(username, (data)=> | |||||
{ | |||||
for(var i = 0; i < data.length; i++) | |||||
{ | |||||
if(!alreadyInGraph(data[i].login)) | |||||
{ | |||||
addPersonToGraph(data[i]); | |||||
} | |||||
} | |||||
resolve(); | |||||
}, | |||||
(error)=> | |||||
{ | |||||
reject(error); | |||||
}) | |||||
}); | |||||
} | |||||
/** | |||||
* Greedy function which checks to see if a edge is in the graphs | |||||
* | |||||
* @param id1 | |||||
* @param id2 | |||||
* @returns {boolean} | |||||
*/ | |||||
function edgeInGraph(id1, id2) | |||||
{ | |||||
for(var i = 0;i < edges.length; i++) | |||||
{ | |||||
if((edges[i].to === id1 && edges[i].from === id2) || | |||||
(edges[i].from === id1 && edges[i].to === id2)) | |||||
{ | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Adds a connection to the graph | |||||
* | |||||
* @param person1 | |||||
* @param person2 | |||||
*/ | |||||
function addConnection(id1, id2) | |||||
{ | |||||
if(id1 !== id2) | |||||
{ | |||||
if(alreadyInGraph(id2) && !edgeInGraph(id1, id2)) | |||||
{ | |||||
network.body.data.edges.add([{ | |||||
from: id1, | |||||
to: id2 | |||||
}]); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Processes all the connections of a user and adds them to the graph | |||||
* | |||||
* @param user has .id and .name | |||||
* @returns {Promise<any>} | |||||
*/ | |||||
function processUserConnections(node) | |||||
{ | |||||
getPersonAPI(node.id, | |||||
(data)=> | |||||
{ | |||||
updateProgress(); | |||||
for(var i = 0; i < data.friends.length; i++) | |||||
{ | |||||
addConnection(node.id, data.friends[i].id) | |||||
} | |||||
}, (error)=> | |||||
{ | |||||
console.log(error); | |||||
}); | |||||
} | |||||
/** | |||||
* Creates connections between all the nodes in | |||||
* the graph. | |||||
* | |||||
* @returns {Promise<any>} | |||||
*/ | |||||
function createConnections() | |||||
{ | |||||
var prom = []; | |||||
for(var i = 0; i < nodes.length; i++) | |||||
{ | |||||
processUserConnections(nodes[i]); | |||||
} | |||||
} | |||||
/** | |||||
* Updates progress bar for loading the JS graph | |||||
*/ | |||||
function updateProgress() | |||||
{ | |||||
indexed++; | |||||
if(indexed >= total) | |||||
{ | |||||
$("#" + progressID).html(""); | |||||
} | |||||
else | |||||
{ | |||||
const percent = parseInt((indexed/total)*100); | |||||
$("#" + progressID).html("<div class=\"progress\">\n" + | |||||
" <div class=\"progress-bar progress-bar-striped progress-bar-animated\" role=\"progressbar\" style=\"width: " + percent + "%\" aria-valuenow=\"" + percent + "\" aria-valuemin=\"0\" aria-valuemax=\"100\"></div>\n" + | |||||
"</div>"); | |||||
} | |||||
} | |||||
var selfData; | |||||
/** | |||||
* Adds the base person to the graph. | |||||
* | |||||
* @param username | |||||
* @returns {Promise<any>} | |||||
*/ | |||||
function addSelfToGraph(id) | |||||
{ | |||||
return new Promise((resolve, reject)=> | |||||
{ | |||||
getPersonAPI(id, (data)=> | |||||
{ | |||||
selfData = data; | |||||
baseID = data.id; | |||||
total = data.friends.length; | |||||
addPersonToGraph(data); | |||||
resolve(); | |||||
}, | |||||
(error)=> | |||||
{ | |||||
reject(error); | |||||
}); | |||||
}); | |||||
} | |||||
/** | |||||
* Used for the on graph click event | |||||
* | |||||
* @param github id | |||||
*/ | |||||
function bringUpProfileView(uname) | |||||
{ | |||||
console.log(uname); | |||||
if(uname === selfData.id) | |||||
{ | |||||
profileGen(selfData, "profileGen"); | |||||
} | |||||
else | |||||
{ | |||||
for(var i = 0; i < selfData.friends.length; i++) | |||||
{ | |||||
if(selfData.friends[i].id === uname) | |||||
{ | |||||
profileGen(selfData.friends[i], "profileGen"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
var network; | |||||
/** | |||||
* Creates a graph | |||||
* @param username | |||||
* @param containerName | |||||
* @param progressBarID | |||||
*/ | |||||
function createFriendsGraph(username, containerName, progressBarID) | |||||
{ | |||||
progressID = progressBarID; | |||||
nodes = []; | |||||
edges = []; | |||||
addSelfToGraph(username).then(()=> | |||||
{ | |||||
$("#" + progressID).html(""); | |||||
var container = document.getElementById(containerName); | |||||
var data = | |||||
{ | |||||
nodes: nodes, | |||||
edges: edges | |||||
}; | |||||
network = new vis.Network(container, data, options); | |||||
bringUpProfileView(selfData.id); | |||||
network.on("click", function (params) | |||||
{ | |||||
if(Number(this.getNodeAt(params.pointer.DOM)) !== NaN) | |||||
{ | |||||
bringUpProfileView(this.getNodeAt(params.pointer.DOM)); | |||||
} | |||||
}); | |||||
createConnections().then(()=> | |||||
{ | |||||
// $("#" + progressID).html(""); | |||||
console.log("Finished"); | |||||
}) | |||||
}).catch((error)=> | |||||
{ | |||||
//$("#" + graphsTitle).html("Error Fetching Data From API"); | |||||
alert("Invalid User"); | |||||
}); | |||||
} |
@ -0,0 +1,19 @@ | |||||
function profileGen(data, container) | |||||
{ | |||||
let html = ` | |||||
<div class="card shadow-sm" style="font-size: 16px;"> | |||||
<div class="card-img-top" style="position: relative;"> | |||||
<img src="${data.avatar}" alt="${data.avatar}" width="100%" class="img-fluid"/> | |||||
</div> | |||||
<div class="card-body"> | |||||
${data.name ? `<h5 class="card-title mb-1">${data.name}</h5>` : ""} | |||||
<a href="${data.avatar}" class="card-subtitle text-muted">${data.realName}</a> | |||||
<ul class="list-unstyled"> | |||||
<li><a href="https://steamcommunity.com/profiles/${data.id}">Steam profile</a></li> | |||||
<li><a href="/friendsGraph.html?id=${data.id}">Friends Graph</a></li> | |||||
</ul> | |||||
</div> | |||||
</div> | |||||
`; | |||||
$("#"+container).html(html); | |||||
} |
@ -0,0 +1,110 @@ | |||||
var events = []; | |||||
var repositoryData; | |||||
function addEvent(group, date, content) | |||||
{ | |||||
var dateFormat = new Date(date); | |||||
var dd = new Date(dateFormat.getFullYear(), dateFormat.getMonth(), dateFormat.getDay()); | |||||
events.push({id: events.length, group: group, start: dd, content: content}); | |||||
} | |||||
// {id: 0, group: 0, start: new Date(2013,7,1), end: new Date(2017,5,15), content: 'High School'}, | |||||
function addRepositories(userName, groupID) | |||||
{ | |||||
return new Promise(function(resolve, reject) | |||||
{ | |||||
getUserRepositories(userName, | |||||
function(data) | |||||
{ | |||||
repositoryData = data; | |||||
for(var i = 0; i < data.length; i++) | |||||
{ | |||||
data[i].id = events.length; | |||||
addEvent(groupID, data[i]['created_at'], data[i]['name']) | |||||
} | |||||
resolve(); | |||||
}, | |||||
function(error) | |||||
{ | |||||
console.log(error); | |||||
reject(error); | |||||
}) | |||||
}) | |||||
} | |||||
function timeLineClickEvent(properties) | |||||
{ | |||||
if(properties.item !== null && typeof repositoryData[properties.item].name !== 'undefined') | |||||
{ | |||||
var item = repositoryData[properties.item]; | |||||
if (item.license === null) { | |||||
item.license = new Object(); | |||||
item.license.name = 'None'; | |||||
} | |||||
var html = ` | |||||
<div class="card shadow" style="font-size: 16px"> | |||||
<div class="card-body"> | |||||
<h5 class="card-title">${item.name}</h5> | |||||
<p class="card-subtitle text-muted">${item.description ? item.description : 'No description'}</p> | |||||
<div class="row py-3"> | |||||
<div class="col-12 col-md-8"> | |||||
${item.homepage ? `<p class="mb-0">Homepage: <a href="${item.license.name}">${item.license.name}</a></p>` : ''} | |||||
<p class="mb-0">Repository URL: <a href="${item.html_url}">${item.html_url}</a></p> | |||||
<p class="mb-0">Languages: ${item.language ? item.language : 'Not specified'}</p> | |||||
<p class="mb-0">License: ${item.license.name}</p> | |||||
</div> | |||||
<div class="col-12 col-md-4"> | |||||
<p class="mb-0">Fork Count: ${item.forks}</p> | |||||
<p class="mb-0">Open Issues: ${item.open_issues_count}</p> | |||||
<p class="mb-0">Watchers: ${item.watchers}</p> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
$("#repositoryInformation").html(html); | |||||
} | |||||
} | |||||
function createProfileTimeLine(username, containerName) | |||||
{ | |||||
var container = document.getElementById(containerName); | |||||
var prom = [addRepositories(username, 1)]; | |||||
var groups = new vis.DataSet([ | |||||
{id: 1, content: 'Repositories', value: 2} | |||||
]); | |||||
Promise.all(prom).then(function() | |||||
{ | |||||
// note that months are zero-based in the JavaScript Date object | |||||
var items = new vis.DataSet(events); | |||||
var options = { | |||||
// option groupOrder can be a property name or a sort function | |||||
// the sort function must compare two groups and return a value | |||||
// > 0 when a > b | |||||
// < 0 when a < b | |||||
// 0 when a == b | |||||
groupOrder: function (a, b) { | |||||
return a.value - b.value; | |||||
}, | |||||
margin: { | |||||
item: 20, | |||||
axis: 40 | |||||
} | |||||
}; | |||||
var timeline = new vis.Timeline(container); | |||||
timeline.setOptions(options); | |||||
timeline.setGroups(groups); | |||||
timeline.setItems(items); | |||||
timeline.on('click', timeLineClickEvent); | |||||
}); | |||||
} |
@ -0,0 +1,74 @@ | |||||
/** | |||||
* Simple file which uses jQuery's ajax | |||||
* calls to make it easier to get data | |||||
* from the steam api. | |||||
* | |||||
* @author Jeffery Russell 2-16-19, 7-12-20 | |||||
*/ | |||||
const APIROOT = ""; | |||||
const API_USER_PATH = "/player/"; | |||||
const API_GAMES_PATH = "/games/" | |||||
/** | |||||
* Fetches a list of fiends for a user. | |||||
* | |||||
* @param {*} userName | |||||
* @param {*} suc | |||||
* @param {*} err | |||||
*/ | |||||
function getPersonAPI(userID, suc, err) | |||||
{ | |||||
// api/friends/jrtechs | |||||
const urlpath = APIROOT + API_USER_PATH + userID; | |||||
runAjax(urlpath, suc, err); | |||||
} | |||||
function getUserGames(userID, suc, err) | |||||
{ | |||||
//ex: http://localhost:7000/api/repositories/jwflory | |||||
const urlpath = APIROOT + "/repositories/" + userID; | |||||
runAjax(urlpath, suc, err); | |||||
} | |||||
/** | |||||
* Queries github API end points with the backend | |||||
* proxy server for github graphs. | |||||
* | |||||
* @param {*} url | |||||
* @param {*} successCallBack | |||||
* @param {*} errorCallBack | |||||
*/ | |||||
function queryUrl(url, successCallBack, errorCallBack) | |||||
{ | |||||
url = url.split("https://api.github.com/").join("api/"); | |||||
runAjax(url, successCallBack, errorCallBack); | |||||
} | |||||
/** | |||||
* Wrapper for AJAX calls so we can unify | |||||
* all of our settings. | |||||
* | |||||
* @param {*} url -- url to query | |||||
* @param {*} successCallBack -- callback with data retrieved | |||||
* @param {*} errorCallBack -- callback with error message | |||||
*/ | |||||
function runAjax(url, successCallBack, errorCallBack) | |||||
{ | |||||
console.log(url); | |||||
$.ajax({ | |||||
type:'GET', | |||||
url: url, | |||||
crossDomain: true, | |||||
dataType: "json", | |||||
success: successCallBack, | |||||
error:errorCallBack, | |||||
timeout: 300000 | |||||
}); | |||||
} |
@ -0,0 +1,11 @@ | |||||
function findGetParameter(parameterName) | |||||
{ | |||||
var result = null, | |||||
tmp = []; | |||||
var items = location.search.substr(1).split("&"); | |||||
for (var index = 0; index < items.length; index++) { | |||||
tmp = items[index].split("="); | |||||
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]); | |||||
} | |||||
return result; | |||||
} |