# weekly-activity Aggregate weekly activity across development platforms (Launchpad, GitHub, GitLab, Debian BTS) into a single text report. ## Usage ```bash # Interactive setup: pick sources, store identities/tokens once uv run weekly-activity config # Aggregated report across every configured source uv run weekly-activity report # bare `weekly-activity` does the same ``` ### Single sources (ad-hoc, no config needed) ```bash # Launchpad (uses system keyring for OAuth; --anonymous for public data only) uv run weekly-activity launchpad uv run weekly-activity launchpad --anonymous # GitHub (uses GITHUB_TOKEN env var or `gh auth token`; anonymous works but rate-limited) uv run weekly-activity github uv run weekly-activity github --token ghp_xxx # GitLab (gitlab.com by default; any self-hosted instance via --url, e.g. Debian Salsa; # uses GITLAB_TOKEN / SALSA_TOKEN env vars, anonymous works but rate-limited) uv run weekly-activity gitlab uv run weekly-activity gitlab --url https://salsa.debian.org uv run weekly-activity gitlab --url https://salsa.debian.org --token glpat-xxx # Debian BTS (bugs.debian.org; identify by the email used on bugs) uv run weekly-activity bts # Custom date range (ISO dates, UTC) uv run weekly-activity launchpad --since 2025-08-01 --until 2025-08-15 uv run weekly-activity github --since 2025-08-01 --until 2025-08-15 uv run weekly-activity gitlab --since 2025-08-01 --until 2025-08-15 uv run weekly-activity bts --since 2025-08-01 --until 2025-08-15 ``` ### GitLab instances Any GitLab instance works via `--url`. Token discovery order: 1. `--token` flag 2. env var named by `--token-env` 3. `_TOKEN` derived from the instance host (e.g. `SALSA_TOKEN` for `salsa.debian.org`) 4. `GITLAB_TOKEN` ## What it reports | Section | Launchpad | GitHub | GitLab | Debian BTS | |---|---|---|---|---| | Merge proposals / PRs authored | `getMergeProposals` (created in range) | `/search/issues` (`author:` + `type:pr`) | `/merge_requests` (`author_id`, created range) | — | | Review requests received | `getRequestedReviews` (filtered by `date_review_requested`) | `/search/issues` (`reviewed-by:` + `type:pr`) | — | — | | Reviews performed | `getRequestedReviews` (filtered by `date_reviewed`) | (same query) | events (`action=approved`) | — | | Issues / Bugs | `searchTasks` (reporter, commenter, assignee) | `/search/issues` (author, commenter, assignee) | `/issues` (`author_id`, `assignee_id`) | `get_bugs` (`submitter`) + `get_status` (created in range) | | Bugs owned, updated | — | — | — | `get_bugs` (`owner`) + `get_status` (`log_modified`) | | Repositories modified | `git_repositories.getRepositories` (`modified_since_date`) | `/users/{user}/repos` (filtered by `pushed_at`) | — (covered by push events) | — | | Commits pushed | — (not in Launchpad REST API) | `/repos/{owner}/{repo}/commits` (per active repo) | events (`action=pushed`) | — | ### Default time window Rolling 7 days ending today (inclusive). Override with `--since` / `--until` (ISO dates, UTC). ## Configuration `weekly-activity config` opens a small menu-driven editor: it lists your current sources (secrets masked), then lets you **Add a new source** (choose the kind, repeat as often as you like), **Remove a source**, or **Exit** (writes the file only when something changed). Several accounts of the same provider can live side by side. Tokens are stored as provided (plaintext), but the file is created with owner-only permissions (`0600`) and secrets are never echoed back. Use `--config ` (also accepted by `report`) to keep configs elsewhere. Default location: `${XDG_CONFIG_HOME:-~/.config}/weekly-activity.toml`. ```toml version = 2 [[launchpad.accounts]] username = "jane" mode = "credentials" # "anonymous" (public data only) | "credentials" # credentials_file = "~/lp-creds.json" # omit -> system keyring [[github.accounts]] name = "personal" # optional display label username = "octocat" token = "" # empty -> $token_env / GITHUB_TOKEN / `gh auth token` [[github.accounts]] name = "work" username = "acme-jane" token_env = "WORK_GH_TOKEN" [[gitlab.accounts]] name = "salsa" url = "https://salsa.debian.org" username = "jane" token_env = "SALSA_TOKEN" [[bts.accounts]] email = "jane@example.org" ``` An absent section disables that source; an empty one is not written. When a non-anonymous Launchpad account is added without a credentials file, the wizard runs launchpadlib's OAuth right there — a browser opens once and the token is kept in your system keyring, so `report` never has to authenticate. Legacy configs without the `version` key (one single-account section per source) keep working: they are migrated transparently when loaded and rewritten in the v2 shape the next time the wizard saves. ## Architecture ``` src/weekly_activity/ model.py ActivityReport, Section, last_week_window report.py format_report() — source-agnostic text renderer cli.py subcommand dispatch (launchpad | github | gitlab | bts | report | config) aggregate.py collect_specs()/format_combined() — one text report across sources config.py ActivityConfig schema + TOML load/save (tomllib read, hand-written) wizard.py interactive `config` onboarding sources/ __init__.py ActivitySource protocol launchpad.py LaunchpadSource (launchpadlib) github.py GitHubSource (httpx) gitlab.py GitLabSource (httpx, REST v4 — gitlab.com or self-hosted) debian_bts.py DebianBtsSource (python-debianbts SOAP) ``` Each source implements `ActivitySource` — a `name` and a `collect(username, since, until) -> ActivityReport` method. Sources keep their own dataclasses internally; the shared contract is `Section` (title + pre-formatted lines) and `ActivityReport` (metadata + sections + warnings). The report formatter (`report.py`) is source-agnostic. Per-query failures (rate limits, server timeouts) are caught per-section — a failing endpoint records a warning and degrades to an empty section rather than killing the whole report. ## Development ```bash uv sync # install deps + dev tooling uv run ruff format src # format uv run ruff check src # lint uv run ty check # type check ``` ## CI / releases Gitea Actions (`.gitea/workflows/ci.yml`) runs two jobs: - **check** — format, lint, and type check on every push to `main` - **release** — on `v*` tags: builds a standalone Linux executable with PyInstaller, smoke-tests it, creates a Gitea release, and uploads the binary as an asset Cut a release with: ```bash git tag vX.Y.Z && git push origin vX.Y.Z ``` ## Dependencies - **launchpadlib** — Launchpad REST API client - **httpx** — GitHub & GitLab REST API client - **python-debianbts** — Debian BTS SOAP client - **ruff** — format + lint (dev) - **ty** — type check (dev)