This repo houses code to help conduct voting for the Pteridophyte Phylogeny Group (PPG) on taxonomic proposals for ferns and lycophytes.
Creating and tallying ballots is automated with {targets}. Each month, run:
targets::tar_make()This will, in order:
- Read
ballot_state.yamlto figure out the next ballot to create and the next one to tally (always "last completed + 1 month", never today's date, so a busy or skipped month can never silently disappear from voting). - Fetch open GitHub issues labeled "taxonomic proposal" for the new ballot's submission month, create a Google Form for them via the Forms API, and draft (but not send) the announcement email.
- Fetch responses for the ballot being tallied directly from the Forms API, check them against the PPG mailing list, tally votes, write
results/ballot-<n>_results*.csv, and draft (but not send) the results email. - Update
ballot_state.yamlto record what was just done.
If a submission month has no eligible proposals, no form is created and no ballot number is spent on it — the month is just marked checked, and the next run picks up the following month with the same ballot number it would have used anyway.
Manual steps that remain:
- Review each drafted email in the
pteridogroup.no.reply@gmail.comGmail drafts folder and send it yourself. - Manually set a closing timer on each new form (Settings > Responses in the Forms UI) — the Forms API has no way to schedule this.
tar_make()prints the deadline to use.
One-time setup:
- Enable the Forms API in the Google Cloud project behind
.secrets/client_secret*.json(in addition to Gmail/Sheets, already enabled). - Before running
tar_make()for the first time, authenticate in your live R console (not insidetar_make()itself, since it runs targets in an isolated subprocess where a browser consent flow can't happen):Complete the browser consent forsource("R/google_forms.R") forms_token()pteridogroup.no.reply@gmail.com. The token is cached in.secrets, so subsequenttar_make()runs can reuse it silently.
R/digest.R sends a weekly summary of open taxonomic proposals to the PPG
mailing list. It runs automatically via GitHub Actions
(.github/workflows/digest.yml) every Monday at 00:00 UTC, and can also
be triggered manually from the Actions tab or with
gh workflow run digest.yml.
One-time / as-needed setup:
The workflow authenticates as pteridogroup.no.reply@gmail.com using the
same .secrets/ OAuth cache as the monthly ballot pipeline, reconstructed
at runtime from GitHub Actions secrets:
GMAIL_CLIENT_SECRET_JSON— base64 of.secrets/client_secret*.json.GMAIL_OAUTH_TOKEN_1..GMAIL_OAUTH_TOKEN_10— the cached OAuth token, base64-encoded and split into chunks because it's too large (~260-350 KB) for a single GitHub secret (~48 KB limit).
If the cached token in .secrets/ needs to be (re)created — e.g. it was
revoked, or this is a fresh checkout — authenticate locally first:
library(gmailr)
options(gargle_oauth_cache = ".secrets")
gm_auth_configure(path = list.files(
".secrets", pattern = "client_secret.*json", full.names = TRUE
))
gm_auth("pteridogroup.no.reply@gmail.com", cache = ".secrets")Then push the result to GitHub with:
./scripts/set_gmail_oauth_secret.shThis splits the token into the numbered secrets above and clears any
unused slots left over from a previous, larger token. Run both steps on
your host machine, not inside the dev container — the container has no
browser for the OAuth consent step, and doesn't have gh installed.
All code is available under the MIT license