Skip to content

Track package and paper metrics weekly - #137

Open
sbfnk-bot wants to merge 12 commits into
mainfrom
stats-tracking
Open

Track package and paper metrics weekly#137
sbfnk-bot wants to merge 12 commits into
mainfrom
stats-tracking

Conversation

@sbfnk-bot

@sbfnk-bot sbfnk-bot commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Adds a weekly job that records download counts, GitHub stars and citation counts, and shows the download figure on the software page.

Issue #85 asked for this to support grant applications. The value is the history, which only accumulates from the day the job starts, so this collects the numbers now and keeps the presentation minimal.

What it collects

_data/package-stats.csv gets one row per package per week: stars, forks, open issues, whether the package is on CRAN, and CRAN downloads for the last month. The list of packages comes from the r-universe registry plus _data/software-extras.yml, so it tracks exactly what the software page shows and needs no list of its own.

_data/paper-citations.csv gets one row per paper per week, with citation counts from OpenAlex. The DOIs come from _data/papers.bib, which the publications workflow already maintains.

Rerunning on the same day corrects the rows rather than duplicating them.

What it shows

One addition to the software page: the download figure joins the existing metadata line, reading R · Updated Aug 2026 · 598 CRAN downloads/month. Naming CRAN matters because only the ten packages on CRAN have a figure, and without it a blank could be read as "not used" rather than "not on CRAN".

Nothing else is rendered. In particular there is no citation count anywhere on the site.

Why citations are collected but not shown

Citation counts belong in a grant application, not on a public page. Beside each paper in a list sorted by year they become a column the eye reads down and ranks, including work by different people in the group, and recent papers legitimately show zero: the ten 2025 papers have 19 citations between them, which is normal and looks poor in a table.

The numbers are also concentrated. Of 5,240 citations across 39 papers, 2,830 belong to a single 2020 paper, and the median paper has 15. OpenAlex indexes fewer sources than Google Scholar, so these are a lower bound.

Packages carry no citation column at all. Of sixteen, three declare a citable reference, and one of those is a CRAN package DOI that is almost never cited, so the column would have been blank or misleading nearly everywhere.

Stars are collected but not displayed for the same reason. They measure visibility rather than use, and the two diverge: scoringutils has 934 downloads and 64 stars, EpiNow2 has 598 and 140, contactsurveys has 263 and 2.

Notes

The job commits the CSVs directly to main rather than opening a weekly pull request, which needs permissions: contents: write. That commit triggers a site build, so the download figures on the software page refresh themselves.

If _data/package-stats.csv is absent the page still renders, but every package then falls to the "On r-universe" branch rather than showing a download figure, so the file is expected to be present once the job has run.

Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
@sbfnk
sbfnk marked this pull request as ready for review August 26, 2026 09:03
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Comment thread _automation/get_stats.R Outdated
Comment thread software.qmd Outdated
echo "No change this week"
else
git commit -m "Weekly package and paper metrics"
git push

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

actions/checkout persists github.token as the git credential, and GitHub does not create workflow runs from events triggered by GITHUB_TOKEN. build-and-deploy.yaml runs on push to main, so this commit will not rebuild the site.

The effect is that the software page keeps serving whatever figures were baked in at the last human push or the monthly cron, so 598 CRAN downloads/month can be weeks behind the CSV while looking current — which is the one claim the PR body makes for committing straight to main.

Flagging rather than fixing: this file is on the sensitive-paths denylist, so the change is for a human to make. Two options — push with a PAT or deploy key, or dispatch the build explicitly at the end of the job. Worth noting software.qmd already fetches the r-universe packages API at render time and that payload carries _downloads$count, so the page could read the figure directly and leave the CSV purely as history.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correct, and not fixing it here. .github/** is on the sensitive-paths denylist for review-driven changes, so a change to CI permissions or credentials is for a human to make deliberately rather than something applied on the strength of a review finding.

Leaving this thread open so it is waiting when you look. The claim in the PR body that the commit rebuilds the site is wrong as written, and either the workflow needs a PAT or deploy key, or it needs to dispatch the build explicitly.

Worth weighing the third option raised: software.qmd already fetches the r-universe packages API at render time and that payload carries _downloads$count, so the page could read the figure directly at build and the CSV would be purely history. That removes the dependency entirely rather than working around it, but it is a design change rather than a fix, so it is your call.

sbfnk-bot and others added 2 commits August 26, 2026 10:43
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Comment thread _automation/get_stats.R
Comment thread software.qmd Outdated
sbfnk-bot and others added 2 commits August 26, 2026 10:54
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Comment thread _automation/get_stats.R Outdated
Comment thread software.qmd Outdated
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Comment thread software.qmd Outdated
# its repository: epimixr lives at sbfnk/epimixr but is built in the
# epiforecasts universe. Anything listed via software-extras.yml is by
# definition not in that registry, so it goes to its owner's instead.
universe <- if (isTRUE(e$from_extras)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The from_extras fix does not fix the case it was made for. Deriving the owner from https://github.com/epiforecasts/newpkg yields epiforecasts — identical to the else branch it was added to avoid.

So an extras entry owned by epiforecasts still emits https://epiforecasts.r-universe.dev/newpkg, confirmed 404. The fix only helps extras owned by a third party. All 16 current packages resolve, so this is latent, but the earlier reply on this diff asserting it was closed is wrong.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c159a03, by dropping the guess rather than improving it. A registry package is served by the epiforecasts universe whatever repo it lives in, so that link is emitted and epimixr still resolves. For an extras entry we cannot know the registry — the owner is not necessarily a universe — so no link is emitted at all. Those entries already carry a "Part of" link to their own project, which is the accurate pointer.

Net effect: five links, all epiforecasts, all verified 200. epinowcast now shows its CRAN figure and its "Part of epinowcast" link, and no r-universe guess. No input can produce a dead link from this branch now.

Comment thread _automation/get_stats.R Outdated
list(
stars = as.integer(info$stargazers_count %||% NA),
forks = as.integer(info$forks_count %||% NA),
issues = as.integer(info$open_issues_count %||% NA)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

open_issues_count is GitHub's count of open issues plus open pull requests, so the open_issues column overstates issues by the number of open PRs.

Measured just now: scoringutils records 119 against 105 issues + 14 PRs; epinowcast 120 against 111 + 9; EpiNow2 66 against 64 + 2. Exact sums in every case.

Because the point of this file is history that cannot be reconstructed, every week merged before this is fixed is permanently wrong under a column name that says otherwise. Either rename the column to what it counts, or subtract the PR count.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c159a03. open_issues now comes from the search endpoint with is:issue, which is the only one that separates issues from pull requests. Verified against the three cases raised: scoringutils 105, epinowcast 111, EpiNow2 64 — matching the issue counts rather than the inflated 119/120/66.

Costs one extra API call per package per week, and returns NA on failure like every other fetch here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

My fix for this was wrong and made three packages worse, not better. /search/issues does not follow repository renames, and three registry URLs point at repositories that have since moved — sbfnk/epimixr, sbfnk/RBi, sbfnk/rbi.helpers all now live under epiforecasts. Search returned 422 for each, so open_issues became NA, and because newest_present() never lets NA overwrite, the inflated pre-fix value survived in the committed CSV. I had verified only the three non-renamed repos named in the original finding.

Fixed in c437f65 without search: open_issues_count minus the count of open pull requests, both from endpoints that do follow renames. That also removes the CI risk raised — search results are scoped by installation, so it could have returned nothing under secrets.GITHUB_TOKEN while working locally.

All 16 packages now report a value, none NA. epimixr reads 3, which is its true issue count against the 5 the CSV was carrying.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

All three addressed in a90d8ac.

Registry fetches aborting the run. Both are now wrapped, and the two halves are reordered so citations are collected and written before the package registries are touched. A registry outage now logs and returns zero rows instead of halting, so the script exits 0, the commit step runs, and the paper series is unaffected. Verified by simulating an unreachable registry: 0 rows, no stop. The comment claiming every fetch degrades to NA is now true rather than aspirational.

The naming fix applied to the wrong half. Correct, and worth stating plainly: registry entries never reach crandb, so naming them was the half that did not matter. Extras now take their name from their own DESCRIPTION, which is what CRAN knows them by — sbfnk/RBi declares Package: rbi. Falls back to the repo name if DESCRIPTION cannot be read.

The deleted 2026-08-25 rows. That was deliberate, not a side effect, though I should have said so here rather than only in passing: those rows were collected by code with three confirmed bugs, including the inflated issue counts this PR exists to remove, and newest_present() cannot repair them because a wrong-but-present value beats a correct one on a later date. Committing them would have baked known-wrong numbers into a file whose stated value is that it cannot be reconstructed. The paper series kept both days because none of the bugs touched it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

All three fixed in 209e16d, by removing the r-universe download path rather than guarding it.

Stale figures. Confirmed: r-universe reported 934/903/161/263 for scoringutils, socialmixr, ringbp and contactsurveys while cranlogs returned 727/928/177/317 for the current window. Stale in both directions, so it was a different window rather than a scaling. cranlogs is now the only source and all four match it exactly. The headline figure on the page was overstating scoringutils by 28%.

The guard silently switching source. That disappears with the dual source: there is one path now, so an outage produces NA rather than a different number in the same column.

The key mismatch. The CSV now records the canonical repository name from the GitHub API and the page keys on the same thing, so the two agree by construction rather than by both happening to lowercase to the same string. This also corrects the three stale registry slugs: rbi, rbi.helpers and epimixr now record epiforecasts/... rather than sbfnk/....

Net: 10 CRAN figures and 5 r-universe links on the page, unchanged in shape, correct in value. universe_downloads() is gone, which is about 25 lines less to be wrong.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Both fixed in 5ea6dd2.

One bad week emptying the page. Real, and the live evidence in the finding makes the point — a distspec timeout would have swapped its figure for the r-universe link. The page now takes each repository's most recent non-NA figure rather than filtering to the newest date, bounded to 35 days so a long outage shows nothing rather than something months stale. Verified by injecting an all-NA week: the page still renders all ten figures from the previous run, where before it rendered none.

A GitHub failure writing a stale slug as the join key. Correct, and it is the same shape as the on_cran bug — a failure encoded as a value on the one column the whole re-keying depends on, and worse because being present it beats the canonical name on a rerun. The error branch now returns NA_character_. Verified: a failed row followed by a good one now repairs to epiforecasts/rbi with stars intact.

On the wider point that the two sides only agree when both GitHub calls succeed — that is right, and my "agree by construction" was too strong. What holds now is weaker and worth stating accurately: when either side cannot resolve the canonical name, that package loses its figure for the week and recovers on the next successful run, rather than recording a key that silently never matches.

sbfnk-bot and others added 5 commits August 26, 2026 12:42
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
…age name

Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
…right name

Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
…sitory

Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
Co-authored-by: sbfnk <sebastian.funk@lshtm.ac.uk>
echo "No change this week"
else
git commit -m "Weekly package and paper metrics"
git push

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A second, more serious problem on this line, separate from the rebuild one already open above: the push will be rejected outright, so the job never records anything.

repos/.../rules/branches/main returns ["deletion","non_fast_forward","pull_request"] — an active ruleset requiring a pull request on the default branch. GITHUB_TOKEN bypasses that only if listed as a bypass actor, and the evidence says it is not: every github-actions[bot] commit on main arrived as a PR merge (#131, #124, #103, #96, #92), never a direct push; update-publications.yaml, the repo's other weekly job writing to _data/, routes through peter-evans/create-pull-request; and release-reminders.yaml uses an App token rather than GITHUB_TOKEN.

Failure scenario: the PR merges, Monday's job collects fine, git commit succeeds, git push is rejected, the run goes red and nothing is written. That repeats weekly with no signal beyond a failed run. Worse, it interacts with the 35-day window added in 5ea6dd2: once the single committed date falls outside it, the page drops all ten CRAN figures and renders "On r-universe" links instead, with nothing to say why. The feature quietly un-does itself about five weeks after merge.

Not fixing it — .github/** stays a human's call. Both options that work are already patterns in this repo: create-pull-request as in update-publications.yaml (fixes the push, not the rebuild), or an App token as in release-reminders.yaml (fixes both, since a push under a non-Actions identity also triggers build-and-deploy).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants