An interactive web app for the results of Мислиша, Serbia's primary-school math competition. Three fully self-contained HTML pages let anyone search 11,052 participant records and browse school / city / grade analytics — with zero backend.
Status: one-off, actively maintained · Stack: static HTML + vanilla JS + Chart.js (CDN), Python for data prep · Runs at: https://den.tanaev.su/mislisa/
Live pages:
| Page | URL |
|---|---|
| Home — „Савремена" generations comparison (default index) | https://den.tanaev.su/mislisa/ |
| Searchable results table (11,052 rows, 2025 + 2026) | https://den.tanaev.su/mislisa/mislisa2026.html |
| Analytics dashboard (school/city rankings, heatmap, charts) | https://den.tanaev.su/mislisa/mislisa2026-analytics.html |
| „Савремена" (canonical URL for the home page) | https://den.tanaev.su/mislisa/savremena.html |
The official results portal (mislisa.arhimedes.rs) shows one grade at a time inside an
iframe, with no search, no cross-year view, and no analytics. This project scrapes all
grades and years once, then bakes the data into standalone HTML pages that are fast,
searchable, and shareable.
- Results table — filter by year (2025 / 2026 / both), grade (1–8), and award type; free-text search across student, school, and city; live award-count stats.
- Analytics — schools and cities ranked by a fair quality index, a top-30 × 8-grade heatmap, and Chart.js breakdowns of awards and score distributions.
- Home / Савремена — a focused comparison view for the „Савремена" school.
Every page is a single HTML file with the data embedded as JS constants — it works offline (swap the Chart.js CDN link for a local copy) and needs no server, database, or build step.
Data is prepared once by a small Python pipeline and embedded into the HTML. There is no runtime backend — the browser just reads in-page constants.
mislisa.arhimedes.rs (official results API, no CORS — server-side fetch only)
│ 8 grades × N years
▼
[scrape] Python + urllib + HTMLParser
│ → data/mislisa_<year>.json (raw rows per year)
▼
[combine] merge years, add `godina` field
│ → data/mislisa_all.json (11,052 combined rows)
▼
[analytics] pre-compute school/city/grade rankings + score distributions
│ → data/mislisa_analytics.json (all / 2025 / 2026 objects)
▼
[embed] inject JSON into HTML templates (placeholder + str.replace)
│ → mislisa2026.html · mislisa2026-analytics.html · savremena.html
▼
[deploy] scp → reports:/opt/den-tanaev/public/mislisa/
│
▼
nginx → https://den.tanaev.su/mislisa/ (index.html is a symlink → savremena.html)
The data/*.json files are the source of truth for regeneration. The deployed HTML files
carry their own copy of that data embedded inline.
Just view it — open any HTML file directly in a browser (no server needed):
open /Users/dt/Projects/mislisa/mislisa2026.html
open /Users/dt/Projects/mislisa/mislisa2026-analytics.htmlRegenerate the data + pages — requires Python 3 only (standard library; no pip installs).
The full scrape → combine → analytics → embed pipeline, with runnable scripts, lives in
CLAUDE.md under "How to Regenerate from Scratch". In short:
- Scrape each year from the Arhimedes API →
data/mislisa_<year>.json - Combine years →
data/mislisa_all.json - Compute analytics →
data/mislisa_analytics.json - Embed the JSON into the HTML templates (use placeholder +
.replace(), not f-strings — see gotcha below)
Summarized from CLAUDE.md → "How to Add a New Year":
- Scrape with
YEAR = 2027→data/mislisa_2027.json - Combine — add
2027to the years list → rebuildsdata/mislisa_all.json - Analytics — add
"2027": compute(...)to the result dict - Table HTML — add the year to the switcher/filter pills and refresh the embedded data
- Analytics HTML — add
"2027": {...}to the embeddedAVconstant and the year switcher - Deploy the updated HTML files
mislisa/
├── README.md ← this file (public landing page)
├── CLAUDE.md ← deep pipeline notes: schema, regen scripts, gotchas
├── savremena.html ← home page („Савремена"); index.html symlinks to it in prod
├── mislisa2026.html ← searchable results table (~2.0 MB, data embedded)
├── mislisa2026-analytics.html ← analytics dashboard (~1.5 MB, data embedded)
└── data/
├── mislisa_2025.json ← raw 2025 rows
├── mislisa_2026.json ← raw 2026 rows
├── mislisa_all.json ← combined 11,052 rows (with `godina`)
└── mislisa_analytics.json ← pre-computed analytics (all / 2025 / 2026)
Only .DS_Store is gitignored — the data JSON is committed so the pages can be regenerated
from a clean clone.
Ship the static files to the personal reports host over SSH:
scp /Users/dt/Projects/mislisa/savremena.html \
/Users/dt/Projects/mislisa/mislisa2026.html \
/Users/dt/Projects/mislisa/mislisa2026-analytics.html \
reports:/opt/den-tanaev/public/mislisa/- SSH alias
reports→ the reports server; web root/opt/den-tanaev/public/. - nginx serves the
mislisa/directory athttps://den.tanaev.su/mislisa/. index.htmlon the server is a symlink →savremena.html, so/mislisa/opens the home page.
Private personal project, © dt. Backed up to https://github.com/deetan/mislisa (public repo).