Full-stack automation
Internship Radar
A personal tool that scrapes placement listings into one ranked feed, filters out placeholder roles, explains every fit score rule by rule and tracks where each application stands.
- Data
- One scraper, plus manual entryRateMyPlacement, de-duplicated and filtered
- Backend
- FastAPI, SQLAlchemy, PostgreSQLRule-based fit score, explained rule by rule
- Interface
- React, TypeScriptRadar, Shortlist and Pipeline over one dataset
- Testing
- 180 backend tests (pytest)Plus 36 frontend tests (Vitest)

01
Collecting listings
Placement listings are spread across job boards and padded with “register your interest” posts that are not real roles. Internship Radar pulls listings into one place and removes that noise before anything is scored.
- A scraper reads RateMyPlacement’s technology listings (now higherin.com) from the JSON embedded in each results page rather than from fragile markup, then fetches each detail page for the description.
- Requests are spaced out, and listings are de-duplicated by URL in memory, against the database and with a unique constraint.
- Placeholder listings such as expressions of interest and talent pools are filtered out before saving.
- Roles found elsewhere can be added by hand; the API rejects a duplicate URL with a 409.
02
A score that explains itself
Each listing gets a fit score from 0 to 100 when it is added. Five rules award points: a placement or internship role (+30), tech keywords (+5 each, up to 30), Manchester (+20) or UK, remote or hybrid (+10), stated requirements (+10) and a modern stack (+10). Three penalties subtract: graduate-only roles (−40), unpaid or volunteer roles (−50) and off-target departments such as sales or audit (−30).
- Transparent rules over a model
- For one user and one source, a hand-weighted score is explainable and testable. Keywords match as whole words, with plurals for longer words and capitalised acronyms inside words such as RESTful, so short terms do not match inside unrelated ones.
- The explanation comes from the scorer itself
- Every job the API returns carries a breakdown built from the same Python scoring module that produced its score: each rule’s points, maximum, state and the words that matched. The browser never recalculates a score, so the explanation cannot drift from the number.

03
Tracking applications
- Tracking as state, not notes
- Each role moves through new, applied, interview, offer or rejected, with shortlisting kept as a separate flag. Radar, Shortlist and Pipeline are three views over the same data, so a change made in one shows in the others.
- Changes apply at once and stay reversible
- Status and shortlist changes update the interface immediately, roll back if the server rejects them, and can be undone.
- Shortlist groups saved roles by what to do next: considering, worth considering (the highest-fit open roles not yet saved), deadline passed and already in the pipeline.
- Filters cover search, role type with live counts, location, deadline window, minimum fit and status, and the current view and selected role live in the URL.
- On phones the list and the role become separate screens with a back bar, and filters open in a bottom sheet.

04
Limits
- One automated source. Other boards appear as sources only for roles added by hand.
- Scraping runs as a script rather than on a schedule.
- Scoring preferences such as Manchester are fixed in code, not user settings.
- Single-user with no authentication, and schema changes are applied without migrations.
- CI runs the backend tests and the frontend build, but not the frontend tests.