|
| 1 | +<?xml version="1.0" encoding="UTF-8"?> |
| 2 | +<databaseChangeLog |
| 3 | +xmlns="http://www.liquibase.org/xml/ns/dbchangelog" |
| 4 | +xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 5 | +xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog |
| 6 | + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> |
| 7 | + |
| 8 | +<!-- |
| 9 | + v1.52 - Statistical Enhancements for Election Cycle Views |
| 10 | + Strategy: Wrap v1.51 base views with window functions |
| 11 | + Author: Intelligence Operative & Advanced Analytics |
| 12 | + Date: 2026-01-14 |
| 13 | +--> |
| 14 | + |
| 15 | +<changeSet id="1.52-intro" author="intelligence-operative-analytics"> |
| 16 | + <comment>v1.52 Statistical Enhancements - Wraps v1.51 views with advanced window functions</comment> |
| 17 | + <sql> |
| 18 | + SELECT |
| 19 | + 'v1.52-statistical-enhancements' AS version, |
| 20 | + 'Wraps v1.51 base views with window functions (RANK, PERCENT_RANK, NTILE, LAG, LEAD, STDDEV_POP)' AS description, |
| 21 | + '+67 analytical columns across 6 views' AS scope, |
| 22 | + CURRENT_TIMESTAMP AS applied_at; |
| 23 | + </sql> |
| 24 | +</changeSet> |
| 25 | + |
| 26 | + |
| 27 | +<!-- VIEW 1: TEMPORAL TRENDS WITH STATISTICAL ENHANCEMENTS --> |
| 28 | +<changeSet id="1.52-drop-temporal-view" author="intelligence-operative-analytics" failOnError="false"> |
| 29 | + <comment>Drop v1.51 temporal trends view for v1.52 enhancement</comment> |
| 30 | + <sql>DROP VIEW IF EXISTS view_election_cycle_temporal_trends CASCADE;</sql> |
| 31 | +</changeSet> |
| 32 | + |
| 33 | +<changeSet id="1.52-temporal-enhanced" author="intelligence-operative-analytics" failOnError="true"> |
| 34 | + <comment> |
| 35 | + v1.52 Enhanced: view_election_cycle_temporal_trends |
| 36 | + Wraps v1.51 base with window functions: RANK, PERCENT_RANK, NTILE, LAG, LEAD, STDDEV_POP |
| 37 | + +15 statistical columns for trend analysis |
| 38 | + </comment> |
| 39 | + <createView viewName="view_election_cycle_temporal_trends"> |
| 40 | + <![CDATA[ |
| 41 | +WITH v151_base AS ( |
| 42 | +WITH election_cycle_calendar AS ( |
| 43 | + SELECT |
| 44 | + year_series AS calendar_year, |
| 45 | + (1994 + (FLOOR((year_series - 1994) / 4.0) * 4))::INTEGER AS cycle_start_year, |
| 46 | + (1994 + (FLOOR((year_series - 1994) / 4.0) * 4) + 4)::INTEGER AS cycle_end_year |
| 47 | + FROM generate_series(1994, EXTRACT(YEAR FROM CURRENT_DATE)::INTEGER + 4, 1) AS year_series |
| 48 | +), |
| 49 | +election_cycle_periods AS ( |
| 50 | + SELECT |
| 51 | + calendar_year, |
| 52 | + cycle_start_year, |
| 53 | + cycle_end_year, |
| 54 | + (cycle_start_year || '-' || cycle_end_year) AS election_cycle_id, |
| 55 | + (calendar_year - cycle_start_year + 1) AS cycle_year, |
| 56 | + MAKE_DATE(calendar_year, 9, 1) AS autumn_start, |
| 57 | + MAKE_DATE(calendar_year + 1, 1, 25) AS autumn_end, |
| 58 | + MAKE_DATE(calendar_year, 1, 26) AS spring_start, |
| 59 | + MAKE_DATE(calendar_year, 8, 31) AS spring_end, |
| 60 | + CASE WHEN (calendar_year - cycle_start_year + 1) = 4 THEN TRUE ELSE FALSE END AS is_election_year |
| 61 | + FROM election_cycle_calendar |
| 62 | +), |
| 63 | +temporal_data_autumn AS ( |
| 64 | + SELECT |
| 65 | + ecp.election_cycle_id, |
| 66 | + ecp.cycle_year, |
| 67 | + ecp.calendar_year, |
| 68 | + 'autumn' AS semester, |
| 69 | + FALSE AS is_pre_election_semester, |
| 70 | + EXTRACT(YEAR FROM AGE(MAKE_DATE(ecp.cycle_end_year, 9, 15), ecp.autumn_start))::INTEGER * 12 + |
| 71 | + EXTRACT(MONTH FROM AGE(MAKE_DATE(ecp.cycle_end_year, 9, 15), ecp.autumn_start))::INTEGER AS months_until_election, |
| 72 | + |
| 73 | + -- From view_politician_behavioral_trends |
| 74 | + COUNT(DISTINCT pbt.person_id) AS active_politicians, |
| 75 | + ROUND(AVG(100 - pbt.avg_absence_rate), 2) AS avg_attendance_rate, |
| 76 | + SUM(pbt.total_ballots) AS total_ballots, |
| 77 | + SUM(pbt.total_votes) AS total_votes, |
| 78 | + ROUND(AVG(pbt.avg_win_rate), 2) AS avg_win_rate, |
| 79 | + ROUND(AVG(pbt.avg_rebel_rate), 2) AS avg_rebel_rate, |
| 80 | + SUM(pbt.violation_count) AS violation_count, |
| 81 | + ROUND(AVG(pbt.ma_3month_absence), 2) AS avg_ma_absence, |
| 82 | + |
| 83 | + -- From view_decision_temporal_trends (NEW) |
| 84 | + ROUND(AVG(dtt.daily_approval_rate), 2) AS avg_approval_rate, |
| 85 | + SUM(dtt.daily_decisions) AS total_decisions, |
| 86 | + |
| 87 | + -- From view_committee_productivity (NEW) |
| 88 | + ROUND(AVG(cp.productivity_score), 2) AS avg_committee_productivity |
| 89 | + |
| 90 | + FROM election_cycle_periods ecp |
| 91 | + LEFT JOIN view_politician_behavioral_trends pbt |
| 92 | + ON pbt.period_start >= ecp.autumn_start AND pbt.period_start <= ecp.autumn_end |
| 93 | + LEFT JOIN view_decision_temporal_trends dtt |
| 94 | + ON dtt.decision_day >= ecp.autumn_start AND dtt.decision_day <= ecp.autumn_end |
| 95 | + LEFT JOIN view_committee_productivity cp |
| 96 | + ON 1=1 -- Current snapshot, no temporal filtering available |
| 97 | + GROUP BY ecp.election_cycle_id, ecp.cycle_year, ecp.calendar_year, ecp.autumn_start, ecp.cycle_end_year |
| 98 | +), |
| 99 | +temporal_data_spring AS ( |
| 100 | + SELECT |
| 101 | + ecp.election_cycle_id, |
| 102 | + ecp.cycle_year, |
| 103 | + ecp.calendar_year, |
| 104 | + 'spring' AS semester, |
| 105 | + CASE WHEN ecp.is_election_year THEN TRUE ELSE FALSE END AS is_pre_election_semester, |
| 106 | + EXTRACT(YEAR FROM AGE(MAKE_DATE(ecp.cycle_end_year, 9, 15), ecp.spring_start))::INTEGER * 12 + |
| 107 | + EXTRACT(MONTH FROM AGE(MAKE_DATE(ecp.cycle_end_year, 9, 15), ecp.spring_start))::INTEGER AS months_until_election, |
| 108 | + |
| 109 | + COUNT(DISTINCT pbt.person_id) AS active_politicians, |
| 110 | + ROUND(AVG(100 - pbt.avg_absence_rate), 2) AS avg_attendance_rate, |
| 111 | + SUM(pbt.total_ballots) AS total_ballots, |
| 112 | + SUM(pbt.total_votes) AS total_votes, |
| 113 | + ROUND(AVG(pbt.avg_win_rate), 2) AS avg_win_rate, |
| 114 | + ROUND(AVG(pbt.avg_rebel_rate), 2) AS avg_rebel_rate, |
| 115 | + SUM(pbt.violation_count) AS violation_count, |
| 116 | + ROUND(AVG(pbt.ma_3month_absence), 2) AS avg_ma_absence, |
| 117 | + ROUND(AVG(dtt.daily_approval_rate), 2) AS avg_approval_rate, |
| 118 | + SUM(dtt.daily_decisions) AS total_decisions, |
| 119 | + ROUND(AVG(cp.productivity_score), 2) AS avg_committee_productivity |
| 120 | + |
| 121 | + FROM election_cycle_periods ecp |
| 122 | + LEFT JOIN view_politician_behavioral_trends pbt |
| 123 | + ON pbt.period_start >= ecp.spring_start AND pbt.period_start <= ecp.spring_end |
| 124 | + LEFT JOIN view_decision_temporal_trends dtt |
| 125 | + ON dtt.decision_day >= ecp.spring_start AND dtt.decision_day <= ecp.spring_end |
| 126 | + LEFT JOIN view_committee_productivity cp |
| 127 | + ON 1=1 -- Current snapshot, no temporal filtering available |
| 128 | + GROUP BY ecp.election_cycle_id, ecp.cycle_year, ecp.calendar_year, ecp.spring_start, ecp.cycle_end_year, ecp.is_election_year |
| 129 | +) |
| 130 | +SELECT * FROM temporal_data_autumn |
| 131 | +UNION ALL |
| 132 | +SELECT * FROM temporal_data_spring |
| 133 | +ORDER BY election_cycle_id, cycle_year, semester; |
| 134 | +), |
| 135 | +windowed_metrics AS ( |
| 136 | + SELECT v151.*, |
| 137 | + RANK() OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.avg_attendance_rate DESC NULLS LAST) AS rank_by_attendance, |
| 138 | + PERCENT_RANK() OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.avg_attendance_rate DESC NULLS LAST) AS percent_rank_attendance, |
| 139 | + NTILE(4) OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.avg_attendance_rate DESC NULLS LAST) AS ntile_performance, |
| 140 | + LAG(v151.avg_attendance_rate) OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.cycle_year, v151.semester) AS prev_semester_attendance, |
| 141 | + LAG(v151.total_decisions) OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.cycle_year, v151.semester) AS prev_semester_decisions, |
| 142 | + LEAD(v151.avg_attendance_rate) OVER (PARTITION BY v151.election_cycle_id ORDER BY v151.cycle_year, v151.semester) AS next_semester_attendance, |
| 143 | + STDDEV_POP(v151.avg_attendance_rate) OVER (PARTITION BY v151.election_cycle_id) AS stddev_attendance, |
| 144 | + STDDEV_POP(v151.avg_win_rate) OVER (PARTITION BY v151.election_cycle_id) AS stddev_win_rate, |
| 145 | + STDDEV_POP(v151.total_decisions) OVER (PARTITION BY v151.election_cycle_id) AS stddev_decisions |
| 146 | + FROM v151_base v151 |
| 147 | +) |
| 148 | +SELECT |
| 149 | + wm.*, |
| 150 | + CASE |
| 151 | + WHEN wm.prev_semester_attendance IS NOT NULL AND wm.prev_semester_attendance > 0 |
| 152 | + THEN ROUND(((wm.avg_attendance_rate - wm.prev_semester_attendance) / wm.prev_semester_attendance * 100)::NUMERIC, 2) |
| 153 | + ELSE NULL |
| 154 | + END AS change_attendance_pct, |
| 155 | + CASE |
| 156 | + WHEN wm.prev_semester_decisions IS NOT NULL AND wm.prev_semester_decisions > 0 |
| 157 | + THEN ROUND(((wm.total_decisions - wm.prev_semester_decisions)::NUMERIC / wm.prev_semester_decisions * 100)::NUMERIC, 2) |
| 158 | + ELSE NULL |
| 159 | + END AS change_decisions_pct, |
| 160 | + CASE |
| 161 | + WHEN wm.prev_semester_attendance IS NULL THEN 'baseline' |
| 162 | + WHEN wm.avg_attendance_rate > wm.prev_semester_attendance + 5 THEN 'improving' |
| 163 | + WHEN wm.avg_attendance_rate < wm.prev_semester_attendance - 5 THEN 'declining' |
| 164 | + ELSE 'stable' |
| 165 | + END AS attendance_trend, |
| 166 | + CASE |
| 167 | + WHEN wm.avg_attendance_rate IS NOT NULL AND wm.avg_win_rate IS NOT NULL |
| 168 | + THEN ROUND((wm.avg_attendance_rate * 0.5 + wm.avg_win_rate * 0.5)::NUMERIC, 2) |
| 169 | + ELSE NULL |
| 170 | + END AS overall_performance_score, |
| 171 | + CASE |
| 172 | + WHEN wm.stddev_attendance > 10 THEN 'high_volatility' |
| 173 | + WHEN wm.stddev_attendance > 5 THEN 'moderate_volatility' |
| 174 | + ELSE 'stable' |
| 175 | + END AS volatility_assessment, |
| 176 | + CASE |
| 177 | + WHEN wm.next_semester_attendance IS NOT NULL AND wm.next_semester_attendance > wm.avg_attendance_rate + 5 THEN 'expected_improvement' |
| 178 | + WHEN wm.next_semester_attendance IS NOT NULL AND wm.next_semester_attendance < wm.avg_attendance_rate - 5 THEN 'expected_decline' |
| 179 | + ELSE 'stable_forecast' |
| 180 | + END AS forecast_trend |
| 181 | +FROM windowed_metrics wm |
| 182 | +ORDER BY wm.election_cycle_id, wm.cycle_year, wm.semester; |
| 183 | + ]]> |
| 184 | + </createView> |
| 185 | + <rollback> |
| 186 | + <dropView viewName="view_election_cycle_temporal_trends"/> |
| 187 | + </rollback> |
| 188 | +</changeSet> |
| 189 | + |
| 190 | + |
| 191 | +</databaseChangeLog> |
0 commit comments