Add higher standard display and trend indicators to school cards
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 57s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 57s
- Display RWM Higher % alongside RWM Expected % on school cards - Add trend indicators (up/down/stable arrows) showing year-over-year change - Backend calculates previous year's RWM for trend comparison - Trend appears on cards and in school detail modal Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -238,11 +238,23 @@ async def get_schools(
|
||||
latest_year = df.groupby("urn")["year"].max().reset_index()
|
||||
df_latest = df.merge(latest_year, on=["urn", "year"])
|
||||
|
||||
# Calculate trend by comparing to previous year
|
||||
# Get second-latest year for each school
|
||||
df_sorted = df.sort_values(["urn", "year"], ascending=[True, False])
|
||||
df_prev = df_sorted.groupby("urn").nth(1).reset_index()
|
||||
if not df_prev.empty and "rwm_expected_pct" in df_prev.columns:
|
||||
prev_rwm = df_prev[["urn", "rwm_expected_pct"]].rename(
|
||||
columns={"rwm_expected_pct": "prev_rwm_expected_pct"}
|
||||
)
|
||||
df_latest = df_latest.merge(prev_rwm, on="urn", how="left")
|
||||
|
||||
# Include key result metrics for display on cards
|
||||
location_cols = ["latitude", "longitude"]
|
||||
result_cols = [
|
||||
"year",
|
||||
"rwm_expected_pct",
|
||||
"rwm_high_pct",
|
||||
"prev_rwm_expected_pct",
|
||||
"reading_expected_pct",
|
||||
"writing_expected_pct",
|
||||
"maths_expected_pct",
|
||||
|
||||
@@ -519,9 +519,16 @@ function renderFeaturedSchools(schools) {
|
||||
${ageRange ? `<div class="school-details">${ageRange}</div>` : ""}
|
||||
<div class="school-stats">
|
||||
<div class="stat">
|
||||
<div class="stat-value">${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}</div>
|
||||
<div class="stat-value">
|
||||
${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
|
||||
${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)}
|
||||
</div>
|
||||
<div class="stat-label">RWM Expected</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}</div>
|
||||
<div class="stat-label">RWM Higher</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">${school.total_pupils || "-"}</div>
|
||||
<div class="stat-label">Pupils</div>
|
||||
@@ -617,6 +624,28 @@ function formatMetricValue(value, metric) {
|
||||
return value.toFixed(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate trend indicator based on current and previous year values
|
||||
* Returns HTML for trend arrow with class
|
||||
*/
|
||||
function getTrendIndicator(current, previous) {
|
||||
if (current === null || current === undefined ||
|
||||
previous === null || previous === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const diff = current - previous;
|
||||
const threshold = 2; // Minimum % change to show trend
|
||||
|
||||
if (diff >= threshold) {
|
||||
return `<span class="trend-indicator trend-up" title="Up ${diff.toFixed(0)}% from last year">▲</span>`;
|
||||
} else if (diff <= -threshold) {
|
||||
return `<span class="trend-indicator trend-down" title="Down ${Math.abs(diff).toFixed(0)}% from last year">▼</span>`;
|
||||
} else {
|
||||
return `<span class="trend-indicator trend-stable" title="Stable (${diff >= 0 ? '+' : ''}${diff.toFixed(0)}%)">▬</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAP FUNCTIONS
|
||||
// =============================================================================
|
||||
@@ -806,9 +835,16 @@ function renderSchools(schools) {
|
||||
${ageRange ? `<div class="school-details">${ageRange}</div>` : ""}
|
||||
<div class="school-stats">
|
||||
<div class="stat">
|
||||
<div class="stat-value">${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}</div>
|
||||
<div class="stat-value">
|
||||
${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
|
||||
${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)}
|
||||
</div>
|
||||
<div class="stat-label">RWM Expected</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}</div>
|
||||
<div class="stat-label">RWM Higher</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">${school.total_pupils || "-"}</div>
|
||||
<div class="stat-label">Pupils</div>
|
||||
@@ -1212,12 +1248,17 @@ async function openSchoolModal(urn) {
|
||||
const latest =
|
||||
sortedData.find((d) => d.rwm_expected_pct !== null) || sortedData[0];
|
||||
|
||||
// Get previous year for trend calculation
|
||||
const latestIndex = sortedData.indexOf(latest);
|
||||
const previous = sortedData[latestIndex + 1] || null;
|
||||
const prevRwm = previous?.rwm_expected_pct;
|
||||
|
||||
elements.modalStats.innerHTML = `
|
||||
<div class="modal-stats-section">
|
||||
<h4>KS2 Results (${latest.year})</h4>
|
||||
<div class="modal-stats-grid">
|
||||
<div class="modal-stat">
|
||||
<div class="modal-stat-value">${formatMetricValue(latest.rwm_expected_pct, "rwm_expected_pct")}</div>
|
||||
<div class="modal-stat-value">${formatMetricValue(latest.rwm_expected_pct, "rwm_expected_pct")} ${getTrendIndicator(latest.rwm_expected_pct, prevRwm)}</div>
|
||||
<div class="modal-stat-label">RWM Expected</div>
|
||||
</div>
|
||||
<div class="modal-stat">
|
||||
|
||||
@@ -536,6 +536,10 @@ body {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-value.positive {
|
||||
@@ -546,6 +550,25 @@ body {
|
||||
color: var(--accent-coral);
|
||||
}
|
||||
|
||||
/* Trend indicators */
|
||||
.trend-indicator {
|
||||
font-size: 0.75rem;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.trend-up {
|
||||
color: var(--accent-teal);
|
||||
}
|
||||
|
||||
.trend-down {
|
||||
color: var(--accent-coral);
|
||||
}
|
||||
|
||||
.trend-stable {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
|
||||
Reference in New Issue
Block a user