A Django admin dashboard that tracks your skills, the proof behind each one, and your job-search funnel. Skills lose freshness over time (a configurable half-life), so the dashboard always shows what to revise next.
flowchart LR
subgraph Users["Who uses it"]
Visitor["Visitor"]
Staff["Staff user"]
You["You, from the shell"]
end
subgraph Django["Django project · config/urls.py"]
Dash["/ · views.dashboard<br/>skills sorted by freshness, funnel"]
Revise["POST /skills/:pk/revise/<br/>staff only, returns row partial"]
Admin["/admin/ · Unfold ModelAdmin<br/>freshness badge, mark_revised action,<br/>dashboard_callback"]
API["/api/v1/ · DRF, read-only<br/>skills, proofs, summary<br/>(star_answer never exposed)"]
Agg["core/dashboard.py<br/>annotate last_proof + proof_count<br/>(no N+1)"]
Cmds["manage.py commands<br/>seed_demo · sync_github · weekly_digest"]
end
Visitor --> Dash & API
Staff --> Revise & Admin
You --> Cmds
Dash & Admin & Cmds --> Agg
subgraph Models["core/models.py"]
Skill["Skill<br/>freshness = exp(-ln2 · days / half_life)"]
Proof["Proof<br/>repo / case / metric"]
Note["RevisionNote"]
Variant["ResumeVariant"]
App["Application<br/>saved → applied → replied →<br/>interview → offer / rejected"]
Snap["MetricSnapshot (weekly)"]
Proof -- "M2M" --> Skill
Note -- "FK" --> Skill
App -- "FK" --> Variant
end
Agg & API & Revise --> Models
Models --> DB[("SQLite by default<br/>PostgreSQL 16 when POSTGRES_DB set")]
Cmds -- "sync_github: repos → Proof,<br/>topics matched to Skill names" --> GH[("GitHub REST API<br/>/user/repos (token) or<br/>/users/GITHUB_USER/repos")]
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py migrate
python manage.py seed_demo
python manage.py createsuperuser
python manage.py runserver- Public dashboard: http://localhost:8000/
- React + TypeScript dashboard: http://localhost:8000/app/
- Admin: http://localhost:8000/admin/
- API: http://localhost:8000/api/v1/ (read-only)
Or with Postgres: docker compose up.
The /app/ dashboard is a Vite + React + TypeScript client that reads the public API. Its
production build is committed in core/static/app/, so the Django server works without Node.
cd frontend
npm ci
npm run build # writes ../core/static/app/app.js and app.cssFor hot reload, run npm run dev in frontend/ (port 5173) next to manage.py runserver; Vite
proxies /api, /admin and /static to Django. npm run typecheck and npm run build both
run tsc --noEmit.
| Command | What it does |
|---|---|
seed_demo |
Creates starter skills (with GitHub topic aliases) and resume variants. Edit levels to be honest. |
sync_github |
Pulls every repo GITHUB_USER owns (paginated) as Proof rows and links skills by repo topic and primary language. Without GITHUB_TOKEN only public repos are visible; with it, private repos are included too. Forks and archived repos are skipped unless you pass --include-forks / --include-archived; --prune deletes proofs for repos GitHub no longer lists (renamed or deleted). It prints how many repos it found and why any were skipped. |
link_check |
Requests every proof URL and flags dead links (link_ok), shown on the dashboard and admin. |
weekly_digest [--save] |
Prints a markdown plan: stale skills, unproven skills, funnel, reply rate per resume variant, next actions.--save stores this week's numbers as a MetricSnapshot. |
- Public page (
/): the interactive decay curve (with a Play button), totals with a live "points fading a day" ticker, a banner for skills with no proof, the half-life map, every skill by freshness (staff can mark one revised in place with HTMX), every proof with a filter and kind chips, the application funnel and reply rate by resume variant. - Half-life map: every skill plotted on one curve by half-lives idle (
0.5 ** n), so skills with different half-lives compare directly and the fresh/fading/stale bands are fixed. Skills with no revision or proof sit in a "no proof" column. Hover or focus a pin for its name; click to jump to its row. - Motion: sections reveal on scroll, bars sweep in, totals count up. Mark revised refills the bar,
counts the percent up, floats a +N chip and slides the skill's pin back to the top of the map. All of
it is skipped under
prefers-reduced-motion, and the page is complete without JavaScript. - Admin home: the same numbers for you, plus the five skills to revise next and weekly snapshots.
- API (
/api/v1/):skills/,proofs/,summary/andfunnel/, read-only, throttled, and the summary and funnel cached for 60 seconds. - React dashboard (
/app/): the TypeScript client. KPI tiles, a freshness curve, searchable and filterable skills, the full funnel, reply rate per resume variant, and a filterable proof library. It refreshes every 60 seconds and shows the last good snapshot if a refresh fails.
A repo topic proves a skill when it equals the skill's name (lowercased, spaces as dashes) or one of
its github_topics, for example LLM integration with llm, agents. Edit them in the admin.
ruff check .
pytest --cov=core --cov-fail-under=85Every change also adds an entry under Unreleased in CHANGELOG.md and updates the docs it
affects (this README, docs/, and NOTES.md for mistakes). The pull request template has a
checklist for it, and a CI job fails any pull request that skips the changelog (label it
skip-changelog for changes that truly need none).
- Freshness is
exp(-ln2 * days / half_life), tested with frozen time. - Admin querysets annotate the last proof date, so the list view has no N+1 queries (tested).
- The API never exposes
star_answer, your private interview drafts (tested). - "Responded" means replied, interview or offer; "sent" is anything past saved. Reply rate is per resume variant.
- Weekly snapshots count applications by
applied_onweek, so they can be re-saved without double counting. - Marking a skill revised from the public page needs a logged-in staff user.
Vercel (Python runtime) with a Neon Postgres database. The full guide is in docs/deployment.md; the short version:
- Create a Neon project in AWS
us-east-1and copy its pooled connection string. - Import the repo in Vercel.
vercel.jsonsets the build (vercel-build.sh) and the function (config/wsgi.py). - Set
SECRET_KEYandDATABASE_URL(Production and Preview), plusDJANGO_SUPERUSER_*(Production) to create the admin user on the first production build. - Push to
main. Production builds migrate and seed; preview builds never touch the schema.
DEBUG defaults to off on Vercel and the *.vercel.app hostnames are allowed automatically.
render.yaml and the Dockerfile are kept as fallbacks; see the guide.
- Link-check command for proof URLs
- Topic-to-skill mapping (
Skill.github_topics) - Scheduled
link_checkandsync_github(needs a cron host or a CI secret for the database) - LLM-drafted STAR answers through a provider gateway
- React + TypeScript dashboard on the public API (
/app/)
See CHANGELOG.md for what changed in each release and NOTES.md for mistakes made and fixed.