Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/mannmadesolutions/mmi-teach/archive/refs/heads/main.zipA WordPress plugin that collects every open K-12 teaching vacancy in Milwaukee, Waukesha and Racine counties, Wisconsin and shows them on one filterable dashboard.
What it shows
- Open teaching postings: totals per county, new in the last 7 days, closing within 14 days.
- Charts: postings by subject, top employers, and open postings by week posted. Click a bar to filter.
- A sortable table of every posting. Click a row for salary, openings, required licenses, contact, a summary and links to apply.
- Filters for county, subject, grade level, full or part time, posting date, keyword, and substitute postings (hidden by default). The filters live in the URL, so a filtered view can be bookmarked or shared.
- Pay: every posting's pay text is parsed into an amount, a unit (hour, day, year, stipend) and a daily equivalent (hourly × 7, annual ÷ 180) for sorting and filtering. "Salary schedule" and "depends on experience" postings are labeled as such; the posted wording is kept in the row details.
- Substitute jobs: postings are tagged daily/on-call, building or long-term sub. The Substitute jobs button and the Job type filter switch between teaching and substitute postings, and a tile shows the median sub pay per day.
- CSV download of the current view.
Become a substitute teacher (/substitute/)
A public guide that removes the guesswork from becoming a sub in the three counties:
- Find your license: a short questionnaire names the DPI permit or license that fits, whether training and fingerprints are needed, and flags employers with stricter rules (MPS requires a bachelor's degree).
- Get trained: approved courses compared by format, length, cost and next start date.
- Apply to DPI: the exact ELO steps, fees and processing times.
- Choose where to sub: direct district hiring vs EDUStaff vs Kelly Education (who employs you, retirement plan, process), and a live table of every employer with open sub postings and their posted day rates.
- Your plan: total cost, a timeline showing how much sooner you start if you do employer steps during the DPI wait, and a printable checklist (saved in your browser only).
Facts live in data/pathway.json, each with a source URL and verified_on date; Settings →
Teaching Vacancies lists any older than 120 days. Live numbers come from the job data.
Qualification requirements
Every posting carries what it takes to qualify, each part labeled with where it came from:
| Part | From the posting | When the posting doesn't say |
|---|---|---|
| Minimum degree | WECAN's Degree field, or the description ("Bachelor's degree required") | Wisconsin rule: substitute permit = associate; teaching license = bachelor's; pupil services = master's; Head Start = associate |
| DPI license areas | WECAN's Licenses list (any one qualifies), or license areas / DPI codes the description names | The licenses a job with that title, subject and grade level normally needs (data/licenses.json → infer.rules) |
| Experience, what to submit | WECAN's Years of Experience and Additional Requirements | — |
| Other ways in | — | One-Year License with Stipulations, License Based on Equivalency, Choice-school (bachelor's accepted) and charter-license rules |
data/licenses.json holds 66 DPI license areas with official titles, ELO subject codes, current
(2018 PI 34) and legacy grade levels, minimum degree, required tests, regex aliases, and the 87
WECAN license names mapped to them. The job board filters by "My highest degree" (shows jobs whose
required degree you meet) and by license area.
Staffing agencies
Every posting shows which staffing agency, if any, fills it (job board column and filter):
- Named in the posting: the posting itself says so ("Employer: Edustaff", "employed through Kelly Education", a mykelly.com link). A short quote is kept as evidence.
- The employer's route: for substitute jobs,
data/agencies.jsonrecords how each employer fills them: through an agency, or directly. A route covers daily subs; agency districts often hire building and long-term subs themselves, so those show "Not confirmed" unless researched. Routes resting on an agency ad or board discussion are labeled "likely". - Regular teaching jobs are hired directly by the employer.
data/agencies.json also lists every agency found placing school staff in Wisconsin, with the
regex aliases used to recognize it in posting text, its clients and sources.
Where the data comes from
| Source | What it covers | How it's read |
|---|---|---|
| WECAN | The statewide K-12 application system. Nearly every public district in the three counties posts here, plus many charter and private schools | Public vacancy search grid (Teacher, Teacher Leader and Substitute types), plus each in-area posting's detail page |
| Milwaukee Public Schools careers | MPS's full external list (only some MPS jobs reach WECAN) | PeopleSoft Candidate Gateway list page |
| Archdiocese of Milwaukee careers | Catholic school openings across the archdiocese | Public careers page, placed by the city in each card |
| Paycom | Milwaukee College Prep, UCC/Bruce-Guadalupe, Next Door, Penfield, Messmer | Public job page → search API |
| Paycor | HOPE Christian Schools | Public career page |
| Lever | Rocketship (Milwaukee schools only) | Public postings API |
| Breezy HR | Carmen Schools of Science & Technology | Public JSON feed |
| iSolved Hire | Cristo Rey Jesuit High School | Public jobs API |
None of these needs an account or key. Every request identifies the plugin in its User-Agent and waits at least 2 seconds between requests to the same host.
When a school lists the same job on WECAN and its own board, the dashboard shows it once (the WECAN copy, which has the most detail) and links the other listing.
County matching. WECAN covers the whole state, so each posting is placed in a county from
its employer and school names, using data/places.json. That file holds every Wisconsin public
district and school (NCES Common Core of Data) and every private school (NCES Private School
Survey) with its county. Employers that match nothing there are listed under Settings →
Teaching Vacancies, where you can place them; data/employers.json holds the checked answers
that ship with the plugin.
Running it
wp plugin activate mmi-teach # creates the table and the dashboard page (front page on an empty site)
wp mmi-teach refresh # read every source; --details=<n> caps detail-page reads (default 200)
wp mmi-teach status # counts per county, source health
wp mmi-teach unplaced # employers not placed in any county
wp mmi-teach reclassify # re-run county, subject, sub-kind and pay parsing after editing data/*.json or rules
wp mmi-teach build-places # rebuild data/places.json from NCES (when a new year is published)
On the server, cron runs wp mmi-teach refresh every 3 hours. A twice-daily WP-Cron event and
the Refresh now button are fallbacks; they cap detail-page reads to stay inside WP-Cron's
time budget.
Safety
- A source that fails, or lists fewer than half the postings it had last time, keeps its last
good list, and the dashboard footer says so. Postings are never deleted: one that leaves its
board gets a
removed_atdate, so history survives. - The dashboard defaults to signed-in users only and is hidden from search engines until you switch it to public in Settings.
- Data reaches the page through one REST route (
/wp-json/mmi-teach/v1/snapshot) that checks the same visibility rule. All posting text is inserted withtextContent; links must be http(s). - Page views read a stored snapshot built at refresh time; nothing is fetched or classified per view.
Adding a source
Implement MMI_Teach\Sources\Source (or extend Sources\Board for a single-employer ATS), and
add it to Refresher::sources() or the mmi_teach_sources filter. fetch() must throw rather
than return a partial list.