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
69 changes: 69 additions & 0 deletions assets/data/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"title": "The Development Seed Contributor Network",
"author": "Pete Gadomski",
"description": "An interactive visualization of contributors to Development Seed code and their connections to other repositories",
"central_repository": "developmentseed/DevSeed Team",
"contributor_padding": 20,
"contributors": {
"AMSCamacho": "Angela Camacho",
"AliceR": "Alice Rühl",
"LanesGood": "Lane Goodman",
"abarciauskas-bgse": "Aimee Barciauskas",
"aboydnw": "Anthony Boyd",
"aliziel": "Ali Z",
"alukach": "Anthony Lukach",
"anayeaye": "Alexandra Kirk",
"batpad": "Sanjay Bhangar",
"beatrizbsperes": "Beatriz Peres",
"benbovy": "Benoît Bovy",
"bitner": "David Bitner",
"botanical": "Jennifer Tran",
"brianna-corremonte": "Brianna Corremonte",
"camillecroft": "Camille Croft",
"ceholden": "Chris Holden",
"chuckwondo": "Chuck Daniels",
"ciaransweet": "Ciaran Sweet",
"danielfdsilva": "Daniel da Silva",
"dannybauman": "Danny Bauman",
"dzole0311": "Gjore Milevski",
"emmalu": "Emma Paz",
"emmanuelmathot": "Emmanuel Mathot",
"faustoperez": "Fausto Pérez",
"gadomski": "Pete Gadomski",
"geohacker": "Sajjad Anwar",
"hanbyul-here": "Hanbyul Jo",
"hrodmn": "Henry Rodman",
"ianschuler": "Ian Schuler",
"indraneel": "Indraneel Purohit",
"ividito": "Isayah Vidito",
"j08lue": "Jonas Sølvsteen",
"jjfrench": "Jamison French",
"kamicut": "Marc Farra",
"kcarini": "Kiri Carini",
"kevinbullock": "Kevin Bullock",
"kimmurph": "Kim Murphy",
"kylebarron": "Kyle Barron",
"leothomas": "Leo Thomas",
"lillythomas": "Lilly Thomas",
"maxrjones": "Max Jones",
"martham93": "Martha Morrissey",
"olafveerman": "Olaf Veerman",
"omniajoe": "Omnia Joehar",
"pantierra": "xıʃǝɟ",
"ricardoduplos": "Ricardo Duplos",
"sandrahoang686": "Sandra Hoang",
"sharkinsspatial": "Sean Harkins",
"sharonwanlu": "SharonLu",
"smohiudd": "Saadiq Mohiuddin",
"srmsoumya": "Soumya Ranjan Mohanty",
"sunu": "Tarashish Mishra",
"vgeorge": "Vitor George",
"vincentsarago": "Vincent Sarago",
"weiji14": "Wei Ji",
"wildintellect": "Alex I. Mandel",
"willemarcel": "Wille Marcel",
"wrynearson": "Will Rynearson",
"yellowcap": "Daniel Wiesmann",
"zacdezgeo": "Zac Deziel"
}
}
143 changes: 75 additions & 68 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,28 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, shrink-to-fit=0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

<title>Contributor Network</title>

<meta name="author" content="Development Seed">
<meta name="description" content="An interactive visualization of contributors and their connections to repositories">
<meta name="theme-color" content="#cf3f02">
<meta property="og:title" content="The Development Seed Contributor Network">
<meta property="og:url" content="https://developmentseed.org/contributor-network/">
<meta property="og:type" content="article">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://developmentseed.org/contributor-network/site-image.jpg">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="800">

<link rel="shortcut icon" type="image/png" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,700;1,400;1,700&family=Roboto+Condensed:wght@400;700&family=Fira+Code:wght@300..700&display=swap" rel="stylesheet">

<script src="assets/lib/d3.v7.js"></script>
<script src="assets/lib/d3-delaunay.js"></script>
<script src="assets/lib/d3-bboxCollide.min.js"></script>
Expand All @@ -15,11 +33,11 @@
<div id="chart-introduction">
<div id="chart-title">
<h1>The Contributor Network of</h1>
<h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
<h1><span id="title-repo-name" class="central-repo">...</span></h1>
<p id="last-commit-date"></p>
</div>
<div id="chart-intro-text">
<p>Explore the network of contributors and repositories.</p>
<p id="chart-description">Explore the network of contributors and repositories.</p>
<p>This visualization is based off of the excellent <a href="https://nbremer.github.io/ORCA/top-contributor-network/">Top Contributor Network</a> from <a href="https://github.com/nbremer">Nadieh Bremer</a>, and is licensed the same (MPL).</p>
</div>
</div>
Expand Down Expand Up @@ -51,84 +69,74 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
</div>

<script type="module">
// Load the main visualization module and handle all initialization
import { createContributorNetworkVisual } from './js/chart.js';

const REPOSITORY = "NASA-IMPACT/veda-config";
const contributor_padding = 20;
// ─── Load configuration ───────────────────────────────────
// In production, config.json is generated by the build command from config.toml.
// For local development, place a config.json in assets/data/ with at minimum:
// { "central_repository": "owner/repo", "contributor_padding": 20, "contributors": {} }
const configResponse = await fetch('assets/data/config.json');
if (!configResponse.ok) {
document.getElementById("chart-container").innerHTML =
'<p style="color: red; padding: 20px;">Error: Could not load config.json. Run the build command or create assets/data/config.json for local development.</p>';
throw new Error('Failed to load config.json');
}
const config = await configResponse.json();

// Master contributor list - simplified for testing
const mainContributors = new Set([
"Angela Camacho",
"Alice Rühl",
"Lane Goodman",
"Aimee Barciauskas",
"Anthony Boyd"
]);
const REPOSITORY = config.central_repository;
const contributor_padding = config.contributor_padding || 20;

// For compatibility with isValidContributor function
const masterContributors = {};
// Build contributor lookup maps from config
const masterContributors = config.contributors || {};
const displayNameToUsername = {};
mainContributors.forEach(name => {
masterContributors[name] = true;
displayNameToUsername[name] = name;
Object.entries(masterContributors).forEach(([username, displayName]) => {
displayNameToUsername[displayName] = username;
});

// Load data
const promises = [
d3.csv('assets/data/top_contributors.csv'),
d3.csv('assets/data/repositories.csv'),
d3.csv('assets/data/links.csv')
];
// ─── Populate page from config ────────────────────────────
const repoDisplayName = REPOSITORY.includes('/') ? REPOSITORY.split('/').pop() : REPOSITORY;
document.getElementById("title-repo-name").textContent = repoDisplayName;
if (config.title) document.title = config.title;
if (config.description) document.getElementById("chart-description").textContent = config.description;

let container = document.getElementById("chart-container");
let wrapper = document.getElementById("chart-wrapper");
let contributorNetworkVisual;
// ─── Set up sizing ────────────────────────────────────────
const container = document.getElementById("chart-container");
const wrapper = document.getElementById("chart-wrapper");

// Get chart dimensions based on available container space
function getChartDimensions() {
let wrapperRect = wrapper.getBoundingClientRect();
let availableWidth = wrapperRect.width - 40;

if (availableWidth < 400) {
availableWidth = Math.min(window.innerWidth - 40, 1400 - 40);
}

return Math.max(availableWidth, 600);
}

// Initialize visualization
function initVisualization() {
let size = getChartDimensions();
container.style.height = size + 'px';

contributorNetworkVisual = createContributorNetworkVisual(
container,
REPOSITORY,
contributor_padding,
masterContributors,
displayNameToUsername
)
.width(size)
.height(size)
.repository(REPOSITORY);

return size;
}

let size = initVisualization();
// ─── Initialize visualization ─────────────────────────────
let size = getChartDimensions();
container.style.height = size + 'px';

const contributorNetworkVisual = createContributorNetworkVisual(
container,
REPOSITORY,
contributor_padding,
masterContributors,
displayNameToUsername
)
.width(size)
.height(size)
.repository(REPOSITORY);

// ─── Load CSV data and render ─────────────────────────────
const promises = [
d3.csv('assets/data/top_contributors.csv'),
d3.csv('assets/data/repositories.csv'),
d3.csv('assets/data/links.csv')
];

document.fonts.ready.then(() => {
Promise.all(promises).then(values => {
let formatDigit = d3.format(",.2r");

// Deep copy data
let data_raw = [];
data_raw.push(JSON.parse(JSON.stringify(values[0])));
data_raw.push(JSON.parse(JSON.stringify(values[1])));
data_raw.push(JSON.parse(JSON.stringify(values[2])));

// Generate organization options
// Generate organization filter options
const uniqueOrgs = new Set();
values[1].forEach(repo => {
const owner = repo.repo.substring(0, repo.repo.indexOf("/"));
Expand All @@ -145,7 +153,7 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
});

let currentSelectedOrg = null;
orgSelect.addEventListener("change", function() {
orgSelect.addEventListener("change", function () {
const selectedOrg = this.value;
if (selectedOrg === "") {
if (currentSelectedOrg) {
Expand All @@ -170,14 +178,12 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
statsElement.textContent = `Filtered to: ${currentSelectedOrg}`;
}
}

updateFilterStats();

// Render the visualization
contributorNetworkVisual(values);

// Get dates
let central_repo = values[1].find(d => d.repo === REPOSITORY);
// Display most recent commit date
let formatDateLong = d3.utcFormat("%B %-e, %Y");
let links_to_central = values[2].filter(d => {
if (typeof d.target === 'object' && d.target && d.target.data) {
Expand All @@ -187,17 +193,18 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
});
let most_recent_commit = d3.max(links_to_central, d => +d.commit_sec_max);
if (most_recent_commit) {
// Convert Unix timestamp (seconds) to Date object (milliseconds)
let commitDate = new Date(most_recent_commit * 1000);
document.getElementById("last-commit-date").innerHTML = `Including commits up to ${formatDateLong(commitDate)}`;
document.getElementById("last-commit-date").innerHTML =
`Including commits up to ${formatDateLong(commitDate)}`;
}
}).catch(err => {
console.error('Error loading data:', err);
document.getElementById("chart-container").innerHTML = '<p style="color: red; padding: 20px;">Error loading data files. Make sure CSV files exist.</p>';
document.getElementById("chart-container").innerHTML =
'<p style="color: red; padding: 20px;">Error loading data files. Make sure CSV files exist in assets/data/.</p>';
});
});

// Handle resize
// ─── Handle resize ────────────────────────────────────────
let resizeTimer = null;
window.addEventListener("resize", function () {
clearTimeout(resizeTimer);
Expand Down
Loading