diff --git a/backend/app.py b/backend/app.py index a41617c..2a2e043 100644 --- a/backend/app.py +++ b/backend/app.py @@ -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", diff --git a/frontend/app.js b/frontend/app.js index 01b30b2..2d2b5d6 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -519,9 +519,16 @@ function renderFeaturedSchools(schools) { ${ageRange ? `
${ageRange}
` : ""}
-
${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
+
+ ${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")} + ${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)} +
RWM Expected
+
+
${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}
+
RWM Higher
+
${school.total_pupils || "-"}
Pupils
@@ -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 ``; + } else if (diff <= -threshold) { + return ``; + } else { + return ``; + } +} + // ============================================================================= // MAP FUNCTIONS // ============================================================================= @@ -806,9 +835,16 @@ function renderSchools(schools) { ${ageRange ? `
${ageRange}
` : ""}
-
${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
+
+ ${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")} + ${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)} +
RWM Expected
+
+
${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}
+
RWM Higher
+
${school.total_pupils || "-"}
Pupils
@@ -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 = `