Merge pull request #5718 from tsiegleauq/negative-voting
Allow negative voting
This commit is contained in:
commit
1eee3bc56d
@ -56,6 +56,7 @@ export interface AssignmentAnalogVoteData {
|
|||||||
votesvalid?: number;
|
votesvalid?: number;
|
||||||
votesinvalid?: number;
|
votesinvalid?: number;
|
||||||
votescast?: number;
|
votescast?: number;
|
||||||
|
global_yes?: number;
|
||||||
global_no?: number;
|
global_no?: number;
|
||||||
global_abstain?: number;
|
global_abstain?: number;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="voting-option">{{ 'Candidates' | translate }}</th>
|
<th class="voting-option">{{ 'Candidates' | translate }}</th>
|
||||||
<th class="result yes">
|
<th class="result yes" *ngIf="showYHeader">
|
||||||
<span *ngIf="!isMethodY">
|
<span *ngIf="!isMethodY">
|
||||||
{{ 'Yes' | translate }}
|
{{ 'Yes' | translate }}
|
||||||
</span>
|
</span>
|
||||||
@ -11,7 +11,14 @@
|
|||||||
{{ 'Votes' | translate }}
|
{{ 'Votes' | translate }}
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th class="result no" *ngIf="!isMethodY">{{ 'No' | translate }}</th>
|
<th class="result" *ngIf="showNHeader">
|
||||||
|
<span class="no" *ngIf="!isMethodN">
|
||||||
|
{{ 'No' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="yes" *ngIf="isMethodN">
|
||||||
|
{{ 'Votes' | translate }}
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
<th class="result abstain" *ngIf="isMethodYNA">{{ 'Abstain' | translate }}</th>
|
<th class="result abstain" *ngIf="isMethodYNA">{{ 'Abstain' | translate }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let row of tableData" [class]="row.class">
|
<tr *ngFor="let row of tableData" [class]="row.class">
|
||||||
@ -26,13 +33,18 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="result" *ngFor="let vote of row.value">
|
<td class="result" *ngFor="let vote of filterRelevantResults(row.value)">
|
||||||
<div class="single-result" [ngClass]="getVoteClass(vote)" *ngIf="vote && voteFitsMethod(vote)">
|
<div class="single-result" [ngClass]="getVoteClass(vote)">
|
||||||
<span>
|
<span>
|
||||||
<span *ngIf="vote.showPercent">
|
<span *ngIf="vote.showPercent">
|
||||||
{{ vote.amount | pollPercentBase: poll:'assignment' }}
|
{{ getVoteAmount(vote, row) | pollPercentBase: poll:'assignment' }}
|
||||||
|
</span>
|
||||||
|
<span *ngIf="row.class === 'user'">
|
||||||
|
{{ getVoteAmount(vote, row) | parsePollNumber }}
|
||||||
|
</span>
|
||||||
|
<span *ngIf="row.class !== 'user'">
|
||||||
|
{{ vote.amount | parsePollNumber }}
|
||||||
</span>
|
</span>
|
||||||
{{ vote.amount | parsePollNumber }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -20,8 +20,20 @@ export class AssignmentPollDetailContentComponent {
|
|||||||
return this.poll.pollmethod;
|
return this.poll.pollmethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get showYHeader(): boolean {
|
||||||
|
return this.isMethodY || this.isMethodYN || this.isMethodYNA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get showNHeader(): boolean {
|
||||||
|
return this.isMethodN || this.isMethodYN || this.isMethodYNA;
|
||||||
|
}
|
||||||
|
|
||||||
public get isMethodY(): boolean {
|
public get isMethodY(): boolean {
|
||||||
return this.method === AssignmentPollMethod.Votes;
|
return this.method === AssignmentPollMethod.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isMethodN(): boolean {
|
||||||
|
return this.method === AssignmentPollMethod.N;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isMethodYN(): boolean {
|
public get isMethodYN(): boolean {
|
||||||
@ -37,19 +49,44 @@ export class AssignmentPollDetailContentComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getVoteClass(votingResult: VotingResult): string {
|
public getVoteClass(votingResult: VotingResult): string {
|
||||||
return votingResult.vote;
|
const votingClass = votingResult.vote;
|
||||||
|
if (this.isMethodN && votingClass === 'no') {
|
||||||
|
return 'yes';
|
||||||
|
} else {
|
||||||
|
return votingClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public filterRelevantResults(votingResult: VotingResult[]): VotingResult[] {
|
||||||
|
return votingResult.filter(result => {
|
||||||
|
return result && this.voteFitsMethod(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getVoteAmount(vote: VotingResult, row: PollTableData): number {
|
||||||
|
if (this.isMethodN && row.class === 'user') {
|
||||||
|
if (vote.amount < 0) {
|
||||||
|
return vote.amount;
|
||||||
|
} else {
|
||||||
|
return this.poll.votesvalid - vote.amount;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return vote.amount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public voteFitsMethod(result: VotingResult): boolean {
|
public voteFitsMethod(result: VotingResult): boolean {
|
||||||
if (this.isMethodY) {
|
if (!result.vote) {
|
||||||
if (result.vote === 'abstain' || result.vote === 'no') {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
if (this.isMethodY) {
|
||||||
} else if (this.isMethodYN) {
|
return result.vote === 'yes';
|
||||||
if (result.vote === 'abstain') {
|
} else if (this.isMethodN) {
|
||||||
return false;
|
return result.vote === 'no';
|
||||||
}
|
} else if (this.isMethodYN) {
|
||||||
|
return result.vote !== 'abstain';
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,16 @@ import { AssignmentOption } from './assignment-option';
|
|||||||
import { BasePoll } from '../poll/base-poll';
|
import { BasePoll } from '../poll/base-poll';
|
||||||
|
|
||||||
export enum AssignmentPollMethod {
|
export enum AssignmentPollMethod {
|
||||||
|
Y = 'Y',
|
||||||
YN = 'YN',
|
YN = 'YN',
|
||||||
YNA = 'YNA',
|
YNA = 'YNA',
|
||||||
Votes = 'votes'
|
N = 'N'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AssignmentPollPercentBase {
|
export enum AssignmentPollPercentBase {
|
||||||
|
Y = 'Y',
|
||||||
YN = 'YN',
|
YN = 'YN',
|
||||||
YNA = 'YNA',
|
YNA = 'YNA',
|
||||||
Votes = 'votes',
|
|
||||||
Valid = 'valid',
|
Valid = 'valid',
|
||||||
Cast = 'cast',
|
Cast = 'cast',
|
||||||
Disabled = 'disabled'
|
Disabled = 'disabled'
|
||||||
@ -33,22 +34,29 @@ export class AssignmentPoll extends BasePoll<
|
|||||||
'votesvalid',
|
'votesvalid',
|
||||||
'votesinvalid',
|
'votesinvalid',
|
||||||
'votescast',
|
'votescast',
|
||||||
'amount_global_abstain',
|
'amount_global_yes',
|
||||||
'amount_global_no'
|
'amount_global_no',
|
||||||
|
'amount_global_abstain'
|
||||||
];
|
];
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public assignment_id: number;
|
public assignment_id: number;
|
||||||
public votes_amount: number;
|
public votes_amount: number;
|
||||||
public allow_multiple_votes_per_candidate: boolean;
|
public allow_multiple_votes_per_candidate: boolean;
|
||||||
|
public global_yes: boolean;
|
||||||
public global_no: boolean;
|
public global_no: boolean;
|
||||||
public global_abstain: boolean;
|
public global_abstain: boolean;
|
||||||
|
public amount_global_yes: number;
|
||||||
public amount_global_no: number;
|
public amount_global_no: number;
|
||||||
public amount_global_abstain: number;
|
public amount_global_abstain: number;
|
||||||
public description: string;
|
public description: string;
|
||||||
|
|
||||||
public get isMethodY(): boolean {
|
public get isMethodY(): boolean {
|
||||||
return this.pollmethod === AssignmentPollMethod.Votes;
|
return this.pollmethod === AssignmentPollMethod.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isMethodN(): boolean {
|
||||||
|
return this.pollmethod === AssignmentPollMethod.N;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isMethodYN(): boolean {
|
public get isMethodYN(): boolean {
|
||||||
@ -64,7 +72,7 @@ export class AssignmentPoll extends BasePoll<
|
|||||||
return ['yes', 'no'];
|
return ['yes', 'no'];
|
||||||
} else if (this.pollmethod === AssignmentPollMethod.YNA) {
|
} else if (this.pollmethod === AssignmentPollMethod.YNA) {
|
||||||
return ['yes', 'no', 'abstain'];
|
return ['yes', 'no', 'abstain'];
|
||||||
} else if (this.pollmethod === AssignmentPollMethod.Votes) {
|
} else if (this.pollmethod === AssignmentPollMethod.Y) {
|
||||||
return ['yes'];
|
return ['yes'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,9 @@ const PollValues = {
|
|||||||
yes: 'Yes',
|
yes: 'Yes',
|
||||||
no: 'No',
|
no: 'No',
|
||||||
abstain: 'Abstain',
|
abstain: 'Abstain',
|
||||||
amount_global_abstain: 'General Abstain',
|
amount_global_yes: 'General Yes',
|
||||||
amount_global_no: 'General No'
|
amount_global_no: 'General No',
|
||||||
|
amount_global_abstain: 'General Abstain'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,15 +18,16 @@ export interface AssignmentPollTitleInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AssignmentPollMethodVerbose = {
|
export const AssignmentPollMethodVerbose = {
|
||||||
votes: _('Yes per candidate'),
|
Y: _('Yes per candidate'),
|
||||||
|
N: _('No per candidate'),
|
||||||
YN: _('Yes/No per candidate'),
|
YN: _('Yes/No per candidate'),
|
||||||
YNA: _('Yes/No/Abstain per candidate')
|
YNA: _('Yes/No/Abstain per candidate')
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AssignmentPollPercentBaseVerbose = {
|
export const AssignmentPollPercentBaseVerbose = {
|
||||||
|
Y: _('Sum of votes including general No/Abstain'),
|
||||||
YN: _('Yes/No per candidate'),
|
YN: _('Yes/No per candidate'),
|
||||||
YNA: _('Yes/No/Abstain per candidate'),
|
YNA: _('Yes/No/Abstain per candidate'),
|
||||||
votes: _('Sum of votes including general No/Abstain'),
|
|
||||||
valid: _('All valid ballots'),
|
valid: _('All valid ballots'),
|
||||||
cast: _('All casted ballots'),
|
cast: _('All casted ballots'),
|
||||||
disabled: _('Disabled (no percents)')
|
disabled: _('Disabled (no percents)')
|
||||||
|
@ -45,9 +45,18 @@
|
|||||||
|
|
||||||
<!-- Global Values -->
|
<!-- Global Values -->
|
||||||
<div>
|
<div>
|
||||||
|
<os-check-input
|
||||||
|
*ngIf="globalYesEnabled"
|
||||||
|
placeholder="{{ PollPropertyVerbose.global_yes | translate }}"
|
||||||
|
[checkboxValue]="-1"
|
||||||
|
inputType="number"
|
||||||
|
[checkboxLabel]="'majority' | translate"
|
||||||
|
formControlName="amount_global_yes"
|
||||||
|
></os-check-input>
|
||||||
|
|
||||||
<os-check-input
|
<os-check-input
|
||||||
*ngIf="globalNoEnabled"
|
*ngIf="globalNoEnabled"
|
||||||
placeholder="{{ 'General No' | translate }}"
|
placeholder="{{ PollPropertyVerbose.global_no | translate }}"
|
||||||
[checkboxValue]="-1"
|
[checkboxValue]="-1"
|
||||||
inputType="number"
|
inputType="number"
|
||||||
[checkboxLabel]="'majority' | translate"
|
[checkboxLabel]="'majority' | translate"
|
||||||
@ -56,7 +65,7 @@
|
|||||||
|
|
||||||
<os-check-input
|
<os-check-input
|
||||||
*ngIf="globalAbstainEnabled"
|
*ngIf="globalAbstainEnabled"
|
||||||
placeholder="{{ 'General Abstain' | translate }}"
|
placeholder="{{ PollPropertyVerbose.global_abstain | translate }}"
|
||||||
[checkboxValue]="-1"
|
[checkboxValue]="-1"
|
||||||
inputType="number"
|
inputType="number"
|
||||||
[checkboxLabel]="'majority' | translate"
|
[checkboxLabel]="'majority' | translate"
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
} from 'app/site/assignments/models/view-assignment-poll';
|
} from 'app/site/assignments/models/view-assignment-poll';
|
||||||
import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component';
|
import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component';
|
||||||
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
||||||
|
import { PollPropertyVerbose } from 'app/site/polls/models/view-base-poll';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||||
|
|
||||||
@ -57,12 +58,14 @@ export class AssignmentPollDialogComponent
|
|||||||
|
|
||||||
public voteValueVerbose = VoteValueVerbose;
|
public voteValueVerbose = VoteValueVerbose;
|
||||||
public generalValueVerbose = GeneralValueVerbose;
|
public generalValueVerbose = GeneralValueVerbose;
|
||||||
|
public PollPropertyVerbose = PollPropertyVerbose;
|
||||||
|
|
||||||
public AssignmentPollMethodVerbose = AssignmentPollMethodVerbose;
|
public AssignmentPollMethodVerbose = AssignmentPollMethodVerbose;
|
||||||
public AssignmentPollPercentBaseVerbose = AssignmentPollPercentBaseVerbose;
|
public AssignmentPollPercentBaseVerbose = AssignmentPollPercentBaseVerbose;
|
||||||
|
|
||||||
public options: OptionsObject;
|
public options: OptionsObject;
|
||||||
|
|
||||||
|
public globalYesEnabled: boolean;
|
||||||
public globalNoEnabled: boolean;
|
public globalNoEnabled: boolean;
|
||||||
public globalAbstainEnabled: boolean;
|
public globalAbstainEnabled: boolean;
|
||||||
|
|
||||||
@ -121,15 +124,25 @@ export class AssignmentPollDialogComponent
|
|||||||
|
|
||||||
private setAnalogPollValues(): void {
|
private setAnalogPollValues(): void {
|
||||||
const pollmethod = this.pollForm.contentForm.get('pollmethod').value;
|
const pollmethod = this.pollForm.contentForm.get('pollmethod').value;
|
||||||
|
this.globalYesEnabled = this.pollForm.contentForm.get('global_yes').value;
|
||||||
this.globalNoEnabled = this.pollForm.contentForm.get('global_no').value;
|
this.globalNoEnabled = this.pollForm.contentForm.get('global_no').value;
|
||||||
this.globalAbstainEnabled = this.pollForm.contentForm.get('global_abstain').value;
|
this.globalAbstainEnabled = this.pollForm.contentForm.get('global_abstain').value;
|
||||||
const analogPollValues: VoteValue[] = ['Y'];
|
|
||||||
if (pollmethod !== AssignmentPollMethod.Votes) {
|
const analogPollValues: VoteValue[] = [];
|
||||||
|
|
||||||
|
if (pollmethod === AssignmentPollMethod.N) {
|
||||||
analogPollValues.push('N');
|
analogPollValues.push('N');
|
||||||
|
} else {
|
||||||
|
analogPollValues.push('Y');
|
||||||
|
|
||||||
|
if (pollmethod !== AssignmentPollMethod.Y) {
|
||||||
|
analogPollValues.push('N');
|
||||||
|
}
|
||||||
|
if (pollmethod === AssignmentPollMethod.YNA) {
|
||||||
|
analogPollValues.push('A');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pollmethod === AssignmentPollMethod.YNA) {
|
|
||||||
analogPollValues.push('A');
|
|
||||||
}
|
|
||||||
this.analogPollValues = analogPollValues;
|
this.analogPollValues = analogPollValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,13 +152,14 @@ export class AssignmentPollDialogComponent
|
|||||||
votesvalid: data.votesvalid,
|
votesvalid: data.votesvalid,
|
||||||
votesinvalid: data.votesinvalid,
|
votesinvalid: data.votesinvalid,
|
||||||
votescast: data.votescast,
|
votescast: data.votescast,
|
||||||
|
amount_global_yes: data.amount_global_yes,
|
||||||
amount_global_no: data.amount_global_no,
|
amount_global_no: data.amount_global_no,
|
||||||
amount_global_abstain: data.amount_global_abstain
|
amount_global_abstain: data.amount_global_abstain
|
||||||
};
|
};
|
||||||
for (const option of data.options) {
|
for (const option of data.options) {
|
||||||
const votes: any = {};
|
const votes: any = {};
|
||||||
votes.Y = option.yes;
|
votes.Y = option.yes;
|
||||||
if (data.pollmethod !== AssignmentPollMethod.Votes) {
|
if (data.pollmethod !== AssignmentPollMethod.Y) {
|
||||||
votes.N = option.no;
|
votes.N = option.no;
|
||||||
}
|
}
|
||||||
if (data.pollmethod === AssignmentPollMethod.YNA) {
|
if (data.pollmethod === AssignmentPollMethod.YNA) {
|
||||||
@ -178,6 +192,7 @@ export class AssignmentPollDialogComponent
|
|||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
|
amount_global_yes: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
amount_global_no: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
amount_global_no: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
amount_global_abstain: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
amount_global_abstain: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
// insert all used global fields
|
// insert all used global fields
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Leftover votes -->
|
<!-- Leftover votes -->
|
||||||
<h4 *ngIf="poll.pollmethod === AssignmentPollMethod.Votes && poll.votes_amount > 1">
|
<h4 *ngIf="(poll.isMethodY || poll.isMethodN) && poll.votes_amount > 1">
|
||||||
{{ 'Available votes' | translate }}:
|
{{ 'Available votes' | translate }}:
|
||||||
|
|
||||||
<b> {{ getVotesAvailable(delegation) }}/{{ poll.votes_amount }} </b>
|
<b> {{ getVotesAvailable(delegation) }}/{{ poll.votes_amount }} </b>
|
||||||
@ -39,9 +39,9 @@
|
|||||||
<div *ngIf="poll.type !== PollType.Pseudoanonymous || !option.user_has_voted">
|
<div *ngIf="poll.type !== PollType.Pseudoanonymous || !option.user_has_voted">
|
||||||
<div
|
<div
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'yna-grid': poll.pollmethod === AssignmentPollMethod.YNA,
|
'yna-grid': poll.isMethodYNA,
|
||||||
'yn-grid': poll.pollmethod === AssignmentPollMethod.YN,
|
'yn-grid': poll.isMethodYN,
|
||||||
'single-vote-grid': poll.pollmethod === AssignmentPollMethod.Votes
|
'single-vote-grid': poll.isMethodY || poll.isMethodN
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="vote-candidate-name">
|
<div class="vote-candidate-name">
|
||||||
@ -64,7 +64,7 @@
|
|||||||
>
|
>
|
||||||
<mat-icon> {{ action.icon }}</mat-icon>
|
<mat-icon> {{ action.icon }}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span *ngIf="poll.pollmethod !== AssignmentPollMethod.Votes" class="vote-label">
|
<span *ngIf="poll.isMethodYN || poll.isMethodYNA" class="vote-label">
|
||||||
{{ action.label | translate }}
|
{{ action.label | translate }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -74,9 +74,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- global no/abstain -->
|
<!-- global no/abstain -->
|
||||||
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethod.Votes && (poll.global_no || poll.global_abstain)">
|
<ng-container
|
||||||
|
*ngIf="(poll.isMethodY || poll.isMethodN) && (poll.global_yes || poll.global_no || poll.global_abstain)"
|
||||||
|
>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="global-option-grid">
|
<div class="global-option-grid">
|
||||||
|
<div *ngIf="poll.global_yes">
|
||||||
|
<button
|
||||||
|
class="vote-button"
|
||||||
|
mat-raised-button
|
||||||
|
(click)="saveGlobalVote('Y', delegation)"
|
||||||
|
[ngClass]="getGlobalYesClass(delegation)"
|
||||||
|
[disabled]="isDeliveringVote(delegation)"
|
||||||
|
>
|
||||||
|
<mat-icon>thumb_up</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span class="vote-label">
|
||||||
|
{{ PollPropertyVerbose.global_yes | translate }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="poll.global_no">
|
<div *ngIf="poll.global_no">
|
||||||
<button
|
<button
|
||||||
class="vote-button"
|
class="vote-button"
|
||||||
@ -85,10 +102,10 @@
|
|||||||
[ngClass]="getGlobalNoClass(delegation)"
|
[ngClass]="getGlobalNoClass(delegation)"
|
||||||
[disabled]="isDeliveringVote(delegation)"
|
[disabled]="isDeliveringVote(delegation)"
|
||||||
>
|
>
|
||||||
<mat-icon> thumb_down </mat-icon>
|
<mat-icon>thumb_down</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span class="vote-label">
|
<span class="vote-label">
|
||||||
{{ 'General No' | translate }}
|
{{ PollPropertyVerbose.global_no | translate }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -100,10 +117,10 @@
|
|||||||
[ngClass]="getGlobalAbstainClass(delegation)"
|
[ngClass]="getGlobalAbstainClass(delegation)"
|
||||||
[disabled]="isDeliveringVote(delegation)"
|
[disabled]="isDeliveringVote(delegation)"
|
||||||
>
|
>
|
||||||
<mat-icon> trip_origin</mat-icon>
|
<mat-icon>trip_origin</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span class="vote-label">
|
<span class="vote-label">
|
||||||
{{ 'General Abstain' | translate }}
|
{{ PollPropertyVerbose.global_abstain | translate }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -149,7 +166,12 @@
|
|||||||
|
|
||||||
<ng-template #sendNow let-delegation="delegation">
|
<ng-template #sendNow let-delegation="delegation">
|
||||||
<div class="centered-button-wrapper">
|
<div class="centered-button-wrapper">
|
||||||
<button mat-flat-button color="accent" (click)="submitVote(delegation)" [disabled]="getVotesCount(delegation) == 0">
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="accent"
|
||||||
|
(click)="submitVote(delegation)"
|
||||||
|
[disabled]="getVotesCount(delegation) == 0"
|
||||||
|
>
|
||||||
<mat-icon> how_to_vote </mat-icon>
|
<mat-icon> how_to_vote </mat-icon>
|
||||||
<span>
|
<span>
|
||||||
{{ 'Submit vote now' | translate }}
|
{{ 'Submit vote now' | translate }}
|
||||||
|
@ -19,6 +19,27 @@ import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-
|
|||||||
import { BasePollVoteComponentDirective, VoteOption } from 'app/site/polls/components/base-poll-vote.component';
|
import { BasePollVoteComponentDirective, VoteOption } from 'app/site/polls/components/base-poll-vote.component';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
|
|
||||||
|
const voteOptions = {
|
||||||
|
Yes: {
|
||||||
|
vote: 'Y',
|
||||||
|
css: 'voted-yes',
|
||||||
|
icon: 'thumb_up',
|
||||||
|
label: 'Yes'
|
||||||
|
} as VoteOption,
|
||||||
|
No: {
|
||||||
|
vote: 'N',
|
||||||
|
css: 'voted-no',
|
||||||
|
icon: 'thumb_down',
|
||||||
|
label: 'No'
|
||||||
|
} as VoteOption,
|
||||||
|
Abstain: {
|
||||||
|
vote: 'A',
|
||||||
|
css: 'voted-abstain',
|
||||||
|
icon: 'trip_origin',
|
||||||
|
label: 'Abstain'
|
||||||
|
} as VoteOption
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-assignment-poll-vote',
|
selector: 'os-assignment-poll-vote',
|
||||||
templateUrl: './assignment-poll-vote.component.html',
|
templateUrl: './assignment-poll-vote.component.html',
|
||||||
@ -70,6 +91,13 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective<
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getGlobalYesClass(user: ViewUser = this.user): string {
|
||||||
|
if (this.voteRequestData[user.id]?.global === 'Y') {
|
||||||
|
return 'voted-yes';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
public getGlobalAbstainClass(user: ViewUser = this.user): string {
|
public getGlobalAbstainClass(user: ViewUser = this.user): string {
|
||||||
if (this.voteRequestData[user.id]?.global === 'A') {
|
if (this.voteRequestData[user.id]?.global === 'A') {
|
||||||
return 'voted-abstain';
|
return 'voted-abstain';
|
||||||
@ -85,29 +113,20 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private defineVoteOptions(): void {
|
private defineVoteOptions(): void {
|
||||||
this.voteActions.push({
|
if (this.poll) {
|
||||||
vote: 'Y',
|
if (this.poll.isMethodN) {
|
||||||
css: 'voted-yes',
|
this.voteActions.push(voteOptions.No);
|
||||||
icon: 'thumb_up',
|
} else {
|
||||||
label: 'Yes'
|
this.voteActions.push(voteOptions.Yes);
|
||||||
});
|
|
||||||
|
|
||||||
if (this.poll?.pollmethod !== AssignmentPollMethod.Votes) {
|
if (!this.poll.isMethodY) {
|
||||||
this.voteActions.push({
|
this.voteActions.push(voteOptions.No);
|
||||||
vote: 'N',
|
}
|
||||||
css: 'voted-no',
|
|
||||||
icon: 'thumb_down',
|
|
||||||
label: 'No'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.poll?.pollmethod === AssignmentPollMethod.YNA) {
|
if (this.poll.isMethodYNA) {
|
||||||
this.voteActions.push({
|
this.voteActions.push(voteOptions.Abstain);
|
||||||
vote: 'A',
|
}
|
||||||
css: 'voted-abstain',
|
}
|
||||||
icon: 'trip_origin',
|
|
||||||
label: 'Abstain'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +174,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective<
|
|||||||
delete this.voteRequestData[user.id].global;
|
delete this.voteRequestData[user.id].global;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.poll.pollmethod === AssignmentPollMethod.Votes) {
|
if (this.poll.isMethodY || this.poll.isMethodN) {
|
||||||
const votesAmount = this.poll.votes_amount;
|
const votesAmount = this.poll.votes_amount;
|
||||||
const tmpVoteRequest = this.poll.options
|
const tmpVoteRequest = this.poll.options
|
||||||
.map(option => option.id)
|
.map(option => option.id)
|
||||||
|
@ -76,7 +76,7 @@ export class AssignmentPollPdfService extends PollPdfService {
|
|||||||
subtitle = subtitle.substring(0, 90) + '...';
|
subtitle = subtitle.substring(0, 90) + '...';
|
||||||
}
|
}
|
||||||
let rowsPerPage = 1;
|
let rowsPerPage = 1;
|
||||||
if (poll.pollmethod === 'votes') {
|
if (poll.pollmethod === AssignmentPollMethod.Y) {
|
||||||
if (poll.options.length <= 2) {
|
if (poll.options.length <= 2) {
|
||||||
rowsPerPage = 4;
|
rowsPerPage = 4;
|
||||||
} else if (poll.options.length <= 5) {
|
} else if (poll.options.length <= 5) {
|
||||||
@ -141,12 +141,18 @@ export class AssignmentPollPdfService extends PollPdfService {
|
|||||||
return a.weight - b.weight;
|
return a.weight - b.weight;
|
||||||
});
|
});
|
||||||
const resultObject = candidates.map(cand => {
|
const resultObject = candidates.map(cand => {
|
||||||
return poll.pollmethod === 'votes'
|
return poll.pollmethod === AssignmentPollMethod.Y
|
||||||
? this.createBallotOption(cand.user.full_name)
|
? this.createBallotOption(cand.user.full_name)
|
||||||
: this.createYNBallotEntry(cand.user.full_name, poll.pollmethod);
|
: this.createYNBallotEntry(cand.user.full_name, poll.pollmethod);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (poll.pollmethod === 'votes') {
|
if (poll.pollmethod === AssignmentPollMethod.Y) {
|
||||||
|
if (poll.global_yes) {
|
||||||
|
const yesEntry = this.createBallotOption(this.translate.instant('Yes'));
|
||||||
|
yesEntry.margin[1] = 25;
|
||||||
|
resultObject.push(yesEntry);
|
||||||
|
}
|
||||||
|
|
||||||
if (poll.global_no) {
|
if (poll.global_no) {
|
||||||
const noEntry = this.createBallotOption(this.translate.instant('No'));
|
const noEntry = this.createBallotOption(this.translate.instant('No'));
|
||||||
noEntry.margin[1] = 25;
|
noEntry.margin[1] = 25;
|
||||||
|
@ -92,6 +92,14 @@ export class AssignmentPollService extends PollService {
|
|||||||
|
|
||||||
private getGlobalVoteKeys(poll: ViewAssignmentPoll | PollData): VotingResult[] {
|
private getGlobalVoteKeys(poll: ViewAssignmentPoll | PollData): VotingResult[] {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
vote: 'amount_global_yes',
|
||||||
|
showPercent: this.showPercentOfValidOrCast(poll),
|
||||||
|
hide:
|
||||||
|
poll.amount_global_yes === VOTE_UNDOCUMENTED ||
|
||||||
|
!poll.amount_global_yes ||
|
||||||
|
poll.pollmethod === AssignmentPollMethod.N
|
||||||
|
},
|
||||||
{
|
{
|
||||||
vote: 'amount_global_no',
|
vote: 'amount_global_no',
|
||||||
showPercent: this.showPercentOfValidOrCast(poll),
|
showPercent: this.showPercentOfValidOrCast(poll),
|
||||||
@ -109,7 +117,14 @@ export class AssignmentPollService extends PollService {
|
|||||||
const tableData: PollTableData[] = poll.options
|
const tableData: PollTableData[] = poll.options
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (this.sortByVote) {
|
if (this.sortByVote) {
|
||||||
return b.yes - a.yes;
|
if (poll.pollmethod === AssignmentPollMethod.N) {
|
||||||
|
// most no on top:
|
||||||
|
// return b.no - a.no;
|
||||||
|
// least no on top:
|
||||||
|
return a.no - b.no;
|
||||||
|
} else {
|
||||||
|
return b.yes - a.yes;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// PollData does not have weight, we need to rely on the order of things.
|
// PollData does not have weight, we need to rely on the order of things.
|
||||||
if (a.weight && b.weight) {
|
if (a.weight && b.weight) {
|
||||||
@ -144,6 +159,7 @@ export class AssignmentPollService extends PollService {
|
|||||||
});
|
});
|
||||||
tableData.push(...this.formatVotingResultToTableData(this.getGlobalVoteKeys(poll), poll));
|
tableData.push(...this.formatVotingResultToTableData(this.getGlobalVoteKeys(poll), poll));
|
||||||
tableData.push(...this.formatVotingResultToTableData(super.getSumTableKeys(poll), poll));
|
tableData.push(...this.formatVotingResultToTableData(super.getSumTableKeys(poll), poll));
|
||||||
|
|
||||||
return tableData;
|
return tableData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +206,7 @@ export class AssignmentPollService extends PollService {
|
|||||||
case AssignmentPollPercentBase.YNA:
|
case AssignmentPollPercentBase.YNA:
|
||||||
totalByBase = this.sumOptionsYNA(poll);
|
totalByBase = this.sumOptionsYNA(poll);
|
||||||
break;
|
break;
|
||||||
case AssignmentPollPercentBase.Votes:
|
case AssignmentPollPercentBase.Y:
|
||||||
totalByBase = this.sumOptionsYNA(poll);
|
totalByBase = this.sumOptionsYNA(poll);
|
||||||
break;
|
break;
|
||||||
case AssignmentPollPercentBase.Valid:
|
case AssignmentPollPercentBase.Valid:
|
||||||
|
@ -221,7 +221,7 @@ export class AssignmentPdfService {
|
|||||||
private getPollResult(votingResult: PollTableData, poll: ViewAssignmentPoll): string {
|
private getPollResult(votingResult: PollTableData, poll: ViewAssignmentPoll): string {
|
||||||
const resultList = votingResult.value
|
const resultList = votingResult.value
|
||||||
.filter((singleResult: VotingResult) => {
|
.filter((singleResult: VotingResult) => {
|
||||||
if (poll.pollmethod === AssignmentPollMethod.Votes) {
|
if (poll.pollmethod === AssignmentPollMethod.Y) {
|
||||||
return singleResult.vote !== 'no' && singleResult.vote !== 'abstain';
|
return singleResult.vote !== 'no' && singleResult.vote !== 'abstain';
|
||||||
} else if (poll.pollmethod === AssignmentPollMethod.YN) {
|
} else if (poll.pollmethod === AssignmentPollMethod.YN) {
|
||||||
return singleResult.vote !== 'abstain';
|
return singleResult.vote !== 'abstain';
|
||||||
|
@ -10,7 +10,7 @@ import { VotingError, VotingService } from 'app/core/ui-services/voting.service'
|
|||||||
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { ViewBasePoll } from '../models/view-base-poll';
|
import { PollPropertyVerbose, ViewBasePoll } from '../models/view-base-poll';
|
||||||
|
|
||||||
export interface VoteOption {
|
export interface VoteOption {
|
||||||
vote?: VoteValue;
|
vote?: VoteValue;
|
||||||
@ -36,6 +36,8 @@ export abstract class BasePollVoteComponentDirective<V extends ViewBasePoll> ext
|
|||||||
|
|
||||||
protected delegations: ViewUser[];
|
protected delegations: ViewUser[];
|
||||||
|
|
||||||
|
public PollPropertyVerbose = PollPropertyVerbose;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
@ -90,7 +92,7 @@ export abstract class BasePollVoteComponentDirective<V extends ViewBasePoll> ext
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getVotingError(user: ViewUser = this.user): string | void {
|
public getVotingError(user: ViewUser = this.user): string | void {
|
||||||
console.log('error ', this.votingService.getVotePermissionErrorVerbose(this.poll, user));
|
console.log('Cannot vote because:', this.votingService.getVotePermissionErrorVerbose(this.poll, user));
|
||||||
return this.votingService.getVotePermissionErrorVerbose(this.poll, user);
|
return this.votingService.getVotePermissionErrorVerbose(this.poll, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,16 +24,18 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Groups entitled to Vote -->
|
<!-- Groups entitled to Vote -->
|
||||||
<mat-form-field *ngIf="contentForm.get('type').value && contentForm.get('type').value !== 'analog'">
|
<div class="suboption">
|
||||||
<os-search-value-selector
|
<mat-form-field *ngIf="contentForm.get('type').value && contentForm.get('type').value !== 'analog'">
|
||||||
formControlName="groups_id"
|
<os-search-value-selector
|
||||||
[multiple]="true"
|
formControlName="groups_id"
|
||||||
[showChips]="false"
|
[multiple]="true"
|
||||||
[includeNone]="false"
|
[showChips]="false"
|
||||||
[placeholder]="PollPropertyVerbose.groups | translate"
|
[includeNone]="false"
|
||||||
[inputListValues]="groupObservable"
|
[placeholder]="PollPropertyVerbose.groups | translate"
|
||||||
></os-search-value-selector>
|
[inputListValues]="groupObservable"
|
||||||
</mat-form-field>
|
></os-search-value-selector>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Poll Methods -->
|
<!-- Poll Methods -->
|
||||||
<mat-form-field *ngIf="pollMethods">
|
<mat-form-field *ngIf="pollMethods">
|
||||||
@ -50,6 +52,29 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Amount of Votes and global options -->
|
||||||
|
<div class="suboption" *ngIf="showAmountAndGlobal(data)">
|
||||||
|
<mat-form-field>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
matInput
|
||||||
|
placeholder="{{ PollPropertyVerbose.votes_amount | translate }}"
|
||||||
|
formControlName="votes_amount"
|
||||||
|
min="1"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="global-options">
|
||||||
|
<mat-checkbox formControlName="global_yes">
|
||||||
|
{{ PollPropertyVerbose.global_yes | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="global_no">{{ PollPropertyVerbose.global_no | translate }}</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="global_abstain">
|
||||||
|
{{ PollPropertyVerbose.global_abstain | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 100 Percent Base -->
|
<!-- 100 Percent Base -->
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select
|
<mat-select
|
||||||
@ -62,25 +87,5 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Amount of Votes -->
|
|
||||||
<ng-container
|
|
||||||
*ngIf="contentForm.get('pollmethod').value === 'votes' && (!data || !data.state || data.isCreated)"
|
|
||||||
>
|
|
||||||
<mat-form-field>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
matInput
|
|
||||||
placeholder="{{ PollPropertyVerbose.votes_amount | translate }}"
|
|
||||||
formControlName="votes_amount"
|
|
||||||
min="1"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox formControlName="global_no">{{ PollPropertyVerbose.global_no | translate }}</mat-checkbox>
|
|
||||||
<mat-checkbox formControlName="global_abstain">{{
|
|
||||||
PollPropertyVerbose.global_abstain | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</ng-container>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,31 +9,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-preview-meta-info {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin: 10px 0;
|
|
||||||
|
|
||||||
.short-description {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 5px;
|
|
||||||
display: inline-block;
|
|
||||||
span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
&-label {
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-preview-meta-info-form {
|
.poll-preview-meta-info-form {
|
||||||
display: flex;
|
.suboption {
|
||||||
align-items: center;
|
margin-left: 1.5em;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
|
|
||||||
& > * {
|
.mat-checkbox {
|
||||||
flex: 1;
|
margin-right: 2em;
|
||||||
margin: 0 4px;
|
}
|
||||||
|
|
||||||
|
.global-options {
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,11 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
this.contentForm.get('type').disable();
|
this.contentForm.get('type').disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public showAmountAndGlobal(data: any): boolean {
|
||||||
|
const selectedPollMethod = this.contentForm.get('pollmethod').value;
|
||||||
|
return (selectedPollMethod === 'Y' || selectedPollMethod === 'N') && (!data || !data.state || data.isCreated);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates the available percent bases according to the pollmethod
|
* updates the available percent bases according to the pollmethod
|
||||||
* @param method the currently chosen pollmethod
|
* @param method the currently chosen pollmethod
|
||||||
@ -182,10 +187,10 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
if (method) {
|
if (method) {
|
||||||
let forbiddenBases = [];
|
let forbiddenBases = [];
|
||||||
if (method === AssignmentPollMethod.YN) {
|
if (method === AssignmentPollMethod.YN) {
|
||||||
forbiddenBases = [PercentBase.YNA, AssignmentPollPercentBase.Votes];
|
forbiddenBases = [PercentBase.YNA, AssignmentPollPercentBase.Y];
|
||||||
} else if (method === AssignmentPollMethod.YNA) {
|
} else if (method === AssignmentPollMethod.YNA) {
|
||||||
forbiddenBases = [AssignmentPollPercentBase.Votes];
|
forbiddenBases = [AssignmentPollPercentBase.Y];
|
||||||
} else if (method === AssignmentPollMethod.Votes) {
|
} else if (method === AssignmentPollMethod.Y || AssignmentPollMethod.N) {
|
||||||
forbiddenBases = [PercentBase.YN, PercentBase.YNA];
|
forbiddenBases = [PercentBase.YN, PercentBase.YNA];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,16 +214,16 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
): AssignmentPollPercentBase {
|
): AssignmentPollPercentBase {
|
||||||
if (
|
if (
|
||||||
method === AssignmentPollMethod.YN &&
|
method === AssignmentPollMethod.YN &&
|
||||||
(base === AssignmentPollPercentBase.YNA || base === AssignmentPollPercentBase.Votes)
|
(base === AssignmentPollPercentBase.YNA || base === AssignmentPollPercentBase.Y)
|
||||||
) {
|
) {
|
||||||
return AssignmentPollPercentBase.YN;
|
return AssignmentPollPercentBase.YN;
|
||||||
} else if (method === AssignmentPollMethod.YNA && base === AssignmentPollPercentBase.Votes) {
|
} else if (method === AssignmentPollMethod.YNA && base === AssignmentPollPercentBase.Y) {
|
||||||
return AssignmentPollPercentBase.YNA;
|
return AssignmentPollPercentBase.YNA;
|
||||||
} else if (
|
} else if (
|
||||||
method === AssignmentPollMethod.Votes &&
|
method === AssignmentPollMethod.Y &&
|
||||||
(base === AssignmentPollPercentBase.YN || base === AssignmentPollPercentBase.YNA)
|
(base === AssignmentPollPercentBase.YN || base === AssignmentPollPercentBase.YNA)
|
||||||
) {
|
) {
|
||||||
return AssignmentPollPercentBase.Votes;
|
return AssignmentPollPercentBase.Y;
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
@ -267,8 +272,10 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
: '---'
|
: '---'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (data.pollmethod === 'votes') {
|
|
||||||
|
if (data.pollmethod === 'Y' || data.pollmethod === 'N') {
|
||||||
this.pollValues.push([this.pollService.getVerboseNameForKey('votes_amount'), data.votes_amount]);
|
this.pollValues.push([this.pollService.getVerboseNameForKey('votes_amount'), data.votes_amount]);
|
||||||
|
this.pollValues.push([this.pollService.getVerboseNameForKey('global_yes'), data.global_yes]);
|
||||||
this.pollValues.push([this.pollService.getVerboseNameForKey('global_no'), data.global_no]);
|
this.pollValues.push([this.pollService.getVerboseNameForKey('global_no'), data.global_no]);
|
||||||
this.pollValues.push([this.pollService.getVerboseNameForKey('global_abstain'), data.global_abstain]);
|
this.pollValues.push([this.pollService.getVerboseNameForKey('global_abstain'), data.global_abstain]);
|
||||||
}
|
}
|
||||||
@ -284,6 +291,7 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
majority_method: ['', Validators.required],
|
majority_method: ['', Validators.required],
|
||||||
votes_amount: [1, [Validators.required, Validators.min(1)]],
|
votes_amount: [1, [Validators.required, Validators.min(1)]],
|
||||||
groups_id: [],
|
groups_id: [],
|
||||||
|
global_yes: [false],
|
||||||
global_no: [false],
|
global_no: [false],
|
||||||
global_abstain: [false]
|
global_abstain: [false]
|
||||||
});
|
});
|
||||||
|
@ -44,6 +44,7 @@ export const PollPropertyVerbose = {
|
|||||||
state: 'State',
|
state: 'State',
|
||||||
groups: 'Entitled to vote',
|
groups: 'Entitled to vote',
|
||||||
votes_amount: 'Amount of votes',
|
votes_amount: 'Amount of votes',
|
||||||
|
global_yes: 'General Yes',
|
||||||
global_no: 'General No',
|
global_no: 'General No',
|
||||||
global_abstain: 'General Abstain'
|
global_abstain: 'General Abstain'
|
||||||
};
|
};
|
||||||
|
@ -109,6 +109,7 @@ export interface PollData {
|
|||||||
votesvalid: number;
|
votesvalid: number;
|
||||||
votesinvalid: number;
|
votesinvalid: number;
|
||||||
votescast: number;
|
votescast: number;
|
||||||
|
amount_global_yes?: number;
|
||||||
amount_global_no?: number;
|
amount_global_no?: number;
|
||||||
amount_global_abstain?: number;
|
amount_global_abstain?: number;
|
||||||
}
|
}
|
||||||
@ -145,6 +146,7 @@ export interface VotingResult {
|
|||||||
| 'votesvalid'
|
| 'votesvalid'
|
||||||
| 'votesinvalid'
|
| 'votesinvalid'
|
||||||
| 'votescast'
|
| 'votescast'
|
||||||
|
| 'amount_global_yes'
|
||||||
| 'amount_global_no'
|
| 'amount_global_no'
|
||||||
| 'amount_global_abstain';
|
| 'amount_global_abstain';
|
||||||
amount?: number;
|
amount?: number;
|
||||||
@ -340,6 +342,9 @@ export abstract class PollService {
|
|||||||
case AssignmentPollMethod.YN: {
|
case AssignmentPollMethod.YN: {
|
||||||
return ['yes', 'no'];
|
return ['yes', 'no'];
|
||||||
}
|
}
|
||||||
|
case AssignmentPollMethod.N: {
|
||||||
|
return ['no'];
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return ['yes'];
|
return ['yes'];
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export interface AssignmentPollSlideData extends BasePollSlideData {
|
|||||||
}[];
|
}[];
|
||||||
|
|
||||||
// optional for published polls:
|
// optional for published polls:
|
||||||
|
amount_global_yes?: number;
|
||||||
amount_global_no?: number;
|
amount_global_no?: number;
|
||||||
amount_global_abstain?: number;
|
amount_global_abstain?: number;
|
||||||
votesvalid: number;
|
votesvalid: number;
|
||||||
|
@ -17,7 +17,11 @@ class AssignmentAccessPermissions(BaseAccessPermissions):
|
|||||||
class AssignmentPollAccessPermissions(BasePollAccessPermissions):
|
class AssignmentPollAccessPermissions(BasePollAccessPermissions):
|
||||||
base_permission = "assignments.can_see"
|
base_permission = "assignments.can_see"
|
||||||
manage_permission = "assignments.can_manage"
|
manage_permission = "assignments.can_manage"
|
||||||
additional_fields = ["amount_global_no", "amount_global_abstain"]
|
additional_fields = [
|
||||||
|
"amount_global_yes",
|
||||||
|
"amount_global_no",
|
||||||
|
"amount_global_abstain",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class AssignmentOptionAccessPermissions(BaseOptionAccessPermissions):
|
class AssignmentOptionAccessPermissions(BaseOptionAccessPermissions):
|
||||||
|
@ -14,7 +14,7 @@ def get_config_variables():
|
|||||||
# Voting
|
# Voting
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name="assignment_poll_method",
|
name="assignment_poll_method",
|
||||||
default_value=AssignmentPoll.POLLMETHOD_VOTES,
|
default_value=AssignmentPoll.POLLMETHOD_Y,
|
||||||
input_type="choice",
|
input_type="choice",
|
||||||
label="Default election method",
|
label="Default election method",
|
||||||
choices=tuple(
|
choices=tuple(
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
# Generated by Django 2.2.15 on 2020-11-24 06:44
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("assignments", "0015_assignmentvote_delegated_user"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentpoll",
|
||||||
|
name="db_amount_global_yes",
|
||||||
|
field=models.DecimalField(
|
||||||
|
blank=True,
|
||||||
|
decimal_places=6,
|
||||||
|
default=Decimal("0"),
|
||||||
|
max_digits=15,
|
||||||
|
null=True,
|
||||||
|
validators=[django.core.validators.MinValueValidator(Decimal("-2"))],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentpoll",
|
||||||
|
name="global_yes",
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignmentpoll",
|
||||||
|
name="pollmethod",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("votes", "Yes per candidate"),
|
||||||
|
("N", "No per candidate"),
|
||||||
|
("YN", "Yes/No per candidate"),
|
||||||
|
("YNA", "Yes/No/Abstain per candidate"),
|
||||||
|
],
|
||||||
|
max_length=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignmentpoll",
|
||||||
|
name="onehundred_percent_base",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("YN", "Yes/No per candidate"),
|
||||||
|
("YNA", "Yes/No/Abstain per candidate"),
|
||||||
|
("Y", "Sum of votes including general No/Abstain"),
|
||||||
|
("valid", "All valid ballots"),
|
||||||
|
("cast", "All casted ballots"),
|
||||||
|
("disabled", "Disabled (no percents)"),
|
||||||
|
],
|
||||||
|
max_length=8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignmentpoll",
|
||||||
|
name="pollmethod",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("Y", "Yes per candidate"),
|
||||||
|
("N", "No per candidate"),
|
||||||
|
("YN", "Yes/No per candidate"),
|
||||||
|
("YNA", "Yes/No/Abstain per candidate"),
|
||||||
|
],
|
||||||
|
max_length=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
30
server/openslides/assignments/migrations/0017_vote_to_y.py
Normal file
30
server/openslides/assignments/migrations/0017_vote_to_y.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Finn Stutzenstein on 2020-11-24 06:44
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def votes_to_y(apps, schema_editor):
|
||||||
|
AssignmentPoll = apps.get_model("assignments", "AssignmentPoll")
|
||||||
|
for poll in AssignmentPoll.objects.all():
|
||||||
|
changed = False
|
||||||
|
if poll.pollmethod == "votes":
|
||||||
|
poll.pollmethod = "Y"
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if poll.onehundred_percent_base == "votes":
|
||||||
|
poll.onehundred_percent_base = "Y"
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
poll.save(skip_autoupdate=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("assignments", "0016_negative_votes"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(votes_to_y),
|
||||||
|
]
|
@ -319,24 +319,26 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
|
|
||||||
POLLMETHOD_YN = "YN"
|
POLLMETHOD_YN = "YN"
|
||||||
POLLMETHOD_YNA = "YNA"
|
POLLMETHOD_YNA = "YNA"
|
||||||
POLLMETHOD_VOTES = "votes"
|
POLLMETHOD_Y = "Y"
|
||||||
|
POLLMETHOD_N = "N"
|
||||||
POLLMETHODS = (
|
POLLMETHODS = (
|
||||||
(POLLMETHOD_VOTES, "Yes per candidate"),
|
(POLLMETHOD_Y, "Yes per candidate"),
|
||||||
|
(POLLMETHOD_N, "No per candidate"),
|
||||||
(POLLMETHOD_YN, "Yes/No per candidate"),
|
(POLLMETHOD_YN, "Yes/No per candidate"),
|
||||||
(POLLMETHOD_YNA, "Yes/No/Abstain per candidate"),
|
(POLLMETHOD_YNA, "Yes/No/Abstain per candidate"),
|
||||||
)
|
)
|
||||||
pollmethod = models.CharField(max_length=5, choices=POLLMETHODS)
|
pollmethod = models.CharField(max_length=5, choices=POLLMETHODS)
|
||||||
|
|
||||||
|
PERCENT_BASE_Y = "Y"
|
||||||
PERCENT_BASE_YN = "YN"
|
PERCENT_BASE_YN = "YN"
|
||||||
PERCENT_BASE_YNA = "YNA"
|
PERCENT_BASE_YNA = "YNA"
|
||||||
PERCENT_BASE_VOTES = "votes"
|
|
||||||
PERCENT_BASE_VALID = "valid"
|
PERCENT_BASE_VALID = "valid"
|
||||||
PERCENT_BASE_CAST = "cast"
|
PERCENT_BASE_CAST = "cast"
|
||||||
PERCENT_BASE_DISABLED = "disabled"
|
PERCENT_BASE_DISABLED = "disabled"
|
||||||
PERCENT_BASES = (
|
PERCENT_BASES = (
|
||||||
(PERCENT_BASE_YN, "Yes/No per candidate"),
|
(PERCENT_BASE_YN, "Yes/No per candidate"),
|
||||||
(PERCENT_BASE_YNA, "Yes/No/Abstain per candidate"),
|
(PERCENT_BASE_YNA, "Yes/No/Abstain per candidate"),
|
||||||
(PERCENT_BASE_VOTES, "Sum of votes including general No/Abstain"),
|
(PERCENT_BASE_Y, "Sum of votes including general No/Abstain"),
|
||||||
(PERCENT_BASE_VALID, "All valid ballots"),
|
(PERCENT_BASE_VALID, "All valid ballots"),
|
||||||
(PERCENT_BASE_CAST, "All casted ballots"),
|
(PERCENT_BASE_CAST, "All casted ballots"),
|
||||||
(PERCENT_BASE_DISABLED, "Disabled (no percents)"),
|
(PERCENT_BASE_DISABLED, "Disabled (no percents)"),
|
||||||
@ -345,8 +347,8 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
max_length=8, blank=False, null=False, choices=PERCENT_BASES
|
max_length=8, blank=False, null=False, choices=PERCENT_BASES
|
||||||
)
|
)
|
||||||
|
|
||||||
global_abstain = models.BooleanField(default=True)
|
global_yes = models.BooleanField(default=True)
|
||||||
db_amount_global_abstain = models.DecimalField(
|
db_amount_global_yes = models.DecimalField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
default=Decimal("0"),
|
default=Decimal("0"),
|
||||||
@ -354,6 +356,7 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
max_digits=15,
|
max_digits=15,
|
||||||
decimal_places=6,
|
decimal_places=6,
|
||||||
)
|
)
|
||||||
|
|
||||||
global_no = models.BooleanField(default=True)
|
global_no = models.BooleanField(default=True)
|
||||||
db_amount_global_no = models.DecimalField(
|
db_amount_global_no = models.DecimalField(
|
||||||
null=True,
|
null=True,
|
||||||
@ -364,6 +367,16 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
decimal_places=6,
|
decimal_places=6,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
global_abstain = models.BooleanField(default=True)
|
||||||
|
db_amount_global_abstain = models.DecimalField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=Decimal("0"),
|
||||||
|
validators=[MinValueValidator(Decimal("-2"))],
|
||||||
|
max_digits=15,
|
||||||
|
decimal_places=6,
|
||||||
|
)
|
||||||
|
|
||||||
votes_amount = models.IntegerField(default=1, validators=[MinValueValidator(1)])
|
votes_amount = models.IntegerField(default=1, validators=[MinValueValidator(1)])
|
||||||
""" For "votes" mode: The amount of votes a voter can give. """
|
""" For "votes" mode: The amount of votes a voter can give. """
|
||||||
|
|
||||||
@ -372,12 +385,55 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
class Meta:
|
class Meta:
|
||||||
default_permissions = ()
|
default_permissions = ()
|
||||||
|
|
||||||
|
def get_amount_global_yes(self):
|
||||||
|
if not self.global_yes:
|
||||||
|
return None
|
||||||
|
elif self.type == self.TYPE_ANALOG:
|
||||||
|
return self.db_amount_global_yes
|
||||||
|
elif self.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
|
return sum(option.yes for option in self.options.all())
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_amount_global_yes(self, value):
|
||||||
|
if self.type != self.TYPE_ANALOG:
|
||||||
|
raise ValueError("Do not set amount_global_yes for non analog polls")
|
||||||
|
self.db_amount_global_yes = value
|
||||||
|
|
||||||
|
amount_global_yes = property(get_amount_global_yes, set_amount_global_yes)
|
||||||
|
|
||||||
|
def get_amount_global_no(self):
|
||||||
|
if not self.global_no:
|
||||||
|
return None
|
||||||
|
elif self.type == self.TYPE_ANALOG:
|
||||||
|
return self.db_amount_global_no
|
||||||
|
elif self.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
|
return sum(option.no for option in self.options.all())
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_amount_global_no(self, value):
|
||||||
|
if self.type != self.TYPE_ANALOG:
|
||||||
|
raise ValueError("Do not set amount_global_no for non analog polls")
|
||||||
|
self.db_amount_global_no = value
|
||||||
|
|
||||||
|
amount_global_no = property(get_amount_global_no, set_amount_global_no)
|
||||||
|
|
||||||
def get_amount_global_abstain(self):
|
def get_amount_global_abstain(self):
|
||||||
if not self.global_abstain:
|
if not self.global_abstain:
|
||||||
return None
|
return None
|
||||||
elif self.type == self.TYPE_ANALOG:
|
elif self.type == self.TYPE_ANALOG:
|
||||||
return self.db_amount_global_abstain
|
return self.db_amount_global_abstain
|
||||||
elif self.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
elif self.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
return sum(option.abstain for option in self.options.all())
|
return sum(option.abstain for option in self.options.all())
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@ -391,23 +447,6 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
get_amount_global_abstain, set_amount_global_abstain
|
get_amount_global_abstain, set_amount_global_abstain
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_amount_global_no(self):
|
|
||||||
if not self.global_no:
|
|
||||||
return None
|
|
||||||
elif self.type == self.TYPE_ANALOG:
|
|
||||||
return self.db_amount_global_no
|
|
||||||
elif self.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
|
||||||
return sum(option.no for option in self.options.all())
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_amount_global_no(self, value):
|
|
||||||
if self.type != self.TYPE_ANALOG:
|
|
||||||
raise ValueError("Do not set amount_global_no for non analog polls")
|
|
||||||
self.db_amount_global_no = value
|
|
||||||
|
|
||||||
amount_global_no = property(get_amount_global_no, set_amount_global_no)
|
|
||||||
|
|
||||||
def create_options(self, skip_autoupdate=False):
|
def create_options(self, skip_autoupdate=False):
|
||||||
related_users = AssignmentRelatedUser.objects.filter(
|
related_users = AssignmentRelatedUser.objects.filter(
|
||||||
assignment__id=self.assignment.id
|
assignment__id=self.assignment.id
|
||||||
@ -435,6 +474,7 @@ class AssignmentPoll(RESTModelMixin, BasePoll):
|
|||||||
inform_changed_data(self.assignment.list_of_speakers)
|
inform_changed_data(self.assignment.list_of_speakers)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.db_amount_global_abstain = Decimal(0)
|
self.db_amount_global_yes = Decimal(0)
|
||||||
self.db_amount_global_no = Decimal(0)
|
self.db_amount_global_no = Decimal(0)
|
||||||
|
self.db_amount_global_abstain = Decimal(0)
|
||||||
super().reset()
|
super().reset()
|
||||||
|
@ -86,6 +86,9 @@ async def assignment_poll_slide(
|
|||||||
poll_data["options"].append(option_data)
|
poll_data["options"].append(option_data)
|
||||||
|
|
||||||
if poll["state"] == AssignmentPoll.STATE_PUBLISHED:
|
if poll["state"] == AssignmentPoll.STATE_PUBLISHED:
|
||||||
|
poll_data["amount_global_yes"] = (
|
||||||
|
float(poll["amount_global_yes"]) if poll["amount_global_yes"] else None
|
||||||
|
)
|
||||||
poll_data["amount_global_no"] = (
|
poll_data["amount_global_no"] = (
|
||||||
float(poll["amount_global_no"]) if poll["amount_global_no"] else None
|
float(poll["amount_global_no"]) if poll["amount_global_no"] else None
|
||||||
)
|
)
|
||||||
|
@ -77,6 +77,9 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
Serializes all polls.
|
Serializes all polls.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
amount_global_yes = DecimalField(
|
||||||
|
max_digits=15, decimal_places=6, min_value=-2, read_only=True
|
||||||
|
)
|
||||||
amount_global_no = DecimalField(
|
amount_global_no = DecimalField(
|
||||||
max_digits=15, decimal_places=6, min_value=-2, read_only=True
|
max_digits=15, decimal_places=6, min_value=-2, read_only=True
|
||||||
)
|
)
|
||||||
@ -92,6 +95,8 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
"pollmethod",
|
"pollmethod",
|
||||||
"votes_amount",
|
"votes_amount",
|
||||||
"allow_multiple_votes_per_candidate",
|
"allow_multiple_votes_per_candidate",
|
||||||
|
"global_yes",
|
||||||
|
"amount_global_yes",
|
||||||
"global_no",
|
"global_no",
|
||||||
"amount_global_no",
|
"amount_global_no",
|
||||||
"global_abstain",
|
"global_abstain",
|
||||||
@ -111,13 +116,13 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
Returns None, if the 100-%-base must not be changed, otherwise the correct 100-%-base.
|
Returns None, if the 100-%-base must not be changed, otherwise the correct 100-%-base.
|
||||||
"""
|
"""
|
||||||
if pollmethod == AssignmentPoll.POLLMETHOD_YN and onehundred_percent_base in (
|
if pollmethod == AssignmentPoll.POLLMETHOD_YN and onehundred_percent_base in (
|
||||||
AssignmentPoll.PERCENT_BASE_VOTES,
|
AssignmentPoll.PERCENT_BASE_Y,
|
||||||
AssignmentPoll.PERCENT_BASE_YNA,
|
AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
):
|
):
|
||||||
return AssignmentPoll.PERCENT_BASE_YN
|
return AssignmentPoll.PERCENT_BASE_YN
|
||||||
if (
|
if (
|
||||||
pollmethod == AssignmentPoll.POLLMETHOD_YNA
|
pollmethod == AssignmentPoll.POLLMETHOD_YNA
|
||||||
and onehundred_percent_base == AssignmentPoll.PERCENT_BASE_VOTES
|
and onehundred_percent_base == AssignmentPoll.PERCENT_BASE_Y
|
||||||
):
|
):
|
||||||
if old_100_percent_base is None:
|
if old_100_percent_base is None:
|
||||||
return AssignmentPoll.PERCENT_BASE_YNA
|
return AssignmentPoll.PERCENT_BASE_YNA
|
||||||
@ -129,12 +134,11 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
return old_100_percent_base
|
return old_100_percent_base
|
||||||
else:
|
else:
|
||||||
return pollmethod
|
return pollmethod
|
||||||
if (
|
if pollmethod == AssignmentPoll.POLLMETHOD_Y and onehundred_percent_base in (
|
||||||
pollmethod == AssignmentPoll.POLLMETHOD_VOTES
|
AssignmentPoll.PERCENT_BASE_YN,
|
||||||
and onehundred_percent_base
|
AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
in (AssignmentPoll.PERCENT_BASE_YN, AssignmentPoll.PERCENT_BASE_YNA)
|
|
||||||
):
|
):
|
||||||
return AssignmentPoll.PERCENT_BASE_VOTES
|
return AssignmentPoll.PERCENT_BASE_Y
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,21 +268,32 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
|
|
||||||
super().perform_create(serializer)
|
super().perform_create(serializer)
|
||||||
poll = AssignmentPoll.objects.get(pk=serializer.data["id"])
|
poll = AssignmentPoll.objects.get(pk=serializer.data["id"])
|
||||||
poll.db_amount_global_abstain = Decimal(0)
|
poll.db_amount_global_yes = Decimal(0)
|
||||||
poll.db_amount_global_no = Decimal(0)
|
poll.db_amount_global_no = Decimal(0)
|
||||||
|
poll.db_amount_global_abstain = Decimal(0)
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
def handle_analog_vote(self, data, poll):
|
def handle_analog_vote(self, data, poll):
|
||||||
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
||||||
setattr(poll, field, data[field])
|
setattr(poll, field, data[field])
|
||||||
|
|
||||||
global_no_enabled = (
|
global_yes_enabled = poll.global_yes and poll.pollmethod in (
|
||||||
poll.global_no and poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
)
|
||||||
|
if global_yes_enabled:
|
||||||
|
poll.amount_global_yes = data.get("amount_global_yes", Decimal(0))
|
||||||
|
|
||||||
|
global_no_enabled = poll.global_no and poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
)
|
)
|
||||||
if global_no_enabled:
|
if global_no_enabled:
|
||||||
poll.amount_global_no = data.get("amount_global_no", Decimal(0))
|
poll.amount_global_no = data.get("amount_global_no", Decimal(0))
|
||||||
global_abstain_enabled = (
|
|
||||||
poll.global_abstain and poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES
|
global_abstain_enabled = poll.global_abstain and poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
)
|
)
|
||||||
if global_abstain_enabled:
|
if global_abstain_enabled:
|
||||||
poll.amount_global_abstain = data.get("amount_global_abstain", Decimal(0))
|
poll.amount_global_abstain = data.get("amount_global_abstain", Decimal(0))
|
||||||
@ -293,28 +304,47 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for option_id, vote in options_data.items():
|
for option_id, vote in options_data.items():
|
||||||
option = options.get(pk=int(option_id))
|
option = options.get(pk=int(option_id))
|
||||||
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
|
||||||
option=option, value="Y"
|
|
||||||
)
|
|
||||||
vote_obj.weight = vote["Y"]
|
|
||||||
vote_obj.save()
|
|
||||||
|
|
||||||
if poll.pollmethod in (
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_N:
|
||||||
AssignmentPoll.POLLMETHOD_YN,
|
|
||||||
AssignmentPoll.POLLMETHOD_YNA,
|
|
||||||
):
|
|
||||||
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
||||||
option=option, value="N"
|
option=option, value="N"
|
||||||
)
|
)
|
||||||
vote_obj.weight = vote["N"]
|
vote_obj.weight = vote["N"]
|
||||||
vote_obj.save()
|
vote_obj.save()
|
||||||
|
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_YNA:
|
elif poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
|
):
|
||||||
|
# All three methods have a Y
|
||||||
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
||||||
option=option, value="A"
|
option=option, value="Y"
|
||||||
)
|
)
|
||||||
vote_obj.weight = vote["A"]
|
vote_obj.weight = vote["Y"]
|
||||||
vote_obj.save()
|
vote_obj.save()
|
||||||
|
|
||||||
|
if poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
|
):
|
||||||
|
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
||||||
|
option=option, value="N"
|
||||||
|
)
|
||||||
|
vote_obj.weight = vote["N"]
|
||||||
|
vote_obj.save()
|
||||||
|
|
||||||
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_YNA:
|
||||||
|
vote_obj, _ = AssignmentVote.objects.get_or_create(
|
||||||
|
option=option, value="A"
|
||||||
|
)
|
||||||
|
vote_obj.weight = vote["A"]
|
||||||
|
vote_obj.save()
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"handle_analog_vote not implemented for {poll.pollmethod}"
|
||||||
|
)
|
||||||
inform_changed_data(option)
|
inform_changed_data(option)
|
||||||
|
|
||||||
poll.save()
|
poll.save()
|
||||||
@ -326,22 +356,27 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
{
|
{
|
||||||
"options": {<option_id>: {"Y": <amount>, ["N": <amount>], ["A": <amount>] }},
|
"options": {<option_id>: {"Y": <amount>, ["N": <amount>], ["A": <amount>] }},
|
||||||
["votesvalid": <amount>], ["votesinvalid": <amount>], ["votescast": <amount>],
|
["votesvalid": <amount>], ["votesinvalid": <amount>], ["votescast": <amount>],
|
||||||
["amount_global_no": <amount>], ["amount_global_abstain": <amount>]
|
["amount_global_yes": <amount>],
|
||||||
|
["amount_global_no": <amount>],
|
||||||
|
["amount_global_abstain": <amount>]
|
||||||
}
|
}
|
||||||
All amounts are decimals as strings
|
All amounts are decimals as strings
|
||||||
required fields per pollmethod:
|
required fields per pollmethod:
|
||||||
- votes: Y
|
- votes: Y
|
||||||
- YN: YN
|
- YN: YN
|
||||||
- YNA: YNA
|
- YNA: YNA
|
||||||
|
- N: N
|
||||||
named|pseudoanonymous:
|
named|pseudoanonymous:
|
||||||
votes:
|
votes:
|
||||||
{<option_id>: <amount>} | 'N' | 'A'
|
{<option_id>: <amount>} | 'Y' | 'N' | 'A'
|
||||||
- Exactly one of the three options must be given
|
- Exactly one of the three options must be given
|
||||||
|
- 'Y' is only valid if poll.global_yes==True
|
||||||
- 'N' is only valid if poll.global_no==True
|
- 'N' is only valid if poll.global_no==True
|
||||||
- 'A' is only valid if poll.global_abstain==True
|
- 'A' is only valid if poll.global_abstain==True
|
||||||
- amounts must be integer numbers >= 0.
|
- amounts must be integer numbers >= 0.
|
||||||
- ids should be integers of valid option ids for this poll
|
- ids should be integers of valid option ids for this poll
|
||||||
- amounts must be 0 or 1, if poll.allow_multiple_votes_per_candidate is False
|
- amounts must be 0 or 1, if poll.allow_multiple_votes_per_candidate is False
|
||||||
|
- if an option is not given, 0 is assumed
|
||||||
- The sum of all amounts must be grater than 0 and <= poll.votes_amount
|
- The sum of all amounts must be grater than 0 and <= poll.votes_amount
|
||||||
|
|
||||||
YN/YNA:
|
YN/YNA:
|
||||||
@ -361,39 +396,56 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
raise ValidationError({"detail": "Keys must be int"})
|
raise ValidationError({"detail": "Keys must be int"})
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise ValidationError({"detail": "A dict per option is required"})
|
raise ValidationError({"detail": "A dict per option is required"})
|
||||||
value["Y"] = self.parse_vote_value(value, "Y")
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_N:
|
||||||
if poll.pollmethod in (
|
|
||||||
AssignmentPoll.POLLMETHOD_YN,
|
|
||||||
AssignmentPoll.POLLMETHOD_YNA,
|
|
||||||
):
|
|
||||||
value["N"] = self.parse_vote_value(value, "N")
|
value["N"] = self.parse_vote_value(value, "N")
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_YNA:
|
else:
|
||||||
value["A"] = self.parse_vote_value(value, "A")
|
value["Y"] = self.parse_vote_value(value, "Y")
|
||||||
|
if poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
|
):
|
||||||
|
value["N"] = self.parse_vote_value(value, "N")
|
||||||
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_YNA:
|
||||||
|
value["A"] = self.parse_vote_value(value, "A")
|
||||||
|
|
||||||
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
||||||
data[field] = self.parse_vote_value(data, field)
|
data[field] = self.parse_vote_value(data, field)
|
||||||
|
|
||||||
global_no_enabled = (
|
global_yes_enabled = poll.global_yes and poll.pollmethod in (
|
||||||
poll.global_no and poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
)
|
)
|
||||||
global_abstain_enabled = (
|
if "amount_global_yes" in data and global_yes_enabled:
|
||||||
poll.global_abstain
|
data["amount_global_yes"] = self.parse_vote_value(
|
||||||
and poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES
|
data, "amount_global_yes"
|
||||||
)
|
|
||||||
if "amount_global_abstain" in data and global_abstain_enabled:
|
|
||||||
data["amount_global_abstain"] = self.parse_vote_value(
|
|
||||||
data, "amount_global_abstain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
global_no_enabled = poll.global_no and poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
)
|
||||||
if "amount_global_no" in data and global_no_enabled:
|
if "amount_global_no" in data and global_no_enabled:
|
||||||
data["amount_global_no"] = self.parse_vote_value(
|
data["amount_global_no"] = self.parse_vote_value(
|
||||||
data, "amount_global_no"
|
data, "amount_global_no"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
global_abstain_enabled = poll.global_abstain and poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
)
|
||||||
|
if "amount_global_abstain" in data and global_abstain_enabled:
|
||||||
|
data["amount_global_abstain"] = self.parse_vote_value(
|
||||||
|
data, "amount_global_abstain"
|
||||||
|
)
|
||||||
|
|
||||||
|
else: # non-analog polls
|
||||||
if isinstance(data, dict) and len(data) == 0:
|
if isinstance(data, dict) and len(data) == 0:
|
||||||
raise ValidationError({"details": "Empty ballots are not allowed"})
|
raise ValidationError({"details": "Empty ballots are not allowed"})
|
||||||
available_options = poll.get_options()
|
available_options = poll.get_options()
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
if poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
amount_sum = 0
|
amount_sum = 0
|
||||||
for option_id, amount in data.items():
|
for option_id, amount in data.items():
|
||||||
@ -426,10 +478,13 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
"args": [poll.votes_amount],
|
"args": [poll.votes_amount],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
# return, if there is a global vote, because we dont have to check option presence
|
||||||
|
elif data == "Y" and poll.global_yes:
|
||||||
|
return
|
||||||
elif data == "N" and poll.global_no:
|
elif data == "N" and poll.global_no:
|
||||||
return # return because we dont have to check option presence
|
return
|
||||||
elif data == "A" and poll.global_abstain:
|
elif data == "A" and poll.global_abstain:
|
||||||
return # return because we dont have to check option presence
|
return
|
||||||
else:
|
else:
|
||||||
raise ValidationError({"detail": "invalid data."})
|
raise ValidationError({"detail": "invalid data."})
|
||||||
|
|
||||||
@ -479,12 +534,15 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
weight = Decimal(amount)
|
weight = Decimal(amount)
|
||||||
if config["users_activate_vote_weight"]:
|
if config["users_activate_vote_weight"]:
|
||||||
weight *= vote_weight
|
weight *= vote_weight
|
||||||
|
value = "Y" # POLLMETHOD_Y
|
||||||
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_N:
|
||||||
|
value = "N"
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option,
|
option=option,
|
||||||
user=vote_user,
|
user=vote_user,
|
||||||
delegated_user=request_user,
|
delegated_user=request_user,
|
||||||
weight=weight,
|
weight=weight,
|
||||||
value="Y",
|
value=value,
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
else: # global_no or global_abstain
|
else: # global_no or global_abstain
|
||||||
@ -529,7 +587,10 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
VotedModel.objects.create(assignmentpoll=poll, user=user)
|
VotedModel.objects.create(assignmentpoll=poll, user=user)
|
||||||
|
|
||||||
def handle_named_vote(self, data, poll, vote_user, request_user):
|
def handle_named_vote(self, data, poll, vote_user, request_user):
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
if poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
self.create_votes_type_votes(
|
self.create_votes_type_votes(
|
||||||
data, poll, vote_user.vote_weight, vote_user, request_user
|
data, poll, vote_user.vote_weight, vote_user, request_user
|
||||||
)
|
)
|
||||||
@ -540,16 +601,22 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
self.create_votes_types_yn_yna(
|
self.create_votes_types_yn_yna(
|
||||||
data, poll, vote_user.vote_weight, vote_user, request_user
|
data, poll, vote_user.vote_weight, vote_user, request_user
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"The method {poll.pollmethod} is not supported!")
|
||||||
|
|
||||||
def handle_pseudoanonymous_vote(self, data, poll, user):
|
def handle_pseudoanonymous_vote(self, data, poll, user):
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
if poll.pollmethod in (
|
||||||
|
AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
AssignmentPoll.POLLMETHOD_N,
|
||||||
|
):
|
||||||
self.create_votes_type_votes(data, poll, user.vote_weight, None, None)
|
self.create_votes_type_votes(data, poll, user.vote_weight, None, None)
|
||||||
|
|
||||||
elif poll.pollmethod in (
|
elif poll.pollmethod in (
|
||||||
AssignmentPoll.POLLMETHOD_YN,
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
AssignmentPoll.POLLMETHOD_YNA,
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
):
|
):
|
||||||
self.create_votes_types_yn_yna(data, poll, user.vote_weight, None, None)
|
self.create_votes_types_yn_yna(data, poll, user.vote_weight, None, None)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"The method {poll.pollmethod} is not supported!")
|
||||||
|
|
||||||
def convert_option_data(self, poll, data):
|
def convert_option_data(self, poll, data):
|
||||||
poll_options = poll.get_options()
|
poll_options = poll.get_options()
|
||||||
|
@ -129,8 +129,10 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_YNA)
|
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_YNA)
|
||||||
self.assertEqual(poll.type, "named")
|
self.assertEqual(poll.type, "named")
|
||||||
# Check defaults
|
# Check defaults
|
||||||
|
self.assertTrue(poll.global_yes)
|
||||||
self.assertTrue(poll.global_no)
|
self.assertTrue(poll.global_no)
|
||||||
self.assertTrue(poll.global_abstain)
|
self.assertTrue(poll.global_abstain)
|
||||||
|
self.assertEqual(poll.amount_global_yes, None)
|
||||||
self.assertEqual(poll.amount_global_no, None)
|
self.assertEqual(poll.amount_global_no, None)
|
||||||
self.assertEqual(poll.amount_global_abstain, None)
|
self.assertEqual(poll.amount_global_abstain, None)
|
||||||
self.assertFalse(poll.allow_multiple_votes_per_candidate)
|
self.assertFalse(poll.allow_multiple_votes_per_candidate)
|
||||||
@ -151,6 +153,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
"majority_method": AssignmentPoll.MAJORITY_THREE_QUARTERS,
|
"majority_method": AssignmentPoll.MAJORITY_THREE_QUARTERS,
|
||||||
|
"global_yes": False,
|
||||||
"global_no": False,
|
"global_no": False,
|
||||||
"global_abstain": False,
|
"global_abstain": False,
|
||||||
"allow_multiple_votes_per_candidate": True,
|
"allow_multiple_votes_per_candidate": True,
|
||||||
@ -164,6 +167,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
self.assertEqual(poll.title, "test_title_ahThai4pae1pi4xoogoo")
|
self.assertEqual(poll.title, "test_title_ahThai4pae1pi4xoogoo")
|
||||||
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_YN)
|
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_YN)
|
||||||
self.assertEqual(poll.type, "pseudoanonymous")
|
self.assertEqual(poll.type, "pseudoanonymous")
|
||||||
|
self.assertFalse(poll.global_yes)
|
||||||
self.assertFalse(poll.global_no)
|
self.assertFalse(poll.global_no)
|
||||||
self.assertFalse(poll.global_abstain)
|
self.assertFalse(poll.global_abstain)
|
||||||
self.assertTrue(poll.allow_multiple_votes_per_candidate)
|
self.assertTrue(poll.allow_multiple_votes_per_candidate)
|
||||||
@ -327,7 +331,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
||||||
"type": "named",
|
"type": "named",
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_VOTES,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_Y,
|
||||||
"majority_method": AssignmentPoll.MAJORITY_SIMPLE,
|
"majority_method": AssignmentPoll.MAJORITY_SIMPLE,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -343,7 +347,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
"pollmethod": AssignmentPoll.POLLMETHOD_YN,
|
"pollmethod": AssignmentPoll.POLLMETHOD_YN,
|
||||||
"type": "named",
|
"type": "named",
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_VOTES,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_Y,
|
||||||
"majority_method": AssignmentPoll.MAJORITY_SIMPLE,
|
"majority_method": AssignmentPoll.MAJORITY_SIMPLE,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -356,7 +360,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_Thoo2eiphohhi1eeXoow",
|
"title": "test_title_Thoo2eiphohhi1eeXoow",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": "named",
|
"type": "named",
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -365,16 +369,14 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_201_CREATED)
|
self.assertHttpStatusVerbose(response, status.HTTP_201_CREATED)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
self.assertEqual(
|
self.assertEqual(poll.onehundred_percent_base, AssignmentPoll.PERCENT_BASE_Y)
|
||||||
poll.onehundred_percent_base, AssignmentPoll.PERCENT_BASE_VOTES
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_with_votes(self):
|
def test_create_with_votes(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": AssignmentPoll.TYPE_ANALOG,
|
"type": AssignmentPoll.TYPE_ANALOG,
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -400,7 +402,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": AssignmentPoll.TYPE_ANALOG,
|
"type": AssignmentPoll.TYPE_ANALOG,
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -408,7 +410,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
"votes": {
|
"votes": {
|
||||||
"options": {"2": {"Y": 1}},
|
"options": {"2": {"Y": 1}},
|
||||||
"votesvalid": "-2",
|
"votesvalid": "-2",
|
||||||
"votesinvalid": "-2",
|
"votesinvalid": "11",
|
||||||
"votescast": "-2",
|
"votescast": "-2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -418,12 +420,12 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
self.assertEqual(poll.state, AssignmentPoll.STATE_FINISHED)
|
self.assertEqual(poll.state, AssignmentPoll.STATE_FINISHED)
|
||||||
self.assertTrue(AssignmentVote.objects.exists())
|
self.assertTrue(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_create_with_votes_publish_immediately(self):
|
def test_create_with_votes_publish_immediately_method_y(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": AssignmentPoll.TYPE_ANALOG,
|
"type": AssignmentPoll.TYPE_ANALOG,
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -442,12 +444,46 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
self.assertEqual(poll.state, AssignmentPoll.STATE_PUBLISHED)
|
self.assertEqual(poll.state, AssignmentPoll.STATE_PUBLISHED)
|
||||||
self.assertTrue(AssignmentVote.objects.exists())
|
self.assertTrue(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_create_with_votes_publish_immediately_method_n(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-list"),
|
||||||
|
{
|
||||||
|
"title": "test_title_greoGKPO3FeBAfwpefl3",
|
||||||
|
"pollmethod": AssignmentPoll.POLLMETHOD_N,
|
||||||
|
"type": AssignmentPoll.TYPE_ANALOG,
|
||||||
|
"assignment_id": self.assignment.id,
|
||||||
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
|
"majority_method": AssignmentPoll.MAJORITY_SIMPLE,
|
||||||
|
"votes": {
|
||||||
|
"options": {"1": {"N": 1}},
|
||||||
|
"votesvalid": "-2",
|
||||||
|
"votesinvalid": "-2",
|
||||||
|
"votescast": "-2",
|
||||||
|
"amount_global_yes": 1,
|
||||||
|
"amount_global_no": 2,
|
||||||
|
"amount_global_abstain": 3,
|
||||||
|
},
|
||||||
|
"publish_immediately": "1",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_201_CREATED)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_PUBLISHED)
|
||||||
|
self.assertTrue(AssignmentVote.objects.exists())
|
||||||
|
self.assertEquals(poll.amount_global_yes, Decimal("1"))
|
||||||
|
self.assertEquals(poll.amount_global_no, Decimal("2"))
|
||||||
|
self.assertEquals(poll.amount_global_abstain, Decimal("3"))
|
||||||
|
option = poll.options.get(pk=1)
|
||||||
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
|
||||||
def test_create_with_invalid_votes(self):
|
def test_create_with_invalid_votes(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": AssignmentPoll.TYPE_ANALOG,
|
"type": AssignmentPoll.TYPE_ANALOG,
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -468,7 +504,7 @@ class CreateAssignmentPoll(TestCase):
|
|||||||
reverse("assignmentpoll-list"),
|
reverse("assignmentpoll-list"),
|
||||||
{
|
{
|
||||||
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
"title": "test_title_dKbv5tV47IzY1oGHXdSz",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
"type": AssignmentPoll.TYPE_NAMED,
|
"type": AssignmentPoll.TYPE_NAMED,
|
||||||
"assignment_id": self.assignment.id,
|
"assignment_id": self.assignment.id,
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
@ -500,9 +536,9 @@ class UpdateAssignmentPoll(TestCase):
|
|||||||
self.poll = AssignmentPoll.objects.create(
|
self.poll = AssignmentPoll.objects.create(
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
title="test_title_beeFaihuNae1vej2ai8m",
|
title="test_title_beeFaihuNae1vej2ai8m",
|
||||||
pollmethod=AssignmentPoll.POLLMETHOD_VOTES,
|
pollmethod=AssignmentPoll.POLLMETHOD_Y,
|
||||||
type=BasePoll.TYPE_NAMED,
|
type=BasePoll.TYPE_NAMED,
|
||||||
onehundred_percent_base=AssignmentPoll.PERCENT_BASE_VOTES,
|
onehundred_percent_base=AssignmentPoll.PERCENT_BASE_Y,
|
||||||
majority_method=AssignmentPoll.MAJORITY_SIMPLE,
|
majority_method=AssignmentPoll.MAJORITY_SIMPLE,
|
||||||
)
|
)
|
||||||
self.poll.create_options()
|
self.poll.create_options()
|
||||||
@ -545,7 +581,7 @@ class UpdateAssignmentPoll(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_VOTES)
|
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_Y)
|
||||||
|
|
||||||
def test_patch_type(self):
|
def test_patch_type(self):
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
@ -631,9 +667,7 @@ class UpdateAssignmentPoll(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
self.assertEqual(
|
self.assertEqual(poll.onehundred_percent_base, AssignmentPoll.PERCENT_BASE_Y)
|
||||||
poll.onehundred_percent_base, AssignmentPoll.PERCENT_BASE_VOTES
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_patch_majority_method(self):
|
def test_patch_majority_method(self):
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
@ -658,7 +692,8 @@ class UpdateAssignmentPoll(TestCase):
|
|||||||
reverse("assignmentpoll-detail", args=[self.poll.pk]),
|
reverse("assignmentpoll-detail", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
"title": "test_title_ees6Tho8ahheen4cieja",
|
"title": "test_title_ees6Tho8ahheen4cieja",
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_VOTES,
|
"pollmethod": AssignmentPoll.POLLMETHOD_Y,
|
||||||
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
"global_abstain": False,
|
"global_abstain": False,
|
||||||
"allow_multiple_votes_per_candidate": True,
|
"allow_multiple_votes_per_candidate": True,
|
||||||
@ -668,9 +703,11 @@ class UpdateAssignmentPoll(TestCase):
|
|||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
self.assertEqual(poll.title, "test_title_ees6Tho8ahheen4cieja")
|
self.assertEqual(poll.title, "test_title_ees6Tho8ahheen4cieja")
|
||||||
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_VOTES)
|
self.assertEqual(poll.pollmethod, AssignmentPoll.POLLMETHOD_Y)
|
||||||
|
self.assertTrue(poll.global_yes)
|
||||||
self.assertTrue(poll.global_no)
|
self.assertTrue(poll.global_no)
|
||||||
self.assertFalse(poll.global_abstain)
|
self.assertFalse(poll.global_abstain)
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("0"))
|
||||||
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
||||||
self.assertEqual(poll.amount_global_abstain, None)
|
self.assertEqual(poll.amount_global_abstain, None)
|
||||||
self.assertTrue(poll.allow_multiple_votes_per_candidate)
|
self.assertTrue(poll.allow_multiple_votes_per_candidate)
|
||||||
@ -1220,12 +1257,12 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
class VoteAssignmentPollNamedY(VoteAssignmentPollBaseTestClass):
|
||||||
def create_poll(self):
|
def create_poll(self):
|
||||||
return AssignmentPoll.objects.create(
|
return AssignmentPoll.objects.create(
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
title="test_title_Zrvh146QAdq7t6iSDwZk",
|
title="test_title_Zrvh146QAdq7t6iSDwZk",
|
||||||
pollmethod=AssignmentPoll.POLLMETHOD_VOTES,
|
pollmethod=AssignmentPoll.POLLMETHOD_Y,
|
||||||
type=BasePoll.TYPE_NAMED,
|
type=BasePoll.TYPE_NAMED,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1296,6 +1333,34 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertEqual(option2.no, Decimal("0"))
|
self.assertEqual(option2.no, Decimal("0"))
|
||||||
self.assertEqual(option2.abstain, Decimal("0"))
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_global_yes(self):
|
||||||
|
self.poll.votes_amount = 2
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option = poll.options.get(pk=1)
|
||||||
|
self.assertEqual(option.yes, Decimal("1"))
|
||||||
|
self.assertEqual(option.no, Decimal("0"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("1"))
|
||||||
|
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_global_yes_forbidden(self):
|
||||||
|
self.poll.global_yes = False
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
self.assertEqual(AssignmentPoll.objects.get().amount_global_yes, None)
|
||||||
|
|
||||||
def test_global_no(self):
|
def test_global_no(self):
|
||||||
self.poll.votes_amount = 2
|
self.poll.votes_amount = 2
|
||||||
self.poll.save()
|
self.poll.save()
|
||||||
@ -1309,6 +1374,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertEqual(option.yes, Decimal("0"))
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
self.assertEqual(option.no, Decimal("1"))
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
self.assertEqual(option.abstain, Decimal("0"))
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("0"))
|
||||||
self.assertEqual(poll.amount_global_no, Decimal("1"))
|
self.assertEqual(poll.amount_global_no, Decimal("1"))
|
||||||
self.assertEqual(poll.amount_global_abstain, Decimal("0"))
|
self.assertEqual(poll.amount_global_abstain, Decimal("0"))
|
||||||
|
|
||||||
@ -1336,6 +1402,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertEqual(option.yes, Decimal("0"))
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
self.assertEqual(option.no, Decimal("0"))
|
self.assertEqual(option.no, Decimal("0"))
|
||||||
self.assertEqual(option.abstain, Decimal("1"))
|
self.assertEqual(option.abstain, Decimal("1"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("0"))
|
||||||
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
||||||
self.assertEqual(poll.amount_global_abstain, Decimal("1"))
|
self.assertEqual(poll.amount_global_abstain, Decimal("1"))
|
||||||
|
|
||||||
@ -1505,6 +1572,321 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
|
class VoteAssignmentPollNamedN(VoteAssignmentPollBaseTestClass):
|
||||||
|
def create_poll(self):
|
||||||
|
return AssignmentPoll.objects.create(
|
||||||
|
assignment=self.assignment,
|
||||||
|
title="test_title_4oi49ckKFk39SDIfj30s",
|
||||||
|
pollmethod=AssignmentPoll.POLLMETHOD_N,
|
||||||
|
type=BasePoll.TYPE_NAMED,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_for_multiple_votes(self):
|
||||||
|
self.poll.allow_multiple_votes_per_candidate = True
|
||||||
|
self.poll.votes_amount = 3
|
||||||
|
self.poll.save()
|
||||||
|
self.add_candidate()
|
||||||
|
|
||||||
|
def test_start_poll(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-start", args=[self.poll.pk])
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("0"))
|
||||||
|
self.assertFalse(poll.get_votes().exists())
|
||||||
|
|
||||||
|
def test_vote(self):
|
||||||
|
self.add_candidate()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 0}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(AssignmentVote.objects.count(), 1)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("1"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("1"))
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertTrue(self.admin in poll.voted.all())
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("1"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("0"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_change_vote(self):
|
||||||
|
self.add_candidate()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 0}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 0, "2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("1"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("0"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_global_yes(self):
|
||||||
|
self.poll.votes_amount = 2
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option = poll.options.get(pk=1)
|
||||||
|
self.assertEqual(option.yes, Decimal("1"))
|
||||||
|
self.assertEqual(option.no, Decimal("0"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("1"))
|
||||||
|
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_global_yes_forbidden(self):
|
||||||
|
self.poll.global_yes = False
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
self.assertEqual(AssignmentPoll.objects.get().amount_global_yes, None)
|
||||||
|
|
||||||
|
def test_global_no(self):
|
||||||
|
self.poll.votes_amount = 2
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option = poll.options.get(pk=1)
|
||||||
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_no, Decimal("1"))
|
||||||
|
self.assertEqual(poll.amount_global_abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_global_no_forbidden(self):
|
||||||
|
self.poll.global_no = False
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
self.assertEqual(AssignmentPoll.objects.get().amount_global_no, None)
|
||||||
|
|
||||||
|
def test_global_abstain(self):
|
||||||
|
self.poll.votes_amount = 2
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option = poll.options.get(pk=1)
|
||||||
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option.no, Decimal("0"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("1"))
|
||||||
|
self.assertEqual(poll.amount_global_yes, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_no, Decimal("0"))
|
||||||
|
self.assertEqual(poll.amount_global_abstain, Decimal("1"))
|
||||||
|
|
||||||
|
def test_global_abstain_forbidden(self):
|
||||||
|
self.poll.global_abstain = False
|
||||||
|
self.poll.save()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
self.assertEqual(AssignmentPoll.objects.get().amount_global_abstain, None)
|
||||||
|
|
||||||
|
def test_negative_vote(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": -1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_multiple_votes(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 2, "2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("2"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("1"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_multiple_votes_wrong_amount(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 2, "2": 2}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_too_many_options(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 1, "3": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_options(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_no_permissions(self):
|
||||||
|
self.start_poll()
|
||||||
|
self.make_admin_delegate()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_anonymous(self):
|
||||||
|
self.start_poll()
|
||||||
|
gclient = self.create_guest_client()
|
||||||
|
response = gclient.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_vote_not_present(self):
|
||||||
|
self.start_poll()
|
||||||
|
self.admin.is_present = False
|
||||||
|
self.admin.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_state(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_missing_data(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertNotIn(self.admin.id, poll.voted.all())
|
||||||
|
|
||||||
|
def test_wrong_data_format(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": [1, 2, 5]},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_wrong_option_format(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": "string"}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_option_id_type(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"id": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_wrong_vote_data(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": [None]}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
||||||
def create_poll(self):
|
def create_poll(self):
|
||||||
return AssignmentPoll.objects.create(
|
return AssignmentPoll.objects.create(
|
||||||
@ -1698,12 +2080,12 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
class VoteAssignmentPollPseudoanonymousY(VoteAssignmentPollBaseTestClass):
|
||||||
def create_poll(self):
|
def create_poll(self):
|
||||||
return AssignmentPoll.objects.create(
|
return AssignmentPoll.objects.create(
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
title="test_title_Zrvh146QAdq7t6iSDwZk",
|
title="test_title_Zrvh146QAdq7t6iSDwZk",
|
||||||
pollmethod=AssignmentPoll.POLLMETHOD_VOTES,
|
pollmethod=AssignmentPoll.POLLMETHOD_Y,
|
||||||
type=BasePoll.TYPE_PSEUDOANONYMOUS,
|
type=BasePoll.TYPE_PSEUDOANONYMOUS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1933,6 +2315,241 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
|
class VoteAssignmentPollPseudoanonymousN(VoteAssignmentPollBaseTestClass):
|
||||||
|
def create_poll(self):
|
||||||
|
return AssignmentPoll.objects.create(
|
||||||
|
assignment=self.assignment,
|
||||||
|
title="test_title_wWPOVJgL9afm83eamf3e",
|
||||||
|
pollmethod=AssignmentPoll.POLLMETHOD_N,
|
||||||
|
type=BasePoll.TYPE_PSEUDOANONYMOUS,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_for_multiple_votes(self):
|
||||||
|
self.poll.allow_multiple_votes_per_candidate = True
|
||||||
|
self.poll.votes_amount = 3
|
||||||
|
self.poll.save()
|
||||||
|
self.add_candidate()
|
||||||
|
|
||||||
|
def test_start_poll(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-start", args=[self.poll.pk])
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("0"))
|
||||||
|
self.assertFalse(poll.get_votes().exists())
|
||||||
|
|
||||||
|
def test_vote(self):
|
||||||
|
self.add_candidate()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 0}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(AssignmentVote.objects.count(), 1)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("1"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("1"))
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertTrue(self.admin in poll.voted.all())
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("1"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("0"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
for vote in poll.get_votes():
|
||||||
|
self.assertIsNone(vote.user)
|
||||||
|
|
||||||
|
def test_change_vote(self):
|
||||||
|
self.add_candidate()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 0}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 0, "2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("1"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("0"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
|
||||||
|
def test_negative_vote(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": -1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_multiple_votes(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 2, "2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
self.assertEqual(option1.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option1.no, Decimal("2"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, Decimal("1"))
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
for vote in poll.get_votes():
|
||||||
|
self.assertIsNone(vote.user)
|
||||||
|
|
||||||
|
def test_multiple_votes_wrong_amount(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 2, "2": 2}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_too_many_options(self):
|
||||||
|
self.setup_for_multiple_votes()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1, "2": 1, "3": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_options(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"2": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_no_permissions(self):
|
||||||
|
self.start_poll()
|
||||||
|
self.make_admin_delegate()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_anonymous(self):
|
||||||
|
self.start_poll()
|
||||||
|
gclient = self.create_guest_client()
|
||||||
|
response = gclient.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_vote_not_present(self):
|
||||||
|
self.start_poll()
|
||||||
|
self.admin.is_present = False
|
||||||
|
self.admin.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_state(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_missing_data(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertNotIn(self.admin.id, poll.voted.all())
|
||||||
|
|
||||||
|
def test_wrong_data_format(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"data": [1, 2, 5]}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_wrong_option_format(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": "string"}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_wrong_option_id_type(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"id": 1}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
def test_wrong_vote_data(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": [None]}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
|
|
||||||
# test autoupdates
|
# test autoupdates
|
||||||
class VoteAssignmentPollAutoupdatesBaseClass(TestCase):
|
class VoteAssignmentPollAutoupdatesBaseClass(TestCase):
|
||||||
poll_type = "" # set by subclass, defines which poll type we use
|
poll_type = "" # set by subclass, defines which poll type we use
|
||||||
@ -1994,10 +2611,12 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"assignments/assignment-poll:1": {
|
"assignments/assignment-poll:1": {
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
"amount_global_abstain": None,
|
"global_abstain": True,
|
||||||
|
"amount_global_yes": None,
|
||||||
"amount_global_no": None,
|
"amount_global_no": None,
|
||||||
|
"amount_global_abstain": None,
|
||||||
"groups_id": [GROUP_DELEGATE_PK],
|
"groups_id": [GROUP_DELEGATE_PK],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
@ -2064,8 +2683,9 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
{
|
{
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
|
"global_abstain": True,
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
||||||
"state": AssignmentPoll.STATE_STARTED,
|
"state": AssignmentPoll.STATE_STARTED,
|
||||||
"type": AssignmentPoll.TYPE_NAMED,
|
"type": AssignmentPoll.TYPE_NAMED,
|
||||||
@ -2114,12 +2734,14 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
autoupdate[0]["assignments/assignment-poll:1"],
|
autoupdate[0]["assignments/assignment-poll:1"],
|
||||||
{
|
{
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"amount_global_abstain": None,
|
"amount_global_yes": None,
|
||||||
"amount_global_no": None,
|
"amount_global_no": None,
|
||||||
|
"amount_global_abstain": None,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"description": "test_description_paiquei5ahpie1wu8ohW",
|
"description": "test_description_paiquei5ahpie1wu8ohW",
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
|
"global_abstain": True,
|
||||||
"groups_id": [GROUP_DELEGATE_PK],
|
"groups_id": [GROUP_DELEGATE_PK],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"majority_method": "two_thirds",
|
"majority_method": "two_thirds",
|
||||||
@ -2186,10 +2808,12 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"assignments/assignment-poll:1": {
|
"assignments/assignment-poll:1": {
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
"amount_global_abstain": None,
|
"global_abstain": True,
|
||||||
|
"amount_global_yes": None,
|
||||||
"amount_global_no": None,
|
"amount_global_no": None,
|
||||||
|
"amount_global_abstain": None,
|
||||||
"groups_id": [GROUP_DELEGATE_PK],
|
"groups_id": [GROUP_DELEGATE_PK],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
@ -2241,8 +2865,9 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
{
|
{
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
|
"global_abstain": True,
|
||||||
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
"pollmethod": AssignmentPoll.POLLMETHOD_YNA,
|
||||||
"state": AssignmentPoll.STATE_STARTED,
|
"state": AssignmentPoll.STATE_STARTED,
|
||||||
"type": AssignmentPoll.TYPE_PSEUDOANONYMOUS,
|
"type": AssignmentPoll.TYPE_PSEUDOANONYMOUS,
|
||||||
@ -2291,12 +2916,14 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
{
|
{
|
||||||
"assignments/assignment-poll:1": {
|
"assignments/assignment-poll:1": {
|
||||||
"allow_multiple_votes_per_candidate": False,
|
"allow_multiple_votes_per_candidate": False,
|
||||||
"amount_global_abstain": None,
|
"amount_global_yes": None,
|
||||||
"amount_global_no": None,
|
"amount_global_no": None,
|
||||||
|
"amount_global_abstain": None,
|
||||||
"assignment_id": 1,
|
"assignment_id": 1,
|
||||||
"description": "test_description_paiquei5ahpie1wu8ohW",
|
"description": "test_description_paiquei5ahpie1wu8ohW",
|
||||||
"global_abstain": True,
|
"global_yes": True,
|
||||||
"global_no": True,
|
"global_no": True,
|
||||||
|
"global_abstain": True,
|
||||||
"groups_id": [GROUP_DELEGATE_PK],
|
"groups_id": [GROUP_DELEGATE_PK],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"majority_method": "two_thirds",
|
"majority_method": "two_thirds",
|
||||||
|
Loading…
Reference in New Issue
Block a user