Skip to content

Events: make the homepage stats dynamic - #2175

Open
wputasic wants to merge 2 commits into
WordPress:productionfrom
wputasic:events/1180-dynamic-stats
Open

wputasic wants to merge 2 commits into
WordPress:productionfrom
wputasic:events/1180-dynamic-stats

Conversation

@wputasic

@wputasic wputasic commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1180. Supersedes #2173 (closed), since the pattern no longer carries numbers at all.

The homepage stats were hardcoded for the 2023 launch, bumped by hand in #1359, and set by hand again
in the site editor on 2026-09-30. They now come from the data and refresh themselves.

What the three figures are

The same three the live page shows today, defined the way @karenalma's 2026-09-30 figures were:

On the page Source
56,000+ WordPress events since 2006 the chapter groups' past events (past_event_count from the Meetup API, the same client WordCamp Reports uses) plus the WordCamps on central (wcpt-scheduled and wcpt-closed posts)
711 local groups in 100 countries the number of chapter groups, and the distinct countries among them
500,000+ meetup members the groups' member_count, summed

That follows the 2023 thread's call for all-time totals (@StevenDufresne, @thetinyl). It differs from
the issue's original sketch in one way: the thread had events and countries coming from wporg_events,
but that table only holds what the importer has synced, not everything since 2006, and the live copy
has since been redefined around the groups. Events and members are rounded down to a clean figure
with a plus (nearest 1,000 and 10,000), since the exact numbers change by the hour; groups and
countries are shown exactly.

How it works

  • inc/stats.php computes the figures and stores them in an option. An hourly cron
    (wporg_events_refresh_stats) fetches the groups through Meetup_Client::get_groups() and counts
    the WordCamps on central. Visitors never wait on the Meetup API: the page reads the stored figures
    only. If the API fails or returns nothing, the stored figures stay. Until the first refresh has run,
    the page shows the copy it launched with (the 2026-09-30 figures), so deploying this changes nothing
    visible.
  • A small server-rendered block, wporg/events-stat, with one attribute, stat (events, groups or
    members). It renders one sentence as a <p> and keeps the pattern's colour and font-size settings
    through block supports. In the editor it uses ServerSideRender, with the stat picked in the
    inspector.
  • patterns/front-cover.php uses three of those blocks instead of three hardcoded paragraphs.

Screenshots

None needed for the front end: with the stored figures, the page renders exactly what it shows today.
The block appears in the editor as the rendered sentence with a "Stat" panel in the inspector.

How to test the changes in this Pull Request:

  1. npm run build in the theme. Load the homepage on a site with the theme's front page template:
    the three stats read as they do on production today (the fallback), since nothing is stored yet.
  2. Run the cron hook, e.g. wp cron event run wporg_events_refresh_stats --url=events.wordpress.test,
    with Meetup API credentials configured. Reload: the figures are the computed ones.
  3. In the site editor, open the front page template and the Events Stat blocks: each shows its
    sentence, and the inspector's "Stat" panel switches between the three.
  4. Break the Meetup credentials and run the hook again: the figures from step 2 stay.

Verification

Rebased onto upstream/production at 5683a89f. It no longer includes #2171: the tests now sit in
the Events 2023 Theme suite that #1561 added, and load inc/stats.php and the block's index.php
themselves, the way that suite's other tests do.

Gate Result
PHPUnit, --testsuite "Events 2023 Theme", PHP 8.4.25 and 8.5.11 with log_errors=1 OK, 38 tests, 67 assertions (8 new), exit 0
Full suite, branch vs production, same container, PHP 8.4 2,433 tests vs 2,425 (+8), identical failure set and identical warning lines
phpcs, CI's phpcs-branch.php against production 0 errors
wp-scripts lint-js on the new block clean
npm run build compiles; build/events-stat/ has block.json, index.js, index.asset.php

Test_Stats (8): compute_stats() sums the groups' past events and the WordCamps, counts groups,
counts each country once whatever the case, and sums members; format_stat() rounds large figures
down with a plus, shows small ones exactly, and returns nothing for an unknown stat; get_stats()
falls back to the launch copy with no data; a refresh stores what it computed; a failed or empty fetch
keeps the last figures; the refresh is scheduled hourly, once; the block renders the sentence as a
paragraph and nothing for an unknown stat. The Meetup call and the central query sit behind two
filters (wporg_events_stats_groups, wporg_events_stats_wordcamp_count) that the tests use to
supply data; nothing in the tests touches the API.

Notes for reviewers

  • Option, not transient. The thread suggested a 24-hour transient primed hourly. An option without
    autoload does the same job without an expiry gap: if cron is late, the last figures stay up rather
    than falling back.
  • @wordpress/i18n is added to the theme's devDependencies (6.21.0, the version the other
    packages pin), for the editor labels. The root lockfile change is three lines, regenerated with npm
    10 to match CI's Node 20.
  • Block registration is skipped when build/ is absent, so a checkout that hasn't run
    npm run build doesn't emit a _doing_it_wrong; production builds on deploy.
  • The live front page template is a saved copy in the database (the stats were edited in the site
    editor), so it still has the three static paragraphs. To get the dynamic figures on
    events.wordpress.org, replace those three paragraphs with Events Stat blocks in the site editor once,
    or reset the template to the theme default.

@wputasic
wputasic force-pushed the events/1180-dynamic-stats branch 2 times, most recently from 788ef83 to 0b9c5ec Compare October 9, 2026 19:58
@wputasic
wputasic force-pushed the events/1180-dynamic-stats branch 2 times, most recently from 59f12a5 to 7db0121 Compare October 14, 2026 14:30
Fixes WordPress#1180.

The three figures on the front cover were hardcoded for the launch and set
by hand since. They now come from the chapter program's Meetup groups,
through the client WordCamp Reports uses, plus the WordCamps on central:
past events + WordCamps since 2006, groups and the countries they're in,
and members. Events and members round down to a clean figure with a plus.

An hourly cron computes and stores them in an option; the page only ever
reads the stored figures, so the Meetup API is never on a visitor's request,
a failed fetch keeps the last figures, and until the first refresh the page
shows the copy it launched with.

A small server-rendered block, wporg/events-stat, renders one figure as a
paragraph and keeps the pattern's colour and font-size settings; the front
cover pattern uses three of them. @wordpress/i18n joins the theme's
devDependencies for the editor labels.
@wputasic
wputasic force-pushed the events/1180-dynamic-stats branch from 7db0121 to 550b3ee Compare October 20, 2026 14:27
@wputasic
wputasic requested a review from bor0 October 20, 2026 14:27

@bor0 bor0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The refresh cron fatals on every run, so the figures never get stored and the homepage stays on the fallback copy.

count_wordcamps() passes 'fields' => 'ids' to get_wordcamps(), which then sets ->meta on each result: Attempt to assign property "meta" on int in 4-helpers-wcpt.php:42. Reproduced with wp cron event run wporg_events_refresh_stats --url=events.wordpress.test. The tests miss it because they always short-circuit wporg_events_stats_wordcamp_count.

Fix: switch to central and count with get_posts() (post_type => 'wordcamp', fields => 'ids', numberposts => -1) or a direct count query. Please don't just drop fields, that loads every camp's meta hourly. Add a test that runs the real count.

With that patched locally, everything else worked: the cron stores the figures, the cards render them, the fallback shows before the first run, and the block works in the editor with the Stat panel.

Minor: edit.js uses the wordcamporg text domain, PHP and block.json use wporg.

`get_wordcamps()` sets `->meta` on every result, so with `'fields' => 'ids'`
the hourly refresh fataled and the figures were never stored. Count the
scheduled and closed camps on central with `get_posts()` IDs instead, so no
meta is loaded, and test the real count through the cron hook.

Also use the `wordcamporg` text domain in the stats PHP and block.json to
match edit.js, which the repo's i18n lint rule pins to `wordcamporg`.
@wputasic

Copy link
Copy Markdown
Contributor Author

Thanks, good catch. get_wordcamps() can't take fields => ids at all, since it sets ->meta on every result.

Fixed in 987677f:

  • count_wordcamps() now switches to central and counts with get_posts() (post_type => wordcamp, the scheduled and closed statuses, fields => ids, numberposts => -1). No meta gets loaded.
  • New test test_refresh_counts_the_wordcamps_on_central. It injects only the Meetup groups, creates real wordcamp posts on central (two scheduled, one closed, plus a needs-vetting, a cancelled, and a post in a scheduled status that shouldn't count), then fires the cron hook. Against the old code it fails with your error (Attempt to assign property "meta" on int).
  • Text domain: switched stats.php and block.json to wordcamporg. I kept edit.js as it is, since the repo's @wordpress/i18n-text-domain lint rule only allows wordcamporg. inc/events-query.php in this theme already uses it too.

Events 2023 suite: 48/48 on PHP 8.4. phpcs is clean on the changed files.

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.

Events: Make stats dynamic

2 participants