Merge pull request #6318 from GabrielInTheWorld/fix-candidate-percentbase

Fixes percentbase of candidates in assignments
This commit is contained in:
Emanuel Schütze 2022-01-06 18:04:43 +01:00 committed by GitHub
commit 31f59e069d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 27 deletions

View File

@ -41,7 +41,7 @@
<div class="single-result" [ngClass]="getVoteClass(vote)">
<span>
<span *ngIf="vote.showPercent">
{{ getVoteAmount(vote, row) | pollPercentBase: poll:'assignment' }}
{{ getVoteAmount(vote, row) | pollPercentBase: poll:row:'assignment' }}
</span>
<span *ngIf="row.class === 'user'">
{{ getVoteAmount(vote, row) | parsePollNumber }}
@ -59,7 +59,7 @@
<td class="result">
<div class="single-result">
<span>
{{ poll.entitled_users_at_stop.length | pollPercentBase: poll:'assignment' }}
{{ poll.entitled_users_at_stop.length | pollPercentBase: poll:row:'assignment' }}
</span>
<span>
{{ poll.entitled_users_at_stop.length }}

View File

@ -3,6 +3,7 @@ import { Pipe, PipeTransform } from '@angular/core';
import { AssignmentPollService } from 'app/site/assignments/modules/assignment-poll/services/assignment-poll.service';
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
import { PollData } from 'app/site/polls/services/poll.service';
import { PollDataOption, PollTableData } from '../../site/polls/services/poll.service';
/**
* Uses a number and a ViewPoll-object.
@ -26,7 +27,12 @@ export class PollPercentBasePipe implements PipeTransform {
private motionPollService: MotionPollService
) {}
public transform(value: number, poll: PollData, type: 'motion' | 'assignment'): string | null {
public transform(
value: number,
poll: PollData,
row: PollDataOption | PollTableData,
type: 'motion' | 'assignment'
): string | null {
// logic handles over the pollService to avoid circular dependencies
let voteValueInPercent: string;
@ -36,9 +42,9 @@ export class PollPercentBasePipe implements PipeTransform {
* we cannot expect the projector to work with real types for now, we need to provice the type
*/
if (type === 'assignment') {
voteValueInPercent = this.assignmentPollService.getVoteValueInPercent(value, poll);
voteValueInPercent = this.assignmentPollService.getVoteValueInPercent(value, { poll, row });
} else {
voteValueInPercent = this.motionPollService.getVoteValueInPercent(value, poll);
voteValueInPercent = this.motionPollService.getVoteValueInPercent(value, { poll, row });
}
if (voteValueInPercent) {

View File

@ -196,33 +196,26 @@ export class AssignmentPollService extends PollService {
}));
}
private sumOptionsYN(poll: PollData): number {
return poll.options.reduce((o, n) => {
o += n.yes > 0 ? n.yes : 0;
o += n.no > 0 ? n.no : 0;
return o;
}, 0);
private sumOptionsYN(option: PollDataOption): number {
return (option?.yes ?? 0) + (option?.no ?? 0);
}
private sumOptionsYNA(poll: PollData): number {
return poll.options.reduce((o, n) => {
o += n.abstain > 0 ? n.abstain : 0;
return o;
}, this.sumOptionsYN(poll));
private sumOptionsYNA(option: PollDataOption): number {
return this.sumOptionsYN(option) + (option?.abstain ?? 0);
}
public getPercentBase(poll: PollData): number {
public getPercentBase(poll: PollData, row: PollDataOption): number {
const base: AssignmentPollPercentBase = poll.onehundred_percent_base as AssignmentPollPercentBase;
let totalByBase: number;
switch (base) {
case AssignmentPollPercentBase.YN:
totalByBase = this.sumOptionsYN(poll);
totalByBase = this.sumOptionsYN(row);
break;
case AssignmentPollPercentBase.YNA:
totalByBase = this.sumOptionsYNA(poll);
totalByBase = this.sumOptionsYNA(row);
break;
case AssignmentPollPercentBase.Y:
totalByBase = this.sumOptionsYNA(poll);
totalByBase = this.sumOptionsYNA(row);
break;
case AssignmentPollPercentBase.Valid:
totalByBase = poll.votesvalid;
@ -246,7 +239,7 @@ export class AssignmentPollService extends PollService {
const voteValue = option[field];
const votingKey = this.translate.instant(this.pollKeyVerbose.transform(field));
const resultValue = this.parsePollNumber.transform(voteValue);
const resultInPercent = this.getVoteValueInPercent(voteValue, poll);
const resultInPercent = this.getVoteValueInPercent(voteValue, { poll, row: option });
let resultLabel = `${votingKey}: ${resultValue}`;
// 0 is a valid number in this case

View File

@ -241,7 +241,12 @@ export class AssignmentPdfService {
.map((singleResult: VotingResult) => {
const votingKey = this.translate.instant(this.pollKeyVerbose.transform(singleResult.vote));
const resultValue = this.parsePollNumber.transform(singleResult.amount);
const resultInPercent = this.pollPercentBase.transform(singleResult.amount, poll, 'assignment');
const resultInPercent = this.pollPercentBase.transform(
singleResult.amount,
poll,
votingResult,
'assignment'
);
return `${votingKey}${!!votingKey ? ': ' : ''}${resultValue} ${
singleResult.showPercent && resultInPercent ? resultInPercent : ''
}`;

View File

@ -385,7 +385,10 @@ export class MotionPdfService {
const resultValue = this.parsePollNumber.transform(value.amount);
column1.push(`${votingOption}:`);
if (value.showPercent) {
const resultInPercent = this.motionPollService.getVoteValueInPercent(value.amount, poll);
const resultInPercent = this.motionPollService.getVoteValueInPercent(value.amount, {
poll,
row: votingResult
});
// hard check for "null" since 0 is a valid number in this case
if (resultInPercent !== null) {
column2.push(`(${resultInPercent})`);

View File

@ -161,6 +161,13 @@ export interface VotingResult {
const PollChartBarThickness = 20;
function isPollTableData(value: any): value is PollTableData {
if (!value) {
return false;
}
return !!value.votingOption && !!value.value;
}
/**
* Shared service class for polls. Used by child classes {@link MotionPollService}
* and {@link AssignmentPollService}
@ -215,10 +222,14 @@ export abstract class PollService {
/**
* return the total number of votes depending on the selected percent base
*/
public abstract getPercentBase(poll: PollData): number;
public abstract getPercentBase(poll: PollData, row: PollDataOption): number;
public getVoteValueInPercent(value: number, poll: PollData): string | null {
const totalByBase = this.getPercentBase(poll);
public getVoteValueInPercent(
value: number,
{ poll, row }: { poll: PollData; row: PollDataOption | PollTableData }
): string | null {
const option = isPollTableData(row) ? this.transformToOptionData(row) : row;
const totalByBase = this.getPercentBase(poll, option);
if (totalByBase && totalByBase > 0) {
const percentNumber = (value / totalByBase) * 100;
if (percentNumber >= 0) {
@ -342,6 +353,17 @@ export abstract class PollService {
return isAssignment ? this.getPollDataFieldsByMethod(poll) : this.getPollDataFieldsByPercentBase(poll);
}
protected transformToOptionData(data: PollTableData): PollDataOption {
const yes = data.value.find(vote => vote.vote === `yes`);
const no = data.value.find(vote => vote.vote === `no`);
const abstain = data.value.find(vote => vote.vote === `abstain`);
return {
yes: yes?.amount,
no: no?.amount,
abstain: abstain?.amount
};
}
private getPollDataFieldsByMethod(poll: PollData | ViewBasePoll): CalculablePollKey[] {
switch (poll.pollmethod) {
case AssignmentPollMethod.YNA: {

View File

@ -2,7 +2,7 @@ black
coverage
flake8
isort<5.0.0
mypy
mypy<0.920
pytest<5.4.2
pytest-django<3.9.0
pytest-asyncio<0.11.0