Skip to content

Commit ac3be9d

Browse files
committed
Add automated community metrics to community page
1 parent 027a1d5 commit ac3be9d

5 files changed

Lines changed: 391 additions & 0 deletions

File tree

.github/workflows/update-discourse-data.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
run: |
3333
python tools/fetch-news.py
3434
python tools/fetch-faq.py
35+
python tools/fetch-community-metrics.py
3536
3637
- name: Configure Git author
3738
run: |
@@ -42,6 +43,7 @@ jobs:
4243
run: |
4344
git add assets/data/news.json
4445
git add assets/data/faq.json
46+
git add assets/data/community-metrics.json
4547
git commit -m "Update Discourse data data [skip ci]" || echo "No changes to commit"
4648
4749
- name: Push commit

assets/data/community-metrics.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"generated_at": "2026-02-22T15:31:13.791892+00:00",
3+
"github": {
4+
"repositories": [
5+
{
6+
"id": "core",
7+
"label": "preCICE core",
8+
"full_name": "precice/precice",
9+
"url": "https://github.com/precice/precice",
10+
"description": "A coupling library and ecosystem for partitioned multi-physics and multi-scale simulations, including surface and volume coupling.",
11+
"stars": 887,
12+
"forks": 224,
13+
"open_issues": 230,
14+
"watchers": 35,
15+
"contributors": 66,
16+
"latest_commit_at": "2026-02-18T13:23:28Z",
17+
"latest_release": {
18+
"name": "v3.3.1",
19+
"tag_name": "v3.3.1",
20+
"published_at": "2026-01-14T15:28:58Z",
21+
"url": "https://github.com/precice/precice/releases/tag/v3.3.1",
22+
"assets_count": 8,
23+
"downloads_count": 345
24+
}
25+
},
26+
{
27+
"id": "tutorials",
28+
"label": "Tutorials",
29+
"full_name": "precice/tutorials",
30+
"url": "https://github.com/precice/tutorials",
31+
"description": "Various tutorial cases for the coupling library preCICE with real solvers. These files are meant to be rendered on precice.org, so don't look at the README files here.",
32+
"stars": 131,
33+
"forks": 138,
34+
"open_issues": 110,
35+
"watchers": 10,
36+
"contributors": 48,
37+
"latest_commit_at": "2026-02-20T20:01:02Z",
38+
"latest_release": {
39+
"name": "v202404.0 - Now with preCICE v3",
40+
"tag_name": "v202404.0",
41+
"published_at": "2024-04-16T20:35:04Z",
42+
"url": "https://github.com/precice/tutorials/releases/tag/v202404.0",
43+
"assets_count": 0,
44+
"downloads_count": 0
45+
}
46+
}
47+
]
48+
},
49+
"discourse": {
50+
"url": "https://precice.discourse.group",
51+
"title": "preCICE Forum on Discourse",
52+
"site_creation_date": "2019-09-16T04:29:48.122Z",
53+
"users_count": 676,
54+
"topics_count": 1163,
55+
"posts_count": 8866,
56+
"active_users_30_days": 62,
57+
"topics_30_days": 18,
58+
"posts_30_days": 83
59+
}
60+
}

content/community/community.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ Meet the community online, ask questions, and help others at the [preCICE forum
2121

2222
Are you looking for something else? Maybe one of the other [community channels](community-channels.html) is for you.
2323

24+
## Community metrics
25+
26+
The following metrics are updated automatically and provide a quick snapshot of community activity.
27+
28+
<div id="community-metrics"></div>
29+
<p id="community-metrics-status">Loading automatically generated metrics...</p>
30+
31+
<script src="js/community-metrics.js"></script>
32+
2433
## Support preCICE
2534

2635
There are different ways how to support preCICE and get priority support from the preCICE developers in return. [Find out which options](community-support-precice.html).

js/community-metrics.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
console.log("community-metrics.js loaded");
2+
3+
(function () {
4+
function formatNumber(value) {
5+
if (typeof value !== "number") {
6+
return "n/a";
7+
}
8+
return new Intl.NumberFormat("en-US").format(value);
9+
}
10+
11+
function formatDate(value) {
12+
if (!value) {
13+
return "n/a";
14+
}
15+
16+
var parsed = new Date(value);
17+
if (Number.isNaN(parsed.getTime())) {
18+
return "n/a";
19+
}
20+
21+
return parsed.toLocaleDateString("en-US", {
22+
year: "numeric",
23+
month: "short",
24+
day: "numeric",
25+
});
26+
}
27+
28+
function createItem(label, value) {
29+
return "<li><strong>" + label + ":</strong> " + value + "</li>";
30+
}
31+
32+
function createCardColumn(title, description, items, linkUrl, linkLabel) {
33+
var column = document.createElement("div");
34+
column.className = "col-md-4 col-sm-6 col-flex";
35+
36+
var card = document.createElement("div");
37+
card.className = "panel panel-primary panel-precice full-height";
38+
39+
var list = "<ul class=\"list-unstyled\">" + items.join("") + "</ul>";
40+
var descriptionHtml = description ? "<p>" + description + "</p>" : "";
41+
var linkHtml = "";
42+
if (linkUrl && linkLabel) {
43+
linkHtml =
44+
"<p class=\"no-margin\"><a href=\"" +
45+
linkUrl +
46+
"\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"no-external-marker\">" +
47+
linkLabel +
48+
" &nbsp;<i class=\"fas fa-chevron-right\"></i></a></p>";
49+
}
50+
51+
card.innerHTML =
52+
"<div class=\"panel-heading-precice text-left\"><strong>" +
53+
title +
54+
"</strong></div>" +
55+
"<div class=\"panel-body\">" +
56+
descriptionHtml +
57+
list +
58+
linkHtml +
59+
"</div>";
60+
61+
column.appendChild(card);
62+
return column;
63+
}
64+
65+
function createRepositoryCard(repo) {
66+
var latestRelease = repo.latest_release;
67+
var latestReleaseValue = "n/a";
68+
if (latestRelease && latestRelease.url) {
69+
latestReleaseValue =
70+
"<a href=\"" +
71+
latestRelease.url +
72+
"\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"no-external-marker\">" +
73+
(latestRelease.name || latestRelease.tag_name || "Release") +
74+
"</a> (" +
75+
formatDate(latestRelease.published_at) +
76+
")";
77+
}
78+
79+
var releaseDownloads = latestRelease ? formatNumber(latestRelease.downloads_count) : "n/a";
80+
81+
return createCardColumn(
82+
repo.label,
83+
repo.description || "",
84+
[
85+
createItem("Stars", formatNumber(repo.stars)),
86+
createItem("Contributors", formatNumber(repo.contributors)),
87+
createItem("Forks", formatNumber(repo.forks)),
88+
createItem("Open issues", formatNumber(repo.open_issues)),
89+
createItem("Latest commit", formatDate(repo.latest_commit_at)),
90+
createItem("Latest release", latestReleaseValue),
91+
createItem("Release downloads", releaseDownloads),
92+
],
93+
repo.url,
94+
"Open repository"
95+
);
96+
}
97+
98+
function createDiscourseCard(discourse) {
99+
return createCardColumn(
100+
"Discourse forum",
101+
"Community activity snapshot from the preCICE forum.",
102+
[
103+
createItem("Users", formatNumber(discourse.users_count)),
104+
createItem("Topics", formatNumber(discourse.topics_count)),
105+
createItem("Posts", formatNumber(discourse.posts_count)),
106+
createItem("Active users (30d)", formatNumber(discourse.active_users_30_days)),
107+
createItem("Topics (30d)", formatNumber(discourse.topics_30_days)),
108+
createItem("Posts (30d)", formatNumber(discourse.posts_30_days)),
109+
],
110+
discourse.url,
111+
"Open forum"
112+
);
113+
}
114+
115+
function showError(status, message) {
116+
if (status) {
117+
status.textContent = message;
118+
}
119+
}
120+
121+
document.addEventListener("DOMContentLoaded", async function () {
122+
var container = document.getElementById("community-metrics");
123+
if (!container) {
124+
return;
125+
}
126+
127+
var status = document.getElementById("community-metrics-status");
128+
129+
try {
130+
var response = await fetch("/assets/data/community-metrics.json");
131+
if (!response.ok) {
132+
throw new Error("HTTP " + response.status);
133+
}
134+
135+
var metrics = await response.json();
136+
var repositories = (metrics.github && metrics.github.repositories) || [];
137+
var discourse = metrics.discourse;
138+
139+
if (!repositories.length || !discourse) {
140+
throw new Error("Missing metrics data");
141+
}
142+
143+
var row = document.createElement("div");
144+
row.className = "row equal";
145+
146+
for (var i = 0; i < repositories.length; i += 1) {
147+
row.appendChild(createRepositoryCard(repositories[i]));
148+
}
149+
row.appendChild(createDiscourseCard(discourse));
150+
151+
container.innerHTML = "";
152+
container.appendChild(row);
153+
154+
if (status) {
155+
status.textContent =
156+
"Automatically generated metrics. Last updated " + formatDate(metrics.generated_at) + ".";
157+
}
158+
} catch (error) {
159+
console.error("Could not load community metrics:", error);
160+
showError(
161+
status,
162+
"Could not load the metrics right now. You can still browse all details on GitHub and in the forum."
163+
);
164+
}
165+
});
166+
})();

0 commit comments

Comments
 (0)