All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m1s
- Add runtime normalization of cryptic school type codes to user-friendly names (e.g., AC/ACC/ACCS -> "Academy", CY/CYS -> "Community") - Update SCHOOL_TYPE_MAP in schemas.py with consolidated mappings - Add normalize_school_type() and get_school_type_codes_for_filter() helpers - Persist selected schools in localStorage across page refreshes - Move "Add to Compare" button from modal footer to header Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
443 lines
22 KiB
HTML
443 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>SchoolCompare | Compare Primary School Performance</title>
|
|
|
|
<!-- Primary Meta Tags -->
|
|
<meta name="description" content="Compare primary school KS2 performance across England. Search, filter and compare Reading, Writing and Maths results for thousands of schools.">
|
|
<meta name="keywords" content="school comparison, KS2 results, primary school performance, England schools, SATs results">
|
|
<meta name="author" content="SchoolCompare">
|
|
<meta name="robots" content="index, follow">
|
|
|
|
<!-- Favicon -->
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
|
|
<!-- Canonical -->
|
|
<link rel="canonical" href="https://schoolcompare.co.uk/">
|
|
|
|
<!-- Open Graph / Facebook -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:url" content="https://schoolcompare.co.uk/">
|
|
<meta property="og:title" content="SchoolCompare | Compare Primary School Performance">
|
|
<meta property="og:description" content="Compare primary school KS2 performance across England. Search and compare Reading, Writing and Maths results.">
|
|
<meta property="og:site_name" content="SchoolCompare">
|
|
|
|
<!-- Twitter -->
|
|
<meta name="twitter:card" content="summary">
|
|
<meta name="twitter:url" content="https://schoolcompare.co.uk/">
|
|
<meta name="twitter:title" content="SchoolCompare | Compare Primary School Performance">
|
|
<meta name="twitter:description" content="Compare primary school KS2 performance across England.">
|
|
|
|
<!-- JSON-LD Structured Data -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "WebApplication",
|
|
"name": "SchoolCompare",
|
|
"url": "https://schoolcompare.co.uk",
|
|
"description": "Compare primary school KS2 performance across England",
|
|
"applicationCategory": "EducationalApplication",
|
|
"operatingSystem": "Web",
|
|
"offers": {
|
|
"@type": "Offer",
|
|
"price": "0",
|
|
"priceCurrency": "GBP"
|
|
},
|
|
"author": {
|
|
"@type": "Organization",
|
|
"name": "SchoolCompare",
|
|
"url": "https://schoolcompare.co.uk"
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<!-- Leaflet Map Library -->
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
<!-- Cookie Consent Banner -->
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/silktide/consent-manager@main/silktide-consent-manager.css">
|
|
</head>
|
|
<body>
|
|
<div class="noise-overlay"></div>
|
|
|
|
<header class="header">
|
|
<div class="header-content">
|
|
<a href="/" class="logo">
|
|
<div class="logo-icon">
|
|
<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<circle cx="20" cy="20" r="18" stroke="currentColor" stroke-width="2"/>
|
|
<path d="M20 8L20 32M12 14L28 14M10 20L30 20M12 26L28 26" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
<circle cx="20" cy="20" r="4" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="logo-text">
|
|
<span class="logo-title">SchoolCompare</span>
|
|
<span class="logo-subtitle">schoolcompare.co.uk</span>
|
|
</div>
|
|
</a>
|
|
<nav class="nav">
|
|
<a href="/" class="nav-link active" data-view="home">Home</a>
|
|
<a href="/compare" class="nav-link" data-view="compare">Compare</a>
|
|
<a href="/rankings" class="nav-link" data-view="rankings">Rankings</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="main">
|
|
<!-- Home View -->
|
|
<section id="home-view" class="view active">
|
|
<div class="hero">
|
|
<h1 class="hero-title">Compare Primary School Performance</h1>
|
|
<p class="hero-subtitle">Search and compare KS2 results across England's primary schools</p>
|
|
</div>
|
|
|
|
<div class="search-section">
|
|
<div class="search-mode-toggle">
|
|
<button class="search-mode-btn active" data-mode="name">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
|
|
<circle cx="11" cy="11" r="8"/>
|
|
<path d="M21 21l-4.35-4.35"/>
|
|
</svg>
|
|
Find by Name
|
|
</button>
|
|
<button class="search-mode-btn" data-mode="location">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
|
<circle cx="12" cy="10" r="3"/>
|
|
</svg>
|
|
Find by Location
|
|
</button>
|
|
</div>
|
|
|
|
<div id="name-search-panel" class="search-panel active">
|
|
<div class="search-container">
|
|
<input type="text" id="school-search" class="search-input" placeholder="Search primary schools by name...">
|
|
<div class="search-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="11" cy="11" r="8"/>
|
|
<path d="M21 21l-4.35-4.35"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div class="filter-row">
|
|
<select id="local-authority-filter" class="filter-select">
|
|
<option value="">All Areas</option>
|
|
</select>
|
|
<select id="type-filter" class="filter-select">
|
|
<option value="">All School Types</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="location-search-panel" class="search-panel">
|
|
<div class="location-input-group">
|
|
<input type="text" id="postcode-search" class="search-input postcode-input" placeholder="Enter postcode...">
|
|
<select id="radius-select" class="filter-select radius-select">
|
|
<option value="0.5" selected>1/2 mile</option>
|
|
<option value="1">1 mile</option>
|
|
<option value="2">2 miles</option>
|
|
</select>
|
|
<button id="location-search-btn" class="btn btn-primary location-btn">Find Nearby</button>
|
|
</div>
|
|
<div class="filter-row">
|
|
<select id="type-filter-location" class="filter-select">
|
|
<option value="">All School Types</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="schools-grid" id="schools-grid">
|
|
<!-- School cards populated by JS -->
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Compare View -->
|
|
<section id="compare-view" class="view">
|
|
<div class="compare-header">
|
|
<h2 class="section-title">Compare Primary Schools</h2>
|
|
<p class="section-subtitle">Select schools to compare their KS2 performance over time</p>
|
|
</div>
|
|
|
|
<div class="compare-search-section">
|
|
<input type="text" id="compare-search" class="search-input" placeholder="Add a school to compare...">
|
|
<div id="compare-results" class="compare-results"></div>
|
|
</div>
|
|
|
|
<div class="selected-schools" id="selected-schools">
|
|
<div class="empty-selection">
|
|
<div class="empty-icon">
|
|
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<rect x="6" y="10" width="36" height="28" rx="2"/>
|
|
<path d="M6 18h36"/>
|
|
<circle cx="14" cy="14" r="2" fill="currentColor"/>
|
|
<circle cx="22" cy="14" r="2" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<p>Search and add schools to compare</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="charts-section" id="charts-section" style="display: none;">
|
|
<div class="metric-selector">
|
|
<label>Select KS2 Metric:</label>
|
|
<select id="metric-select" class="filter-select">
|
|
<optgroup label="Expected Standard">
|
|
<option value="rwm_expected_pct">Reading, Writing & Maths Combined %</option>
|
|
<option value="reading_expected_pct">Reading Expected %</option>
|
|
<option value="writing_expected_pct">Writing Expected %</option>
|
|
<option value="maths_expected_pct">Maths Expected %</option>
|
|
<option value="gps_expected_pct">GPS Expected %</option>
|
|
<option value="science_expected_pct">Science Expected %</option>
|
|
</optgroup>
|
|
<optgroup label="Higher Standard">
|
|
<option value="rwm_high_pct">RWM Combined Higher %</option>
|
|
<option value="reading_high_pct">Reading Higher %</option>
|
|
<option value="writing_high_pct">Writing Higher %</option>
|
|
<option value="maths_high_pct">Maths Higher %</option>
|
|
<option value="gps_high_pct">GPS Higher %</option>
|
|
</optgroup>
|
|
<optgroup label="Progress Scores">
|
|
<option value="reading_progress">Reading Progress</option>
|
|
<option value="writing_progress">Writing Progress</option>
|
|
<option value="maths_progress">Maths Progress</option>
|
|
</optgroup>
|
|
<optgroup label="Average Scores">
|
|
<option value="reading_avg_score">Reading Avg Score</option>
|
|
<option value="maths_avg_score">Maths Avg Score</option>
|
|
<option value="gps_avg_score">GPS Avg Score</option>
|
|
</optgroup>
|
|
<optgroup label="Gender Performance">
|
|
<option value="rwm_expected_boys_pct">RWM Expected % (Boys)</option>
|
|
<option value="rwm_expected_girls_pct">RWM Expected % (Girls)</option>
|
|
<option value="rwm_high_boys_pct">RWM Higher % (Boys)</option>
|
|
<option value="rwm_high_girls_pct">RWM Higher % (Girls)</option>
|
|
</optgroup>
|
|
<optgroup label="Equity (Disadvantaged)">
|
|
<option value="rwm_expected_disadvantaged_pct">RWM Expected % (Disadvantaged)</option>
|
|
<option value="rwm_expected_non_disadvantaged_pct">RWM Expected % (Non-Disadvantaged)</option>
|
|
<option value="disadvantaged_gap">Disadvantaged Gap vs National</option>
|
|
</optgroup>
|
|
<optgroup label="School Context">
|
|
<option value="disadvantaged_pct">% Disadvantaged Pupils</option>
|
|
<option value="eal_pct">% EAL Pupils</option>
|
|
<option value="sen_support_pct">% SEN Support</option>
|
|
<option value="stability_pct">% Pupil Stability</option>
|
|
</optgroup>
|
|
<optgroup label="3-Year Trends">
|
|
<option value="rwm_expected_3yr_pct">RWM Expected % (3-Year Avg)</option>
|
|
<option value="reading_avg_3yr">Reading Score (3-Year Avg)</option>
|
|
<option value="maths_avg_3yr">Maths Score (3-Year Avg)</option>
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="chart-container">
|
|
<canvas id="comparison-chart"></canvas>
|
|
</div>
|
|
|
|
<div class="data-table-container">
|
|
<table class="data-table" id="comparison-table">
|
|
<thead>
|
|
<tr id="table-header"></tr>
|
|
</thead>
|
|
<tbody id="table-body"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Rankings View -->
|
|
<section id="rankings-view" class="view">
|
|
<div class="rankings-header">
|
|
<h2 class="section-title">Primary School Rankings</h2>
|
|
<p class="section-subtitle">Top performing primary schools ranked by KS2 metric</p>
|
|
</div>
|
|
|
|
<div class="rankings-controls">
|
|
<select id="ranking-area" class="filter-select">
|
|
<option value="">All Areas</option>
|
|
<!-- Populated by JS -->
|
|
</select>
|
|
<select id="ranking-metric" class="filter-select">
|
|
<optgroup label="Expected Standard">
|
|
<option value="rwm_expected_pct">Reading, Writing & Maths Combined %</option>
|
|
<option value="reading_expected_pct">Reading Expected %</option>
|
|
<option value="writing_expected_pct">Writing Expected %</option>
|
|
<option value="maths_expected_pct">Maths Expected %</option>
|
|
<option value="gps_expected_pct">GPS Expected %</option>
|
|
<option value="science_expected_pct">Science Expected %</option>
|
|
</optgroup>
|
|
<optgroup label="Higher Standard">
|
|
<option value="rwm_high_pct">RWM Combined Higher %</option>
|
|
<option value="reading_high_pct">Reading Higher %</option>
|
|
<option value="writing_high_pct">Writing Higher %</option>
|
|
<option value="maths_high_pct">Maths Higher %</option>
|
|
<option value="gps_high_pct">GPS Higher %</option>
|
|
</optgroup>
|
|
<optgroup label="Progress Scores">
|
|
<option value="reading_progress">Reading Progress</option>
|
|
<option value="writing_progress">Writing Progress</option>
|
|
<option value="maths_progress">Maths Progress</option>
|
|
</optgroup>
|
|
<optgroup label="Average Scores">
|
|
<option value="reading_avg_score">Reading Avg Score</option>
|
|
<option value="maths_avg_score">Maths Avg Score</option>
|
|
<option value="gps_avg_score">GPS Avg Score</option>
|
|
</optgroup>
|
|
<optgroup label="Gender Performance">
|
|
<option value="rwm_expected_boys_pct">RWM Expected % (Boys)</option>
|
|
<option value="rwm_expected_girls_pct">RWM Expected % (Girls)</option>
|
|
<option value="rwm_high_boys_pct">RWM Higher % (Boys)</option>
|
|
<option value="rwm_high_girls_pct">RWM Higher % (Girls)</option>
|
|
</optgroup>
|
|
<optgroup label="Equity (Disadvantaged)">
|
|
<option value="rwm_expected_disadvantaged_pct">RWM Expected % (Disadvantaged)</option>
|
|
<option value="rwm_expected_non_disadvantaged_pct">RWM Expected % (Non-Disadvantaged)</option>
|
|
</optgroup>
|
|
<optgroup label="3-Year Trends">
|
|
<option value="rwm_expected_3yr_pct">RWM Expected % (3-Year Avg)</option>
|
|
</optgroup>
|
|
</select>
|
|
<select id="ranking-year" class="filter-select">
|
|
<!-- Populated by JS -->
|
|
</select>
|
|
</div>
|
|
|
|
<div class="rankings-list" id="rankings-list">
|
|
<!-- Rankings populated by JS -->
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- School Detail Modal -->
|
|
<div class="modal" id="school-modal">
|
|
<div class="modal-backdrop"></div>
|
|
<div class="modal-content">
|
|
<button class="modal-close" id="modal-close">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M18 6L6 18M6 6l12 12"/>
|
|
</svg>
|
|
</button>
|
|
<div class="modal-header">
|
|
<h2 id="modal-school-name"></h2>
|
|
<div class="modal-meta" id="modal-meta"></div>
|
|
<div class="modal-details" id="modal-details"></div>
|
|
<button class="btn btn-primary" id="add-to-compare">Add to Compare</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="modal-chart-container">
|
|
<canvas id="school-detail-chart"></canvas>
|
|
</div>
|
|
<div class="modal-stats" id="modal-stats"></div>
|
|
<div class="modal-map-container" id="modal-map-container">
|
|
<h4>Location</h4>
|
|
<div class="modal-map" id="modal-map"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="footer">
|
|
<p>Data source: <a href="https://www.compare-school-performance.service.gov.uk/" target="_blank">UK Government - Compare School Performance</a></p>
|
|
<p class="footer-note">Primary school (KS2) data for England. Data from 2019-2020, 2020-2021, 2021-2022 unavailable due to COVID-19 disruption.</p>
|
|
</footer>
|
|
|
|
<script src="/static/app.js"></script>
|
|
|
|
<!-- Google Analytics (loaded conditionally after consent) -->
|
|
<script>
|
|
var GA_MEASUREMENT_ID = null;
|
|
var analyticsConsentGiven = false;
|
|
|
|
function loadGoogleAnalytics() {
|
|
if (window.gaLoaded || !GA_MEASUREMENT_ID) return;
|
|
window.gaLoaded = true;
|
|
|
|
// Load gtag.js script
|
|
var script = document.createElement('script');
|
|
script.async = true;
|
|
script.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
|
|
document.head.appendChild(script);
|
|
|
|
// Initialize dataLayer and gtag function
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
window.gtag = gtag;
|
|
gtag('js', new Date());
|
|
gtag('config', GA_MEASUREMENT_ID);
|
|
}
|
|
|
|
// Fetch GA ID from server config, then load GA if consent already given
|
|
fetch('/api/config')
|
|
.then(function(response) { return response.json(); })
|
|
.then(function(config) {
|
|
if (config.ga_measurement_id) {
|
|
GA_MEASUREMENT_ID = config.ga_measurement_id;
|
|
// If consent was already given before config loaded, load GA now
|
|
if (analyticsConsentGiven) {
|
|
loadGoogleAnalytics();
|
|
}
|
|
}
|
|
})
|
|
.catch(function(err) { console.warn('Failed to load config:', err); });
|
|
</script>
|
|
|
|
<!-- Cookie Consent Banner -->
|
|
<script src="https://cdn.jsdelivr.net/gh/silktide/consent-manager@main/silktide-consent-manager.js"></script>
|
|
<script>
|
|
window.silktideConsentManager.init({
|
|
consentTypes: [
|
|
{
|
|
id: 'necessary',
|
|
label: 'Necessary',
|
|
description: 'Essential cookies required for the website to function properly.',
|
|
required: true,
|
|
defaultValue: true
|
|
},
|
|
{
|
|
id: 'analytics',
|
|
label: 'Analytics',
|
|
description: 'Help us understand how visitors use our site so we can improve it.',
|
|
required: false,
|
|
defaultValue: false
|
|
}
|
|
],
|
|
text: {
|
|
title: 'Cookie Preferences',
|
|
description: 'We use cookies to improve your experience. Analytics cookies help us understand how you use the site.',
|
|
acceptAll: 'Accept All',
|
|
rejectAll: 'Reject All',
|
|
save: 'Save Preferences'
|
|
},
|
|
onConsentChange: function(consent) {
|
|
if (consent.analytics) {
|
|
analyticsConsentGiven = true;
|
|
loadGoogleAnalytics();
|
|
}
|
|
}
|
|
});
|
|
|
|
// Check existing consent state after initialization
|
|
(function() {
|
|
var manager = window.silktideConsentManager.getInstance();
|
|
if (manager) {
|
|
var analyticsConsent = manager.getConsentChoice('analytics');
|
|
if (analyticsConsent === true) {
|
|
analyticsConsentGiven = true;
|
|
loadGoogleAnalytics();
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|