Skip to content

Commit 4a9c51d

Browse files
committed
Cleanup stuff
1 parent e6bc2ee commit 4a9c51d

5 files changed

Lines changed: 259 additions & 120 deletions

File tree

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,22 @@
1-
-- Create materialized view for pre-calculating majority party votes on motions
1+
-- Create materialized view for party votes on motions (simplified approach)
2+
-- This directly looks at stemmingen (votes) to determine party positions
23
CREATE MATERIALIZED VIEW majority_party_votes AS
3-
WITH PartyMotionVotes AS (
4-
SELECT
5-
b.zaak_id,
6-
z.gestart_op,
7-
f.id as fractie_id,
8-
s.soort AS vote_type,
9-
COUNT(*) AS vote_count
10-
FROM stemmingen s
11-
JOIN besluiten b ON s.besluit_id = b.id
12-
JOIN zaken z ON b.zaak_id = z.id
13-
JOIN fracties f ON s.actor_fractie = f.naam_nl OR s.actor_fractie = f.afkorting
14-
WHERE s.actor_fractie IS NOT NULL
15-
AND s.soort IN ('Voor', 'Tegen')
16-
AND z.soort = 'Motie'
17-
GROUP BY b.zaak_id, z.gestart_op, f.id, s.soort
18-
)
19-
SELECT
20-
zaak_id,
21-
gestart_op,
22-
fractie_id,
23-
vote_type
24-
FROM (
25-
SELECT
26-
zaak_id,
27-
gestart_op,
28-
fractie_id,
29-
vote_type,
30-
ROW_NUMBER() OVER(PARTITION BY zaak_id, fractie_id ORDER BY vote_count DESC, vote_type ASC) as rn
31-
FROM PartyMotionVotes
32-
) ranked_votes
33-
WHERE rn = 1;
4+
SELECT DISTINCT
5+
b.zaak_id,
6+
z.gestart_op,
7+
f.id as fractie_id,
8+
s.soort AS vote_type
9+
FROM stemmingen s
10+
JOIN besluiten b ON s.besluit_id = b.id
11+
JOIN zaken z ON b.zaak_id = z.id
12+
JOIN fracties f ON (s.actor_fractie = f.naam_nl OR s.actor_fractie = f.afkorting)
13+
WHERE s.actor_fractie IS NOT NULL
14+
AND s.soort IN ('Voor', 'Tegen')
15+
AND z.soort = 'Motie'
16+
AND f.datum_inactief IS NULL;
3417

3518
-- Create indexes to speed up queries on the materialized view
3619
CREATE INDEX idx_majority_party_votes_zaak_id ON majority_party_votes(zaak_id);
3720
CREATE INDEX idx_majority_party_votes_fractie_id ON majority_party_votes(fractie_id);
3821
CREATE INDEX idx_majority_party_votes_gestart_op ON majority_party_votes(gestart_op);
22+
CREATE INDEX idx_majority_party_votes_vote_type ON majority_party_votes(vote_type);
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
-- This view depends on the 'majority_party_votes' materialized view.
2-
-- It should be refreshed after 'majority_party_votes' is refreshed.
1+
-- Create materialized view for party likeness per motion
2+
-- This depends on the 'majority_party_votes' materialized view
33
CREATE MATERIALIZED VIEW party_likeness_per_motion AS
44
SELECT
55
mv1.fractie_id as fractie1_id,
@@ -8,8 +8,12 @@ SELECT
88
mv1.gestart_op,
99
(mv1.vote_type = mv2.vote_type) as same_vote
1010
FROM majority_party_votes mv1
11-
JOIN majority_party_votes mv2 ON mv1.zaak_id = mv2.zaak_id AND mv1.fractie_id < mv2.fractie_id;
11+
JOIN majority_party_votes mv2 ON mv1.zaak_id = mv2.zaak_id
12+
WHERE mv1.fractie_id < mv2.fractie_id;
1213

14+
-- Create indexes to speed up queries on the materialized view
1315
CREATE INDEX idx_plpm_gestart_op ON party_likeness_per_motion(gestart_op);
1416
CREATE INDEX idx_plpm_fractie1_id ON party_likeness_per_motion(fractie1_id);
1517
CREATE INDEX idx_plpm_fractie2_id ON party_likeness_per_motion(fractie2_id);
18+
CREATE INDEX idx_plpm_zaak_id ON party_likeness_per_motion(zaak_id);
19+
CREATE INDEX idx_plpm_same_vote ON party_likeness_per_motion(same_vote);

app/backend/src/routers/statistics.ts

Lines changed: 133 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,72 @@ export const statisticsRouter = {
1616
async ({ input }) => {
1717
const { dateFrom, dateTo } = input || {};
1818

19-
const partyFilter = Prisma.sql`
20-
p1.datum_inactief IS NULL
21-
AND p2.datum_inactief IS NULL
22-
`;
23-
2419
const dateFilter =
2520
dateFrom && dateTo
26-
? Prisma.sql`AND pl.gestart_op BETWEEN ${dateFrom} AND ${dateTo}`
21+
? Prisma.sql`AND z.gestart_op BETWEEN ${dateFrom} AND ${dateTo}`
2722
: Prisma.empty;
2823

24+
// Simplified approach: directly compare votes between parties
25+
// Based on the PHP implementation which looks at all stemmingen
2926
const results: PartyLikeness[] = await db.$queryRaw`
30-
SELECT
31-
p1.id AS "party1Id",
32-
p1.afkorting AS "party1Name",
33-
p2.id AS "party2Id",
34-
p2.afkorting AS "party2Name",
35-
COUNT(*) AS "commonMotions",
36-
SUM(CASE WHEN pl.same_vote THEN 1 ELSE 0 END) AS "sameVotes",
37-
(SUM(CASE WHEN pl.same_vote THEN 1 ELSE 0 END)::float / NULLIF(COUNT(*), 0)::float) * 100 AS "likenessPercentage"
38-
FROM party_likeness_per_motion pl
39-
JOIN fracties p1 ON pl.fractie1_id = p1.id
40-
JOIN fracties p2 ON pl.fractie2_id = p2.id
41-
WHERE ${partyFilter} ${dateFilter}
42-
GROUP BY p1.id, p1.afkorting, p2.id, p2.afkorting
43-
ORDER BY p1.afkorting, "likenessPercentage" DESC;
44-
`;
27+
WITH PartyVotes AS (
28+
SELECT DISTINCT
29+
b.zaak_id,
30+
f.id as fractie_id,
31+
f.afkorting as fractie_name,
32+
s.soort as vote_type
33+
FROM stemmingen s
34+
JOIN besluiten b ON s.besluit_id = b.id
35+
JOIN zaken z ON b.zaak_id = z.id
36+
JOIN fracties f ON (s.actor_fractie = f.naam_nl OR s.actor_fractie = f.afkorting)
37+
WHERE s.actor_fractie IS NOT NULL
38+
AND s.soort IN ('Voor', 'Tegen')
39+
AND z.soort = 'Motie'
40+
AND f.datum_inactief IS NULL
41+
${dateFilter}
42+
),
43+
PartyComparisons AS (
44+
SELECT
45+
p1.fractie_id as party1_id,
46+
p1.fractie_name as party1_name,
47+
p2.fractie_id as party2_id,
48+
p2.fractie_name as party2_name,
49+
COUNT(*) as common_motions,
50+
SUM(CASE WHEN p1.vote_type = p2.vote_type THEN 1 ELSE 0 END) as same_votes
51+
FROM PartyVotes p1
52+
JOIN PartyVotes p2 ON p1.zaak_id = p2.zaak_id AND p1.fractie_id < p2.fractie_id
53+
GROUP BY p1.fractie_id, p1.fractie_name, p2.fractie_id, p2.fractie_name
54+
)
55+
SELECT
56+
party1_id AS "party1Id",
57+
party1_name AS "party1Name",
58+
party2_id AS "party2Id",
59+
party2_name AS "party2Name",
60+
common_motions AS "commonMotions",
61+
same_votes AS "sameVotes",
62+
CASE
63+
WHEN common_motions > 0
64+
THEN (same_votes::float / common_motions::float) * 100
65+
ELSE 0
66+
END AS "likenessPercentage"
67+
FROM PartyComparisons
68+
ORDER BY party1_name, "likenessPercentage" DESC;
69+
`;
4570

46-
return results.map((r) => ({
71+
// Also get the reverse relationships (party2 to party1)
72+
const reverseResults: PartyLikeness[] = results.map((r) => ({
73+
party1Id: r.party2Id,
74+
party1Name: r.party2Name,
75+
party2Id: r.party1Id,
76+
party2Name: r.party1Name,
77+
commonMotions: r.commonMotions,
78+
sameVotes: r.sameVotes,
79+
likenessPercentage: r.likenessPercentage,
80+
}));
81+
82+
const allResults = [...results, ...reverseResults];
83+
84+
return allResults.map((r) => ({
4785
...r,
4886
commonMotions: Number(r.commonMotions),
4987
sameVotes: Number(r.sameVotes),
@@ -71,31 +109,31 @@ export const statisticsRouter = {
71109
: Prisma.empty;
72110

73111
const results: PartyFocusCategory[] = await db.$queryRaw`
74-
SELECT
75-
mc.id AS "categoryId",
76-
mc.name AS "categoryName",
77-
mc.type AS "categoryType",
78-
COUNT(z.id) AS "motionCount"
79-
FROM
80-
zaak_actors za
81-
JOIN
82-
zaken z ON za.zaak_id = z.id
83-
JOIN
84-
fracties f ON za.actor_fractie = f.naam_nl OR za.actor_fractie = f.afkorting
85-
JOIN
86-
zaak_categories zc ON z.id = zc.zaak_id
87-
JOIN
88-
motion_categories mc ON zc.category_id = mc.id
89-
WHERE
90-
f.id = ${partyId}
91-
AND za.relatie = 'Indiener'
92-
AND z.soort = 'Motie'
93-
${dateFilter}
94-
GROUP BY
95-
mc.id, mc.name, mc.type
96-
ORDER BY
97-
"motionCount" DESC;
98-
`;
112+
SELECT
113+
mc.id AS "categoryId",
114+
mc.name AS "categoryName",
115+
mc.type AS "categoryType",
116+
COUNT(z.id) AS "motionCount"
117+
FROM
118+
zaak_actors za
119+
JOIN
120+
zaken z ON za.zaak_id = z.id
121+
JOIN
122+
fracties f ON za.actor_fractie = f.naam_nl OR za.actor_fractie = f.afkorting
123+
JOIN
124+
zaak_categories zc ON z.id = zc.zaak_id
125+
JOIN
126+
motion_categories mc ON zc.category_id = mc.id
127+
WHERE
128+
f.id = ${partyId}
129+
AND za.relatie = 'Indiener'
130+
AND z.soort = 'Motie'
131+
${dateFilter}
132+
GROUP BY
133+
mc.id, mc.name, mc.type
134+
ORDER BY
135+
"motionCount" DESC;
136+
`;
99137

100138
return {
101139
party: mapPartyToContract(party),
@@ -112,45 +150,57 @@ export const statisticsRouter = {
112150

113151
const dateFilter =
114152
dateFrom && dateTo
115-
? Prisma.sql`WHERE gestart_op BETWEEN ${dateFrom} AND ${dateTo}`
153+
? Prisma.sql`AND z.gestart_op BETWEEN ${dateFrom} AND ${dateTo}`
116154
: Prisma.empty;
117155

156+
// Simplified approach: directly analyze votes by category
118157
const results: PartyCategoryLikeness[] = await db.$queryRaw`
119-
WITH MotionMajorityVotes AS (
120-
SELECT * FROM majority_party_votes ${dateFilter}
121-
),
122-
MotionCategoryVotes AS (
123-
SELECT
124-
mmv.zaak_id,
125-
mmv.fractie_id,
126-
mmv.vote_type,
127-
zc.category_id
128-
FROM MotionMajorityVotes mmv
129-
JOIN zaak_categories zc ON mmv.zaak_id = zc.zaak_id
130-
)
131-
SELECT
132-
mc.id AS "categoryId",
133-
mc.name AS "categoryName",
134-
p2.id AS "party2Id",
135-
p2.afkorting AS "party2Name",
136-
(SUM(CASE WHEN mcv1.vote_type = mcv2.vote_type THEN 1 ELSE 0 END)::float / NULLIF(COUNT(mcv2.zaak_id), 0)::float) * 100 AS "likenessPercentage"
137-
FROM
138-
(SELECT * FROM MotionCategoryVotes WHERE fractie_id = ${partyId}) mcv1
139-
JOIN
140-
MotionCategoryVotes mcv2 ON mcv1.zaak_id = mcv2.zaak_id AND mcv1.category_id = mcv2.category_id
141-
JOIN
142-
fracties p2 ON mcv2.fractie_id = p2.id
143-
JOIN
144-
motion_categories mc ON mcv1.category_id = mc.id
145-
WHERE
146-
mcv1.fractie_id != mcv2.fractie_id
147-
AND p2.datum_inactief IS NULL
148-
AND p2.naam_nl NOT IN ('Groep Van Haga', 'Fractie Den Haan', 'Lid Omtzigt', 'Lid Gündoğan')
149-
GROUP BY
150-
mc.id, mc.name, p2.id, p2.afkorting
151-
ORDER BY
152-
mc.name, p2.afkorting;
153-
`;
158+
WITH PartyVotesByCategory AS (
159+
SELECT DISTINCT
160+
b.zaak_id,
161+
f.id as fractie_id,
162+
s.soort as vote_type,
163+
zc.category_id
164+
FROM stemmingen s
165+
JOIN besluiten b ON s.besluit_id = b.id
166+
JOIN zaken z ON b.zaak_id = z.id
167+
JOIN fracties f ON (s.actor_fractie = f.naam_nl OR s.actor_fractie = f.afkorting)
168+
JOIN zaak_categories zc ON z.id = zc.zaak_id
169+
WHERE s.actor_fractie IS NOT NULL
170+
AND s.soort IN ('Voor', 'Tegen')
171+
AND z.soort = 'Motie'
172+
AND f.datum_inactief IS NULL
173+
${dateFilter}
174+
),
175+
CategoryComparisons AS (
176+
SELECT
177+
pv1.category_id,
178+
pv2.fractie_id as other_party_id,
179+
COUNT(*) as total_votes,
180+
SUM(CASE WHEN pv1.vote_type = pv2.vote_type THEN 1 ELSE 0 END) as same_votes
181+
FROM PartyVotesByCategory pv1
182+
JOIN PartyVotesByCategory pv2 ON pv1.zaak_id = pv2.zaak_id
183+
AND pv1.category_id = pv2.category_id
184+
AND pv1.fractie_id != pv2.fractie_id
185+
WHERE pv1.fractie_id = ${partyId}
186+
GROUP BY pv1.category_id, pv2.fractie_id
187+
)
188+
SELECT
189+
mc.id AS "categoryId",
190+
mc.name AS "categoryName",
191+
f.id AS "party2Id",
192+
f.afkorting AS "party2Name",
193+
CASE
194+
WHEN cc.total_votes > 0
195+
THEN (cc.same_votes::float / cc.total_votes::float) * 100
196+
ELSE 0
197+
END AS "likenessPercentage"
198+
FROM CategoryComparisons cc
199+
JOIN motion_categories mc ON cc.category_id = mc.id
200+
JOIN fracties f ON cc.other_party_id = f.id
201+
WHERE f.datum_inactief IS NULL
202+
ORDER BY mc.name, f.afkorting;
203+
`;
154204

155205
return results.map((r) => ({
156206
...r,

0 commit comments

Comments
 (0)