Convert README.nfo to README.org !1
3 files changed, +125 −86
Layout: unified · split
.github/workflows/test.yml added +35
| @@ -0,0 +1,35 @@ | ||
| 1 | name: Test | |
| 2 | ||
| 3 | # This service had no CI at all: 46 tests under tests/ that ran only when | |
| 4 | # someone remembered to. Six dependency bumps were merged in one sitting with | |
| 5 | # nothing verifying them, which is the failure this exists to prevent. | |
| 6 | on: | |
| 7 | pull_request: | |
| 8 | push: | |
| 9 | branches: [main] | |
| 10 | ||
| 11 | permissions: | |
| 12 | contents: read | |
| 13 | ||
| 14 | jobs: | |
| 15 | test: | |
| 16 | runs-on: ubuntu-latest | |
| 17 | steps: | |
| 18 | - uses: actions/checkout@v7 | |
| 19 | ||
| 20 | # uv is how the project is actually developed — uv.lock is committed and | |
| 21 | # is what dependabot updates, so CI must resolve through it rather than | |
| 22 | # pip-installing whatever is newest. | |
| 23 | - uses: astral-sh/setup-uv@v10.0.1 | |
| 24 | with: | |
| 25 | enable-cache: true | |
| 26 | ||
| 27 | # --locked, not --frozen. --frozen installs from the lockfile without | |
| 28 | # looking at pyproject.toml, so a manifest that has drifted past its lock | |
| 29 | # installs happily; --locked fails instead. Verified both ways against a | |
| 30 | # deliberately drifted constraint. | |
| 31 | - name: Install | |
| 32 | run: uv sync --extra dev --locked | |
| 33 | ||
| 34 | - name: Tests | |
| 35 | run: uv run pytest -q | |
README.nfo deleted −86
| @@ -1,86 +0,0 @@ | ||
| 1 | ┌──────────────────────────────────────────────────────────────┐ | |
| 2 | │ H U T C H - S T A T S [ KRZ ] krz.sh │ | |
| 3 | └──────────────────────────────────────────────────────────────┘ | |
| 4 | ||
| 5 | WHAT | |
| 6 | srht-contrib. python service. polls sourcehut activity, normalizes | |
| 7 | it into one event model, stores it in sqlite, and serves a | |
| 8 | contribution-calendar json api an ios app renders directly. | |
| 9 | ||
| 10 | v1 scope: | |
| 11 | - fastapi json api | |
| 12 | - sqlite persistence | |
| 13 | - polling ingestion | |
| 14 | - full todo.sr.ht path | |
| 15 | - git.sr.ht commit ingestion with repo auto-discovery | |
| 16 | - public read-only endpoints, api-key auth for mutating routes | |
| 17 | - alembic migrations | |
| 18 | ||
| 19 | DOES | |
| 20 | collects activity from sr.ht graphql services, aggregates by day, | |
| 21 | and returns zero-filled ranges so the client never patches missing | |
| 22 | dates. polls incrementally, backfills the last 365 days for new | |
| 23 | actors, prunes older activity. | |
| 24 | ||
| 25 | SERVICES | |
| 26 | implemented: todo.sr.ht, git.sr.ht. | |
| 27 | event types: ticket_created, ticket_comment, ticket_closed, commit. | |
| 28 | ||
| 29 | ENV | |
| 30 | API_KEY=replace-me | |
| 31 | ENABLE_SCHEDULER=false | |
| 32 | SRHT_TOKEN=replace-me | |
| 33 | TODO_SRHT_ENDPOINT=https://todo.sr.ht/query | |
| 34 | GIT_SRHT_ENDPOINT=https://git.sr.ht/query | |
| 35 | DATABASE_URL=sqlite:///./srht_contrib.db | |
| 36 | DEFAULT_ACTOR=~your-user | |
| 37 | POLL_INTERVAL_SECONDS=300 | |
| 38 | ||
| 39 | more knobs (discovery batch size, repoll and backoff intervals, | |
| 40 | actor aliases, tracked repositories) live in config.py. | |
| 41 | ||
| 42 | RUN | |
| 43 | uv venv | |
| 44 | source .venv/bin/activate | |
| 45 | uv pip install -e ".[dev]" | |
| 46 | cp .env.example .env | |
| 47 | alembic upgrade head | |
| 48 | uvicorn srht_contrib.main:app --reload | |
| 49 | ||
| 50 | POLL | |
| 51 | manual poll: | |
| 52 | ||
| 53 | curl -X POST \ | |
| 54 | "http://127.0.0.1:8000/api/contributions/poll?actor=~your-user" \ | |
| 55 | -H "X-API-Key: replace-me" | |
| 56 | ||
| 57 | scheduled polling runs only when ENABLE_SCHEDULER=true. it drains | |
| 58 | due actors in batches, indexes fast, then backfills a year in | |
| 59 | bounded passes. | |
| 60 | ||
| 61 | bulk queue without immediate indexing: | |
| 62 | ||
| 63 | srht-enqueue-actors srht_usernames.txt --stagger-seconds 60 | |
| 64 | ||
| 65 | ENDPOINTS | |
| 66 | calendar by year, calendar by date range, stats, and repository | |
| 67 | crud. full request and response shapes are in API.txt. | |
| 68 | ||
| 69 | WEIGHTS | |
| 70 | commit 1.0, ticket_created 1.0, ticket_comment 0.5, | |
| 71 | ticket_closed 0.75, build_started 0.25, build_passed 0.25. | |
| 72 | ||
| 73 | TESTS | |
| 74 | pytest | |
| 75 | ||
| 76 | LIMITS | |
| 77 | - git polling assumes repos are discoverable via graphql | |
| 78 | - the scheduler runs in-process, not distributed | |
| 79 | - new actors index asynchronously; the first read may be empty | |
| 80 | - rolling one-year window; older activity is not kept | |
| 81 | - alias management is config-driven, no crud api yet | |
| 82 | - trusted-operator v1, not a public multi-tenant service | |
| 83 | ||
| 84 | ┌──────────────────────────────────────────────────────────────┐ | |
| 85 | │ krz.sh │ | |
| 86 | └──────────────────────────────────────────────────────────────┘ | |
README.org added +90
| @@ -0,0 +1,90 @@ | ||
| 1 | #+title: hutch-stats | |
| 2 | ||
| 3 | * what | |
| 4 | srht-contrib. python service. polls sourcehut activity, normalizes | |
| 5 | it into one event model, stores it in sqlite, and serves a | |
| 6 | contribution-calendar json api an ios app renders directly. | |
| 7 | ||
| 8 | v1 scope: | |
| 9 | - fastapi json api | |
| 10 | - sqlite persistence | |
| 11 | - polling ingestion | |
| 12 | - full todo.sr.ht path | |
| 13 | - git.sr.ht commit ingestion with repo auto-discovery | |
| 14 | - public read-only endpoints, api-key auth for mutating routes | |
| 15 | - alembic migrations | |
| 16 | ||
| 17 | * does | |
| 18 | collects activity from sr.ht graphql services, aggregates by day, | |
| 19 | and returns zero-filled ranges so the client never patches missing | |
| 20 | dates. polls incrementally, backfills the last 365 days for new | |
| 21 | actors, prunes older activity. | |
| 22 | ||
| 23 | * services | |
| 24 | implemented: todo.sr.ht, git.sr.ht. | |
| 25 | event types: ticket_created, ticket_comment, ticket_closed, commit. | |
| 26 | ||
| 27 | * env | |
| 28 | #+begin_src conf | |
| 29 | API_KEY=replace-me | |
| 30 | ENABLE_SCHEDULER=false | |
| 31 | SRHT_TOKEN=replace-me | |
| 32 | TODO_SRHT_ENDPOINT=https://todo.sr.ht/query | |
| 33 | GIT_SRHT_ENDPOINT=https://git.sr.ht/query | |
| 34 | DATABASE_URL=sqlite:///./srht_contrib.db | |
| 35 | DEFAULT_ACTOR=~your-user | |
| 36 | POLL_INTERVAL_SECONDS=300 | |
| 37 | #+end_src | |
| 38 | ||
| 39 | more knobs (discovery batch size, repoll and backoff intervals, | |
| 40 | actor aliases, tracked repositories) live in config.py. | |
| 41 | ||
| 42 | * run | |
| 43 | #+begin_src sh | |
| 44 | uv venv | |
| 45 | source .venv/bin/activate | |
| 46 | uv pip install -e ".[dev]" | |
| 47 | cp .env.example .env | |
| 48 | alembic upgrade head | |
| 49 | uvicorn srht_contrib.main:app --reload | |
| 50 | #+end_src | |
| 51 | ||
| 52 | * poll | |
| 53 | manual poll: | |
| 54 | ||
| 55 | #+begin_src sh | |
| 56 | curl -X POST \ | |
| 57 | "http://127.0.0.1:8000/api/contributions/poll?actor=~your-user" \ | |
| 58 | -H "X-API-Key: replace-me" | |
| 59 | #+end_src | |
| 60 | ||
| 61 | scheduled polling runs only when ENABLE_SCHEDULER=true. it drains | |
| 62 | due actors in batches, indexes fast, then backfills a year in | |
| 63 | bounded passes. | |
| 64 | ||
| 65 | bulk queue without immediate indexing: | |
| 66 | ||
| 67 | #+begin_src sh | |
| 68 | srht-enqueue-actors srht_usernames.txt --stagger-seconds 60 | |
| 69 | #+end_src | |
| 70 | ||
| 71 | * endpoints | |
| 72 | calendar by year, calendar by date range, stats, and repository | |
| 73 | crud. full request and response shapes are in API.txt. | |
| 74 | ||
| 75 | * weights | |
| 76 | commit 1.0, ticket_created 1.0, ticket_comment 0.5, | |
| 77 | ticket_closed 0.75, build_started 0.25, build_passed 0.25. | |
| 78 | ||
| 79 | * tests | |
| 80 | #+begin_src sh | |
| 81 | pytest | |
| 82 | #+end_src | |
| 83 | ||
| 84 | * limits | |
| 85 | - git polling assumes repos are discoverable via graphql | |
| 86 | - the scheduler runs in-process, not distributed | |
| 87 | - new actors index asynchronously; the first read may be empty | |
| 88 | - rolling one-year window; older activity is not kept | |
| 89 | - alias management is config-driven, no crud api yet | |
| 90 | - trusted-operator v1, not a public multi-tenant service | |