Skip to content
Snippets Groups Projects
Commit 5cd3d4a6 authored by Alexis POYEN's avatar Alexis POYEN
Browse files

Resolve "Calculate the results for direct metropolitan election"

parent 8a5b24b9
No related branches found
No related tags found
No related merge requests found
No preview for this file type
// Imports
import * as results from "/services/election/calculate-election-generic.js";
export async function mount(where, round) {
const resultComponent = new ResultComponent(round);
await resultComponent.mount(where);
await resultComponent.calculateResults();
}
class ResultComponent {
......@@ -29,15 +31,15 @@ class ResultComponent {
</div>
<div class="control filter">
<label class="radio">
<input type="radio" name="answer" checked />
<input type="radio" name="filter" value="partial" checked />
Partiel
</label>
<label class="radio">
<input type="radio" name="answer" />
<input type="radio" name="filter" value="completed" />
Complété
</label>
<label class="radio">
<input type="radio" name="answer" />
<input type="radio" name="filter" value="validated" />
Validé
</label>
</div>
......@@ -100,6 +102,7 @@ class ResultComponent {
`;
this.handleDom();
document.getElementById("areas").click();
this.calculator = await results.mountCalculator(this.round);
}
handleDom() {
......@@ -126,6 +129,13 @@ class ResultComponent {
.addEventListener("click", function () {
resultHandler.zoomResults();
});
let radioButtons = document.getElementsByName("filter");
for (var i = 0; i < radioButtons.length; i++) {
radioButtons[i].addEventListener("click", (e) => {
this.calculateResults();
});
}
}
zoomMap() {
......@@ -152,7 +162,7 @@ class ResultComponent {
});
}
zoomResults(){
zoomResults() {
let resultHandler = this;
document.getElementById("results-section").parentElement.className =
"column is-full";
......@@ -179,4 +189,9 @@ class ResultComponent {
this.handleDom();
}
async calculateResults() {
let filter = document.querySelector('input[name="filter"]:checked').value;
await this.calculator.calculateResults(filter);
}
}
import * as Auth from "/services/auth/auth.js";
import * as ElectionModel from "/services/model/election-model.js";
import * as RoundModel from "/services/model/round-model.js";
import * as AreaModel from "/services/model/area-model.js";
import * as SectionModel from "/services/model/section-model.js";
import * as DeskModel from "/services/model/desk-model.js";
import * as PartyModel from "/services/model/party-model.js";
import * as DeskRoundModel from "/services/model/deskRound-model.js";
import * as VoteModel from "/services/model/vote-model.js";
import * as CandidateListModel from "/services/model/candidateList-model.js";
export async function mountCalculator(round) {
const directMetropolitanCalculator = new DirectMetropolitanCalculator(round);
await directMetropolitanCalculator.initModel();
return directMetropolitanCalculator;
}
class DirectMetropolitanCalculator {
constructor(round, filter) {
this.round = round;
this.ElectionModel = ElectionModel.getElectionModel();
this.RoundModel = RoundModel.getRoundModel();
this.AreaModel = AreaModel.getAreaModel();
this.SectionModel = SectionModel.getSectionModel();
this.DeskModel = DeskModel.getDeskModel();
this.PartyModel = PartyModel.getPartyModel();
this.DeskRoundModel = DeskRoundModel.getDeskRoundModel();
this.VoteModel = VoteModel.getVoteModel();
this.CandidateListModel = CandidateListModel.getCandidateListModel();
}
async initModel() {
this.ElectionModel.current_user = await Auth.GetUser();
this.RoundModel.current_user = await Auth.GetUser();
this.AreaModel.current_user = await Auth.GetUser();
this.SectionModel.current_user = await Auth.GetUser();
this.DeskModel.current_user = await Auth.GetUser();
this.PartyModel.current_user = await Auth.GetUser();
this.DeskRoundModel.current_user = await Auth.GetUser();
this.VoteModel.current_user = await Auth.GetUser();
this.CandidateListModel.current_user = await Auth.GetUser();
}
async calculateResults(filter) {
this.CandidateListModel.refreshCandidateLists();
let calculator = this;
this.filter = filter;
this.deskRounds = await this.DeskRoundModel.getDeskRounds();
this.deskRounds = this.deskRounds.filter(function (deskRound) {
return deskRound.RoundID === calculator.round.ID;
});
this.candidateLists = await this.CandidateListModel.getCandidateLists();
this.candidateLists = this.candidateLists.filter((candidateList) => {
return candidateList.RoundID === calculator.round.ID;
});
this.stats = await this.calculateStats(this.deskRounds);
let flag = true;
switch (this.filter) {
case "partial":
if (this.stats.VotesExpressed === 0) {
this.status = "no_results";
} else {
this.roundResults = await this.calculateRoundResults();
this.status = "partial";
}
break;
case "completed":
flag = true;
this.deskRounds.forEach((deskRound) => {
if (!deskRound.Completed) flag = false;
});
if (flag) {
this.roundResults = await this.calculateRoundResults();
this.stats = await this.calculateStats(this.deskRounds);
this.status = "completed";
} else {
this.roundResults = null;
this.stats = null;
this.status = "incompleted";
}
break;
case "validated":
flag = true;
this.deskRounds.forEach((deskRound) => {
if (!deskRound.Validated) flag = false;
});
if (flag) {
this.roundResults = await this.calculateRoundResults();
this.stats = await this.calculateStats(this.deskRounds);
this.status = "validated";
} else {
this.roundResults = null;
this.stats = null;
this.status = "not validated";
}
}
this.areasResults = await this.calculateAreasResults();
console.log(this);
}
async calculateRoundResults() {
let partiesIDToKeep = [];
this.candidateLists.forEach((candidateList) => {
partiesIDToKeep.push(candidateList.PartyID);
});
partiesIDToKeep = partiesIDToKeep.filter(function (item, index) {
return partiesIDToKeep.indexOf(item) >= index;
});
let parties = await this.PartyModel.getParties();
parties = parties.filter((party) => partiesIDToKeep.includes(party.ID));
parties.forEach((party) => {
party.VoiceNumber = 0;
let currentParty = party;
this.candidateLists.forEach((candidateList) => {
if (candidateList.PartyID == currentParty.ID) {
currentParty.VoiceNumber = candidateList.Votes.reduce(
(voiceNumber, vote) => {
return voiceNumber + vote.VoiceNumber;
},
currentParty.VoiceNumber
);
}
});
party = currentParty;
});
parties.sort((a, b) => {
return b.VoiceNumber - a.VoiceNumber;
});
return parties;
}
async calculateAreasResults() {
let calculator = this;
this.AreaModel.refreshAreas();
let areas = await this.AreaModel.getAreas();
areas = areas.filter(function (area) {
return area.ElectionID == calculator.round.ElectionID;
});
let areasCalculated = [];
for (let i in areas) {
areasCalculated.push(await this.calculateAreaResults(areas[i]));
}
areas = areasCalculated;
return areas;
}
async calculateAreaResults(area) {
let deskRounds = [];
for (let i in area.Sections) {
let section = await this.SectionModel.getSection(area.Sections[i].ID);
for (let j in section.Desks) {
for (let k in this.deskRounds) {
if (section.Desks[j].ID == this.deskRounds[k].DeskID)
deskRounds.push(this.deskRounds[k]);
}
}
}
let candidateListToKeep = this.candidateLists.filter((candidateList) => {
return candidateList.AreaID === area.ID;
});
let sections = [];
for (let i in area.Sections) {
sections.push(
await this.calculateSectionResults(
area.Sections[i],
candidateListToKeep.map((a) => ({ ...a }))
)
);
}
area.Sections = sections;
area.stats = await this.calculateStats(deskRounds);
let flag = true;
switch (this.filter) {
case "partial":
if (area.stats.VotesExpressed === 0) {
area.status = "no_results";
return;
}
area.status = "partial";
break;
case "completed":
flag = true;
deskRounds.forEach((deskRound) => {
if (!deskRound.Completed) flag = false;
});
if (flag) {
area.status = "completed";
} else {
area.status = "incompleted";
return;
}
break;
case "validated":
flag = true;
deskRounds.forEach((deskRound) => {
if (!deskRound.Validated) flag = false;
});
if (flag) {
area.status = "validated";
} else {
area.status = "not validated";
return;
}
}
candidateListToKeep.forEach((candidateList) => {
candidateList.VoiceNumber = candidateList.Votes.reduce(
(voiceNumber, vote) => {
return voiceNumber + vote.VoiceNumber;
},
0
);
candidateList.Percentage =
(candidateList.VoiceNumber / area.stats.VotesExpressed) * 100;
});
area.candidateLists = candidateListToKeep;
area.candidateLists.sort((a, b) => {
return b.VoiceNumber - a.VoiceNumber;
});
area.Electeds = this.getElecteds(area);
area.candidateLists.sort((a, b) => {
return b.VoiceNumber - a.VoiceNumber;
});
return area;
}
async calculateSectionResults(section, candidateLists) {
section = await this.SectionModel.getSection(section.ID);
let deskRounds = [];
for (let i in section.Desks) {
for (let j in this.deskRounds) {
if (section.Desks[i].ID == this.deskRounds[j].DeskID)
deskRounds.push(this.deskRounds[j]);
}
}
section.stats = await this.calculateStats(deskRounds);
let flag = true;
switch (this.filter) {
case "partial":
if (section.stats.VotesExpressed === 0) {
section.status = "no_results";
return;
}
section.status = "partial";
break;
case "completed":
flag = true;
deskRounds.forEach((deskRound) => {
if (!deskRound.Completed) flag = false;
});
if (flag) {
section.status = "completed";
} else {
section.status = "incompleted";
return;
}
break;
case "validated":
flag = true;
deskRounds.forEach((deskRound) => {
if (!deskRound.Validated) flag = false;
});
if (flag) {
section.status = "validated";
} else {
section.status = "not validated";
return;
}
}
candidateLists.forEach((candidateList) => {
candidateList.Votes = candidateList.Votes.filter((vote) => {
return deskRounds
.map((deskRound) => deskRound.ID)
.includes(vote.DeskRoundID);
});
candidateList.VoiceNumber = candidateList.Votes.reduce(
(voiceNumber, vote) => {
return voiceNumber + vote.VoiceNumber;
},
0
);
candidateList.Percentage =
(candidateList.VoiceNumber / section.stats.VotesExpressed) * 100;
});
section.candidateLists = candidateLists;
section.candidateLists.sort((a, b) => {
return b.VoiceNumber - a.VoiceNumber;
});
return section;
}
async calculateStats(deskRounds) {
let subscribed = 0;
let blank = 0;
let nullVote = 0;
let totalVotes = 0;
let VotesExpressed = 0;
for (let i in deskRounds) {
let desk = await this.DeskModel.getDesk(deskRounds[i].DeskID);
subscribed += desk.Subscribed;
deskRounds[i].Votes.forEach((vote) => {
totalVotes += vote.VoiceNumber;
if (vote.Blank) blank += vote.VoiceNumber;
else if (vote.NullVote) nullVote += vote.VoiceNumber;
else VotesExpressed += vote.VoiceNumber;
});
}
return {
Abstention: Number(
((subscribed - totalVotes) / subscribed) * 100
).toFixed(2),
Blank: Number((blank / totalVotes) * 100).toFixed(2),
NullVote: Number((nullVote / totalVotes) * 100).toFixed(2),
VotesExpressed: VotesExpressed,
};
}
getElecteds(area) {
let electeds = [];
// order candidates by rank and remove refused or removed candidates
area.candidateLists.forEach((candidateList) => {
candidateList.Candidates.sort(function (a, b) {
return a.Rank - b.Rank;
});
for (let i = 0; i < candidateList.Candidates.length; i++) {
if (
candidateList.Candidates[i].Refused ||
candidateList.Candidates[i].Removed
) {
candidateList.Candidates.splice(i, 1);
}
}
});
// première étape
let seatForFirst = parseInt(area.SeatNumber / 2);
if ((parseInt(area.seatNumber) / 2) % 2 != 0) seatForFirst += 1;
electeds = electeds.concat(
area.candidateLists[0].Candidates.splice(0, seatForFirst)
);
// deuxième étape
let leftSeats = area.SeatNumber - seatForFirst;
let electoralQuotien = area.stats.VotesExpressed / leftSeats;
area.candidateLists.forEach((candidateList) => {
let seatsAttributed = parseInt(
candidateList.VoiceNumber / electoralQuotien
);
candidateList.SeatsAttributed = seatsAttributed;
leftSeats -= seatsAttributed;
if (seatsAttributed > 0) {
electeds = electeds.concat(
candidateList.Candidates.splice(0, seatsAttributed)
);
}
});
// //troisème étape
var day = new Date();
while (leftSeats > 0) {
area.candidateLists.forEach((candidateList) => {
candidateList.Average =
candidateList.VoiceNumber /
(parseInt(candidateList.SeatsAttributed) + 1);
});
area.candidateLists.sort(function (a, b) {
return b.Average - a.Average;
});
if (area.candidateLists[0].Average === area.candidateLists[1].Average) {
if (area.candidateLists[1].vote > area.candidateLists[0].vote)
[area.candidateLists[0], area.candidateLists[1]] = [
area.candidateLists[1],
area.candidateLists[0],
];
if (area.candidateLists[0].vote == area.candidateLists[1].vote) {
if (
area.candidateLists[0].Candidates[0].Birthdate == "" ||
area.candidateLists[1].Candidates[0].Birthdate == ""
) {
area.errorAgeAverage = true;
}
ageCandidate1 = ageCount(
day,
new Date(area.candidateLists[0].Candidates[0].Birthdate)
);
ageCandidate2 = ageCount(
day,
new Date(area.candidateLists[1].Candidates[0].Birthdate)
);
if (ageCandidate2 > ageCandidate1) {
[area.candidateLists[0], area.candidateLists[1]] = [
area.candidateLists[1],
area.candidateLists[0],
];
}
}
}
electeds = electeds.concat(
area.candidateLists[0].Candidates.splice(0, 1)
);
area.candidateLists[0].SeatsAttributed += 1;
leftSeats -= 1;
}
return electeds;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment