Implement vote weight in client
Implements vote weight in client The user detail page has a new property change deserialize to parse floats change "yes"-voting to send "Y" and "0" instead of "1" and "0" add vote weight to user list, filter, sort add vote weight to single voting result votesvalid and votescast respect the individual vote weight fix parse-poll pipe and null in pdf
This commit is contained in:
parent
0f3d07f151
commit
97c2299aec
@ -7,7 +7,7 @@ export abstract class BaseDecimalModel<T = any> extends BaseModel<T> {
|
|||||||
if (input && typeof input === 'object') {
|
if (input && typeof input === 'object') {
|
||||||
this.getDecimalFields().forEach(field => {
|
this.getDecimalFields().forEach(field => {
|
||||||
if (input[field] !== undefined) {
|
if (input[field] !== undefined) {
|
||||||
input[field] = parseInt(input[field], 10);
|
input[field] = parseFloat(input[field]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,10 @@ export class User extends BaseDecimalModel<User> {
|
|||||||
public auth_type?: UserAuthType;
|
public auth_type?: UserAuthType;
|
||||||
public vote_weight: number;
|
public vote_weight: number;
|
||||||
|
|
||||||
|
public get isVoteWeightOne(): boolean {
|
||||||
|
return this.vote_weight === 1;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(input?: Partial<User>) {
|
public constructor(input?: Partial<User>) {
|
||||||
super(User.COLLECTIONSTRING, input);
|
super(User.COLLECTIONSTRING, input);
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,13 @@ export class ParsePollNumberPipe implements PipeTransform {
|
|||||||
public constructor(private translate: TranslateService) {}
|
public constructor(private translate: TranslateService) {}
|
||||||
|
|
||||||
public transform(value: number): number | string {
|
public transform(value: number): number | string {
|
||||||
const input = Math.trunc(value);
|
switch (value) {
|
||||||
switch (input) {
|
|
||||||
case VOTE_MAJORITY:
|
case VOTE_MAJORITY:
|
||||||
return this.translate.instant('majority');
|
return this.translate.instant('majority');
|
||||||
case VOTE_UNDOCUMENTED:
|
case VOTE_UNDOCUMENTED:
|
||||||
return this.translate.instant('undocumented');
|
return this.translate.instant('undocumented');
|
||||||
default:
|
default:
|
||||||
return input;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,13 @@
|
|||||||
<div *pblNgridCellDef="'user'; row as vote">
|
<div *pblNgridCellDef="'user'; row as vote">
|
||||||
<div *ngIf="vote.user">
|
<div *ngIf="vote.user">
|
||||||
{{ vote.user.getShortName() }}
|
{{ vote.user.getShortName() }}
|
||||||
<div class="user-subtitle" *ngIf="vote.user.getLevelAndNumber()">
|
<div class="user-subtitle">
|
||||||
{{ vote.user.getLevelAndNumber() }}
|
<div *ngIf="vote.user.getLevelAndNumber()">
|
||||||
|
{{ vote.user.getLevelAndNumber() }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isVoteWeightActive">
|
||||||
|
{{ 'Vote weight' | translate }}: {{ vote.user.vote_weight }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!vote.user">
|
<div *ngIf="!vote.user">
|
||||||
|
@ -10,6 +10,7 @@ import { OperatorService } from 'app/core/core-services/operator.service';
|
|||||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||||
import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service';
|
import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service';
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||||
@ -32,6 +33,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
|
|
||||||
public candidatesLabels: string[] = [];
|
public candidatesLabels: string[] = [];
|
||||||
|
|
||||||
|
public isVoteWeightActive: boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
@ -41,12 +44,16 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
groupRepo: GroupRepositoryService,
|
groupRepo: GroupRepositoryService,
|
||||||
prompt: PromptService,
|
prompt: PromptService,
|
||||||
pollDialog: AssignmentPollDialogService,
|
pollDialog: AssignmentPollDialogService,
|
||||||
|
configService: ConfigService,
|
||||||
protected pollService: AssignmentPollService,
|
protected pollService: AssignmentPollService,
|
||||||
votesRepo: AssignmentVoteRepositoryService,
|
votesRepo: AssignmentVoteRepositoryService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
|
configService
|
||||||
|
.get<boolean>('users_activate_vote_weight')
|
||||||
|
.subscribe(active => (this.isVoteWeightActive = active));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createVotesData(): void {
|
protected createVotesData(): void {
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
[filterProps]="filterProps"
|
[filterProps]="filterProps"
|
||||||
[allowProjector]="false"
|
[allowProjector]="false"
|
||||||
[fullScreen]="true"
|
[fullScreen]="true"
|
||||||
[vScrollFixed]="60"
|
[vScrollFixed]="-1"
|
||||||
listStorageKey="motion-poll-vote"
|
listStorageKey="motion-poll-vote"
|
||||||
[cssClasses]="{ 'single-votes-table': true }"
|
[cssClasses]="{ 'single-votes-table': true }"
|
||||||
>
|
>
|
||||||
@ -56,7 +56,18 @@
|
|||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div *pblNgridCellDef="'user'; row as vote">
|
<div *pblNgridCellDef="'user'; row as vote">
|
||||||
<div *ngIf="vote.user">{{ vote.user.getFullName() }}</div>
|
<div *ngIf="vote.user">
|
||||||
|
{{ vote.user.getShortName() }}
|
||||||
|
<div class="user-subtitle">
|
||||||
|
<div *ngIf="vote.user.getLevelAndNumber()">
|
||||||
|
{{ vote.user.getLevelAndNumber() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="isVoteWeightActive">
|
||||||
|
{{ 'Vote weight' | translate }}: {{ vote.user.vote_weight }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div *ngIf="!vote.user">{{ 'Anonymous' | translate }}</div>
|
<div *ngIf="!vote.user">{{ 'Anonymous' | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div *pblNgridCellDef="'vote'; row as vote" class="vote-cell">
|
<div *pblNgridCellDef="'vote'; row as vote" class="vote-cell">
|
||||||
|
@ -10,6 +10,7 @@ import { OperatorService } from 'app/core/core-services/operator.service';
|
|||||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||||
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
@ -41,6 +42,8 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
|||||||
|
|
||||||
public filterProps = ['user.getFullName', 'valueVerbose'];
|
public filterProps = ['user.getFullName', 'valueVerbose'];
|
||||||
|
|
||||||
|
public isVoteWeightActive: boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
@ -52,10 +55,14 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
|||||||
pollDialog: MotionPollDialogService,
|
pollDialog: MotionPollDialogService,
|
||||||
pollService: MotionPollService,
|
pollService: MotionPollService,
|
||||||
votesRepo: MotionVoteRepositoryService,
|
votesRepo: MotionVoteRepositoryService,
|
||||||
|
configService: ConfigService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
|
configService
|
||||||
|
.get<boolean>('users_activate_vote_weight')
|
||||||
|
.subscribe(active => (this.isVoteWeightActive = active));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createVotesData(): void {
|
protected createVotesData(): void {
|
||||||
|
@ -381,7 +381,12 @@ export class MotionPdfService {
|
|||||||
column1.push(`${votingOption}:`);
|
column1.push(`${votingOption}:`);
|
||||||
if (value.showPercent) {
|
if (value.showPercent) {
|
||||||
const resultInPercent = this.motionPollService.getVoteValueInPercent(value.amount, poll);
|
const resultInPercent = this.motionPollService.getVoteValueInPercent(value.amount, poll);
|
||||||
column2.push(`(${resultInPercent})`);
|
// hard check for "null" since 0 is a valid number in this case
|
||||||
|
if (resultInPercent !== null) {
|
||||||
|
column2.push(`(${resultInPercent})`);
|
||||||
|
} else {
|
||||||
|
column2.push('');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
column2.push('');
|
column2.push('');
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,13 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- Strucuture Level -->
|
<!-- Strucuture Level -->
|
||||||
<mat-form-field class="form70 distance">
|
<mat-form-field
|
||||||
|
class="distance"
|
||||||
|
[ngClass]="{
|
||||||
|
form37: showVoteWeight,
|
||||||
|
form70: !showVoteWeight
|
||||||
|
}"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
@ -119,8 +125,15 @@
|
|||||||
formControlName="structure_level"
|
formControlName="structure_level"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Participant Number -->
|
<!-- Participant Number -->
|
||||||
<mat-form-field class="form25 force-min-with">
|
<mat-form-field
|
||||||
|
[ngClass]="{
|
||||||
|
distance: showVoteWeight,
|
||||||
|
form37: showVoteWeight,
|
||||||
|
form25: !showVoteWeight
|
||||||
|
}"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
matInput
|
matInput
|
||||||
@ -128,6 +141,17 @@
|
|||||||
formControlName="number"
|
formControlName="number"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Vote weight -->
|
||||||
|
<mat-form-field class="form16 force-min-with" *ngIf="showVoteWeight">
|
||||||
|
<!-- TODO Input type should be number with limited decimal spaces -->
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
matInput
|
||||||
|
placeholder="{{ 'Vote weight' | translate }}"
|
||||||
|
formControlName="vote_weight"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -275,6 +299,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isAllowed('manage')">
|
<div *ngIf="isAllowed('manage')">
|
||||||
|
<!-- Vote weight -->
|
||||||
|
<div *ngIf="user.vote_weight && showVoteWeight">
|
||||||
|
<h4>{{ 'Vote weight' | translate }}</h4>
|
||||||
|
<span>{{ user.vote_weight }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Initial Password -->
|
<!-- Initial Password -->
|
||||||
<div *ngIf="user.default_password">
|
<div *ngIf="user.default_password">
|
||||||
<h4>{{ 'Initial password' | translate }}</h4>
|
<h4>{{ 'Initial password' | translate }}</h4>
|
||||||
|
@ -11,10 +11,12 @@ import { ConstantsService } from 'app/core/core-services/constants.service';
|
|||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { genders } from 'app/shared/models/users/user';
|
import { genders } from 'app/shared/models/users/user';
|
||||||
import { OneOfValidator } from 'app/shared/validators/one-of-validator';
|
import { OneOfValidator } from 'app/shared/validators/one-of-validator';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { PollService } from 'app/site/polls/services/poll.service';
|
||||||
import { UserPdfExportService } from '../../services/user-pdf-export.service';
|
import { UserPdfExportService } from '../../services/user-pdf-export.service';
|
||||||
import { ViewGroup } from '../../models/view-group';
|
import { ViewGroup } from '../../models/view-group';
|
||||||
import { ViewUser } from '../../models/view-user';
|
import { ViewUser } from '../../models/view-user';
|
||||||
@ -76,6 +78,12 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
|
|
||||||
private userBackends: UserBackends | null = null;
|
private userBackends: UserBackends | null = null;
|
||||||
|
|
||||||
|
private isVoteWeightActive: boolean;
|
||||||
|
|
||||||
|
public get showVoteWeight(): boolean {
|
||||||
|
return this.pollService.isElectronicVotingEnabled && this.isVoteWeightActive;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for user
|
* Constructor for user
|
||||||
*
|
*
|
||||||
@ -103,12 +111,17 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private pdfService: UserPdfExportService,
|
private pdfService: UserPdfExportService,
|
||||||
private groupRepo: GroupRepositoryService,
|
private groupRepo: GroupRepositoryService,
|
||||||
private constantsService: ConstantsService
|
private constantsService: ConstantsService,
|
||||||
|
private pollService: PollService,
|
||||||
|
configService: ConfigService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
this.createForm();
|
this.createForm();
|
||||||
|
|
||||||
this.constantsService.get<UserBackends>('UserBackends').subscribe(backends => (this.userBackends = backends));
|
this.constantsService.get<UserBackends>('UserBackends').subscribe(backends => (this.userBackends = backends));
|
||||||
|
configService
|
||||||
|
.get<boolean>('users_activate_vote_weight')
|
||||||
|
.subscribe(active => (this.isVoteWeightActive = active));
|
||||||
|
|
||||||
this.groupRepo.getViewModelListObservableWithoutDefaultGroup().subscribe(this.groups);
|
this.groupRepo.getViewModelListObservableWithoutDefaultGroup().subscribe(this.groups);
|
||||||
}
|
}
|
||||||
@ -157,6 +170,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
gender: [''],
|
gender: [''],
|
||||||
structure_level: [''],
|
structure_level: [''],
|
||||||
number: [''],
|
number: [''],
|
||||||
|
vote_weight: [],
|
||||||
about_me: [''],
|
about_me: [''],
|
||||||
groups_id: [''],
|
groups_id: [''],
|
||||||
is_present: [true],
|
is_present: [true],
|
||||||
|
@ -51,20 +51,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<div class="code red-warning-text">
|
<div class="code red-warning-text">
|
||||||
<span>{{ 'Title' | translate }}</span
|
<span *ngFor="let entry of headerRow; let last = last">
|
||||||
>, <span>{{ 'Given name' | translate }}</span
|
{{ entry | translate }}<span *ngIf="!last">, </span>
|
||||||
>, <span>{{ 'Surname' | translate }}</span
|
</span>
|
||||||
>, <span>{{ 'Structure level' | translate }}</span
|
|
||||||
>, <span>{{ 'Participant number' | translate }}</span
|
|
||||||
>, <span>{{ 'Groups' | translate }}</span
|
|
||||||
>, <span>{{ 'Comment' | translate }}</span
|
|
||||||
>, <span>{{ 'Is active' | translate }}</span
|
|
||||||
>, <span>{{ 'Is present' | translate }}</span
|
|
||||||
>, <span>{{ 'Is committee' | translate }}</span
|
|
||||||
>, <span>{{ 'Initial password' | translate }}</span
|
|
||||||
>, <span>{{ 'Email' | translate }}</span
|
|
||||||
>, <span>{{ 'Username' | translate }}</span
|
|
||||||
>, <span>{{ 'Gender' | translate }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@ -282,35 +271,46 @@
|
|||||||
<mat-checkbox disabled [checked]="entry.newEntry.is_active"> </mat-checkbox>
|
<mat-checkbox disabled [checked]="entry.newEntry.is_active"> </mat-checkbox>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="is_present">
|
<ng-container matColumnDef="is_present">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Is present' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Is present' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry">
|
<mat-cell *matCellDef="let entry">
|
||||||
<mat-checkbox disabled [checked]="entry.newEntry.is_present"> </mat-checkbox>
|
<mat-checkbox disabled [checked]="entry.newEntry.is_present"> </mat-checkbox>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="is_committee">
|
<ng-container matColumnDef="is_committee">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Is committee' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Is committee' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry">
|
<mat-cell *matCellDef="let entry">
|
||||||
<mat-checkbox disabled [checked]="entry.newEntry.is_committee"> </mat-checkbox>
|
<mat-checkbox disabled [checked]="entry.newEntry.is_committee"> </mat-checkbox>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="default_password">
|
<ng-container matColumnDef="default_password">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Initial password' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Initial password' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.default_password }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.default_password }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="email">
|
<ng-container matColumnDef="email">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Email' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Email' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.email }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.email }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="username">
|
<ng-container matColumnDef="username">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Username' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Username' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.username }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.username }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="gender">
|
<ng-container matColumnDef="gender">
|
||||||
<mat-header-cell *matHeaderCellDef>{{ 'Gender' | translate }}</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>{{ 'Gender' | translate }}</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.gender }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.gender }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="vote_weight">
|
||||||
|
<mat-header-cell *matHeaderCellDef>{{ 'Vote weight' | translate }}</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.vote_weight }} </mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
||||||
<mat-row [ngClass]="getStateClass(row)" *matRowDef="let row; columns: getColumnDefinition()"> </mat-row>
|
<mat-row [ngClass]="getStateClass(row)" *matRowDef="let row; columns: getColumnDefinition()"> </mat-row>
|
||||||
</table>
|
</table>
|
||||||
|
@ -21,6 +21,24 @@ import { UserImportService } from '../../services/user-import.service';
|
|||||||
export class UserImportListComponent extends BaseImportListComponentDirective<User> {
|
export class UserImportListComponent extends BaseImportListComponentDirective<User> {
|
||||||
public textAreaForm: FormGroup;
|
public textAreaForm: FormGroup;
|
||||||
|
|
||||||
|
public headerRow = [
|
||||||
|
'Title',
|
||||||
|
'Given name',
|
||||||
|
'Surname',
|
||||||
|
'Structure level',
|
||||||
|
'Participant number',
|
||||||
|
'Groups',
|
||||||
|
'Comment',
|
||||||
|
'Is active',
|
||||||
|
'Is present',
|
||||||
|
'Is a committee',
|
||||||
|
'Initial password',
|
||||||
|
'Email',
|
||||||
|
'Username',
|
||||||
|
'Gender',
|
||||||
|
'Vote weight'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for list view bases
|
* Constructor for list view bases
|
||||||
*
|
*
|
||||||
@ -47,22 +65,6 @@ export class UserImportListComponent extends BaseImportListComponentDirective<Us
|
|||||||
* Triggers an example csv download
|
* Triggers an example csv download
|
||||||
*/
|
*/
|
||||||
public downloadCsvExample(): void {
|
public downloadCsvExample(): void {
|
||||||
const headerRow = [
|
|
||||||
'Title',
|
|
||||||
'Given name',
|
|
||||||
'Surname',
|
|
||||||
'Structure level',
|
|
||||||
'Participant number',
|
|
||||||
'Groups',
|
|
||||||
'Comment',
|
|
||||||
'Is active',
|
|
||||||
'Is present',
|
|
||||||
'Is a committee',
|
|
||||||
'Initial password',
|
|
||||||
'Email',
|
|
||||||
'Username',
|
|
||||||
'Gender'
|
|
||||||
];
|
|
||||||
const rows = [
|
const rows = [
|
||||||
[
|
[
|
||||||
'Dr.',
|
'Dr.',
|
||||||
@ -78,7 +80,8 @@ export class UserImportListComponent extends BaseImportListComponentDirective<Us
|
|||||||
'initialPassword',
|
'initialPassword',
|
||||||
null,
|
null,
|
||||||
'mmustermann',
|
'mmustermann',
|
||||||
'm'
|
'm',
|
||||||
|
1.0
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
@ -94,12 +97,13 @@ export class UserImportListComponent extends BaseImportListComponentDirective<Us
|
|||||||
null,
|
null,
|
||||||
'john.doe@email.com',
|
'john.doe@email.com',
|
||||||
'jdoe',
|
'jdoe',
|
||||||
'diverse'
|
'diverse',
|
||||||
|
2.0
|
||||||
],
|
],
|
||||||
[null, 'Julia', 'Bloggs', 'London', null, null, null, null, null, null, null, null, 'jbloggs', 'f'],
|
[null, 'Julia', 'Bloggs', 'London', null, null, null, null, null, null, null, null, 'jbloggs', 'f', 1.5],
|
||||||
[null, null, 'Executive Board', null, null, null, null, null, null, 1, null, null, 'executive', null]
|
[null, null, 'Executive Board', null, null, null, null, null, null, 1, null, null, 'executive', null, 2.5]
|
||||||
];
|
];
|
||||||
this.exporter.dummyCSVExport(headerRow, rows, `${this.translate.instant('participants-example')}.csv`);
|
this.exporter.dummyCSVExport(this.headerRow, rows, `${this.translate.instant('participants-example')}.csv`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,18 +30,26 @@
|
|||||||
(dataSourceChange)="onDataSourceChange($event)"
|
(dataSourceChange)="onDataSourceChange($event)"
|
||||||
>
|
>
|
||||||
<!-- Name column -->
|
<!-- Name column -->
|
||||||
<div *pblNgridCellDef="'short_name'; value as name; row as user; rowContext as rowContext" class="cell-slot fill">
|
<div *pblNgridCellDef="'short_name'; row as user; rowContext as rowContext" class="cell-slot fill">
|
||||||
<a class="detail-link" [routerLink]="user.id" *ngIf="!isMultiSelect"></a>
|
<a class="detail-link" [routerLink]="user.id" *ngIf="!isMultiSelect"></a>
|
||||||
<div class="nameCell">
|
<div class="nameCell">
|
||||||
<span>{{ name }}</span>
|
<div>
|
||||||
</div>
|
<div>{{ user.short_name }}</div>
|
||||||
<div class="icon-group">
|
<div class="user-subtitle" *ngIf="showVoteWeight">
|
||||||
<mat-icon matTooltip="{{ 'Is committee' | translate }}" *ngIf="user.is_committee">account_balance</mat-icon>
|
{{ 'Vote weight' | translate }}: {{ user.vote_weight }}
|
||||||
<mat-icon
|
</div>
|
||||||
matTooltip="{{ 'Inactive' | translate }}"
|
</div>
|
||||||
*ngIf="!user.is_active && this.operator.hasPerms('users.see_extra')"
|
<div class="icon-group">
|
||||||
>block</mat-icon
|
<mat-icon matTooltip="{{ 'Is committee' | translate }}" *ngIf="user.is_committee">
|
||||||
>
|
account_balance
|
||||||
|
</mat-icon>
|
||||||
|
<mat-icon
|
||||||
|
matTooltip="{{ 'Inactive' | translate }}"
|
||||||
|
*ngIf="!user.is_active && this.operator.hasPerms('users.see_extra')"
|
||||||
|
>
|
||||||
|
block
|
||||||
|
</mat-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -29,5 +29,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-group {
|
.icon-group {
|
||||||
|
margin-left: 1em;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import { PromptService } from 'app/core/ui-services/prompt.service';
|
|||||||
import { genders } from 'app/shared/models/users/user';
|
import { genders } from 'app/shared/models/users/user';
|
||||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||||
|
import { PollService } from 'app/site/polls/services/poll.service';
|
||||||
import { UserFilterListService } from '../../services/user-filter-list.service';
|
import { UserFilterListService } from '../../services/user-filter-list.service';
|
||||||
import { UserPdfExportService } from '../../services/user-pdf-export.service';
|
import { UserPdfExportService } from '../../services/user-pdf-export.service';
|
||||||
import { UserSortListService } from '../../services/user-sort-list.service';
|
import { UserSortListService } from '../../services/user-sort-list.service';
|
||||||
@ -98,6 +99,8 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
|||||||
return this._presenceViewConfigured && this.operator.hasPerms('users.can_manage');
|
return this._presenceViewConfigured && this.operator.hasPerms('users.can_manage');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isVoteWeightActive: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to check for main button permissions
|
* Helper to check for main button permissions
|
||||||
*
|
*
|
||||||
@ -107,6 +110,10 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
|||||||
return this.operator.hasPerms('users.can_manage');
|
return this.operator.hasPerms('users.can_manage');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get showVoteWeight(): boolean {
|
||||||
|
return this.pollService.isElectronicVotingEnabled && this.isVoteWeightActive;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the columns to show
|
* Define the columns to show
|
||||||
*/
|
*/
|
||||||
@ -173,13 +180,15 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
|||||||
public sortService: UserSortListService,
|
public sortService: UserSortListService,
|
||||||
config: ConfigService,
|
config: ConfigService,
|
||||||
private userPdf: UserPdfExportService,
|
private userPdf: UserPdfExportService,
|
||||||
private dialog: MatDialog
|
private dialog: MatDialog,
|
||||||
|
private pollService: PollService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar, storage);
|
super(titleService, translate, matSnackBar, storage);
|
||||||
|
|
||||||
// enable multiSelect for this listView
|
// enable multiSelect for this listView
|
||||||
this.canMultiSelect = true;
|
this.canMultiSelect = true;
|
||||||
config.get<boolean>('users_enable_presence_view').subscribe(state => (this._presenceViewConfigured = state));
|
config.get<boolean>('users_enable_presence_view').subscribe(state => (this._presenceViewConfigured = state));
|
||||||
|
config.get<boolean>('users_activate_vote_weight').subscribe(active => (this.isVoteWeightActive = active));
|
||||||
config.get<boolean>(this.selfPresentConfStr).subscribe(allowed => (this.allowSelfSetPresent = allowed));
|
config.get<boolean>(this.selfPresentConfStr).subscribe(allowed => (this.allowSelfSetPresent = allowed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,14 @@ export class UserFilterListService extends BaseFilterListService<ViewUser> {
|
|||||||
{ condition: true, label: this.translate.instant('Got an email') },
|
{ condition: true, label: this.translate.instant('Got an email') },
|
||||||
{ condition: false, label: this.translate.instant("Didn't get an email") }
|
{ condition: false, label: this.translate.instant("Didn't get an email") }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: 'isVoteWeightOne',
|
||||||
|
label: this.translate.instant('Vote Weight'),
|
||||||
|
options: [
|
||||||
|
{ condition: false, label: this.translate.instant('Has changed vote weight') },
|
||||||
|
{ condition: true, label: this.translate.instant('Has unchanged vote weight') }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
return staticFilterOptions.concat(this.userGroupFilterOptions);
|
return staticFilterOptions.concat(this.userGroupFilterOptions);
|
||||||
|
@ -33,7 +33,8 @@ export class UserImportService extends BaseImportService<User> {
|
|||||||
'default_password',
|
'default_password',
|
||||||
'email',
|
'email',
|
||||||
'username',
|
'username',
|
||||||
'gender'
|
'gender',
|
||||||
|
'vote_weight'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,6 +118,13 @@ export class UserImportService extends BaseImportService<User> {
|
|||||||
case 'number':
|
case 'number':
|
||||||
newViewUser.number = line[idx];
|
newViewUser.number = line[idx];
|
||||||
break;
|
break;
|
||||||
|
case 'vote_weight':
|
||||||
|
if (!line[idx]) {
|
||||||
|
newViewUser[this.expectedHeader[idx]] = 1;
|
||||||
|
} else {
|
||||||
|
newViewUser[this.expectedHeader[idx]] = line[idx];
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
newViewUser[this.expectedHeader[idx]] = line[idx];
|
newViewUser[this.expectedHeader[idx]] = line[idx];
|
||||||
break;
|
break;
|
||||||
|
@ -31,6 +31,7 @@ export class UserSortListService extends BaseSortListService<ViewUser> {
|
|||||||
{ property: 'is_committee', label: 'Is committee' },
|
{ property: 'is_committee', label: 'Is committee' },
|
||||||
{ property: 'number', label: 'Participant number' },
|
{ property: 'number', label: 'Participant number' },
|
||||||
{ property: 'structure_level', label: 'Structure level' },
|
{ property: 'structure_level', label: 'Structure level' },
|
||||||
|
{ property: 'vote_weight', label: 'Vote weight' },
|
||||||
{ property: 'comment' }
|
{ property: 'comment' }
|
||||||
// TODO email send?
|
// TODO email send?
|
||||||
];
|
];
|
||||||
|
@ -3,6 +3,7 @@ from decimal import Decimal
|
|||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
|
from openslides.core.config import config
|
||||||
from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet
|
from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet
|
||||||
from openslides.utils.auth import has_perm
|
from openslides.utils.auth import has_perm
|
||||||
from openslides.utils.autoupdate import inform_changed_data
|
from openslides.utils.autoupdate import inform_changed_data
|
||||||
@ -489,14 +490,20 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
# skip creating votes with empty weights
|
# skip creating votes with empty weights
|
||||||
if amount == 0:
|
if amount == 0:
|
||||||
continue
|
continue
|
||||||
|
weight = Decimal(amount)
|
||||||
|
if config["users_activate_vote_weight"]:
|
||||||
|
weight *= user.vote_weight
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option, user=user, weight=Decimal(amount), value="Y"
|
option=option, user=user, weight=weight, value="Y"
|
||||||
)
|
)
|
||||||
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
|
||||||
option = options[0]
|
option = options[0]
|
||||||
|
weight = (
|
||||||
|
user.vote_weight if config["users_activate_vote_weight"] else Decimal(1)
|
||||||
|
)
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option, user=user, weight=Decimal(1), value=data
|
option=option, user=user, weight=weight, value=data
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
inform_changed_data(option)
|
inform_changed_data(option)
|
||||||
@ -512,13 +519,15 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
vote_user is the one put into the vote
|
vote_user is the one put into the vote
|
||||||
"""
|
"""
|
||||||
options = poll.get_options()
|
options = poll.get_options()
|
||||||
|
weight = (
|
||||||
|
check_user.vote_weight
|
||||||
|
if config["users_activate_vote_weight"]
|
||||||
|
else Decimal(1)
|
||||||
|
)
|
||||||
for option_id, result in data.items():
|
for option_id, result in data.items():
|
||||||
option = options.get(pk=option_id)
|
option = options.get(pk=option_id)
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option,
|
option=option, user=vote_user, value=result, weight=weight,
|
||||||
user=vote_user,
|
|
||||||
value=result,
|
|
||||||
weight=check_user.vote_weight,
|
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
inform_changed_data(option, no_delete_on_restriction=True)
|
inform_changed_data(option, no_delete_on_restriction=True)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from decimal import Decimal
|
||||||
from typing import List, Set
|
from typing import List, Set
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
@ -1227,16 +1228,20 @@ class MotionPollViewSet(BasePollViewSet):
|
|||||||
VotedModel.objects.create(motionpoll=poll, user=user)
|
VotedModel.objects.create(motionpoll=poll, user=user)
|
||||||
|
|
||||||
def handle_named_vote(self, data, poll, user):
|
def handle_named_vote(self, data, poll, user):
|
||||||
self.handle_named_and_pseudoanonymous_vote(data, user.vote_weight, user, poll)
|
self.handle_named_and_pseudoanonymous_vote(data, user, user, poll)
|
||||||
|
|
||||||
def handle_pseudoanonymous_vote(self, data, poll, user):
|
def handle_pseudoanonymous_vote(self, data, poll, user):
|
||||||
self.handle_named_and_pseudoanonymous_vote(data, user.vote_weight, None, poll)
|
self.handle_named_and_pseudoanonymous_vote(data, user, None, poll)
|
||||||
|
|
||||||
def handle_named_and_pseudoanonymous_vote(self, data, weight, user, poll):
|
def handle_named_and_pseudoanonymous_vote(self, data, weight_user, vote_user, poll):
|
||||||
option = poll.options.get()
|
option = poll.options.get()
|
||||||
vote = MotionVote.objects.create(user=user, option=option)
|
vote = MotionVote.objects.create(user=vote_user, option=option)
|
||||||
vote.value = data
|
vote.value = data
|
||||||
vote.weight = weight
|
vote.weight = (
|
||||||
|
weight_user.vote_weight
|
||||||
|
if config["users_activate_vote_weight"]
|
||||||
|
else Decimal(1)
|
||||||
|
)
|
||||||
vote.save(no_delete_on_restriction=True)
|
vote.save(no_delete_on_restriction=True)
|
||||||
inform_changed_data(option)
|
inform_changed_data(option)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from django.conf import settings
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from ..core.config import config
|
||||||
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
|
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
|
||||||
from ..utils.models import SET_NULL_AND_AUTOUPDATE
|
from ..utils.models import SET_NULL_AND_AUTOUPDATE
|
||||||
|
|
||||||
@ -184,7 +185,7 @@ class BasePoll(models.Model):
|
|||||||
if self.type == self.TYPE_ANALOG:
|
if self.type == self.TYPE_ANALOG:
|
||||||
return self.db_votesvalid
|
return self.db_votesvalid
|
||||||
else:
|
else:
|
||||||
return Decimal(self.amount_users_voted())
|
return Decimal(self.amount_users_voted_with_individual_weight())
|
||||||
|
|
||||||
def set_votesvalid(self, value):
|
def set_votesvalid(self, value):
|
||||||
if self.type != self.TYPE_ANALOG:
|
if self.type != self.TYPE_ANALOG:
|
||||||
@ -210,7 +211,7 @@ class BasePoll(models.Model):
|
|||||||
if self.type == self.TYPE_ANALOG:
|
if self.type == self.TYPE_ANALOG:
|
||||||
return self.db_votescast
|
return self.db_votescast
|
||||||
else:
|
else:
|
||||||
return Decimal(self.amount_users_voted())
|
return Decimal(self.amount_users_voted_with_individual_weight())
|
||||||
|
|
||||||
def set_votescast(self, value):
|
def set_votescast(self, value):
|
||||||
if self.type != self.TYPE_ANALOG:
|
if self.type != self.TYPE_ANALOG:
|
||||||
@ -219,8 +220,11 @@ class BasePoll(models.Model):
|
|||||||
|
|
||||||
votescast = property(get_votescast, set_votescast)
|
votescast = property(get_votescast, set_votescast)
|
||||||
|
|
||||||
def amount_users_voted(self):
|
def amount_users_voted_with_individual_weight(self):
|
||||||
return len(self.voted.all())
|
if config["users_activate_vote_weight"]:
|
||||||
|
return sum(user.vote_weight for user in self.voted.all())
|
||||||
|
else:
|
||||||
|
return len(self.voted.all())
|
||||||
|
|
||||||
def create_options(self):
|
def create_options(self):
|
||||||
""" Should be called after creation of this model. """
|
""" Should be called after creation of this model. """
|
||||||
|
@ -44,6 +44,15 @@ def get_config_variables():
|
|||||||
group="Participants",
|
group="Participants",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="users_activate_vote_weight",
|
||||||
|
default_value=False,
|
||||||
|
input_type="boolean",
|
||||||
|
label="Activate vote weight",
|
||||||
|
weight=513,
|
||||||
|
group="Participants",
|
||||||
|
)
|
||||||
|
|
||||||
# PDF
|
# PDF
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
|
@ -15,6 +15,7 @@ from openslides.assignments.models import (
|
|||||||
AssignmentPoll,
|
AssignmentPoll,
|
||||||
AssignmentVote,
|
AssignmentVote,
|
||||||
)
|
)
|
||||||
|
from openslides.core.config import config
|
||||||
from openslides.poll.models import BasePoll
|
from openslides.poll.models import BasePoll
|
||||||
from openslides.utils.auth import get_group_model
|
from openslides.utils.auth import get_group_model
|
||||||
from openslides.utils.autoupdate import inform_changed_data
|
from openslides.utils.autoupdate import inform_changed_data
|
||||||
@ -982,6 +983,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
self.assertEqual(poll.votescast, Decimal("1"))
|
self.assertEqual(poll.votescast, Decimal("1"))
|
||||||
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), Decimal("1"))
|
||||||
option1 = poll.options.get(pk=1)
|
option1 = poll.options.get(pk=1)
|
||||||
option2 = poll.options.get(pk=2)
|
option2 = poll.options.get(pk=2)
|
||||||
option3 = poll.options.get(pk=3)
|
option3 = poll.options.get(pk=3)
|
||||||
@ -995,6 +997,43 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.assertEqual(option3.no, Decimal("0"))
|
self.assertEqual(option3.no, Decimal("0"))
|
||||||
self.assertEqual(option3.abstain, Decimal("1"))
|
self.assertEqual(option3.abstain, Decimal("1"))
|
||||||
|
|
||||||
|
def test_vote_with_voteweight(self):
|
||||||
|
config["users_activate_vote_weight"] = True
|
||||||
|
self.admin.vote_weight = weight = Decimal("4.2")
|
||||||
|
self.admin.save()
|
||||||
|
self.add_candidate()
|
||||||
|
self.add_candidate()
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"1": "Y", "2": "N", "3": "A"},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(AssignmentVote.objects.count(), 3)
|
||||||
|
poll = AssignmentPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, weight)
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, weight)
|
||||||
|
self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED)
|
||||||
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), weight)
|
||||||
|
option1 = poll.options.get(pk=1)
|
||||||
|
option2 = poll.options.get(pk=2)
|
||||||
|
option3 = poll.options.get(pk=3)
|
||||||
|
self.assertEqual(option1.yes, weight)
|
||||||
|
self.assertEqual(option1.no, Decimal("0"))
|
||||||
|
self.assertEqual(option1.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option2.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option2.no, weight)
|
||||||
|
self.assertEqual(option2.abstain, Decimal("0"))
|
||||||
|
self.assertEqual(option3.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option3.no, Decimal("0"))
|
||||||
|
self.assertEqual(option3.abstain, weight)
|
||||||
|
|
||||||
|
def test_vote_without_voteweight(self):
|
||||||
|
self.admin.vote_weight = Decimal("4.2")
|
||||||
|
self.admin.save()
|
||||||
|
self.test_vote()
|
||||||
|
|
||||||
def test_change_vote(self):
|
def test_change_vote(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@ -2233,7 +2272,7 @@ class PseudoanonymizeAssignmentPoll(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.get_votes().count(), 2)
|
self.assertEqual(poll.get_votes().count(), 2)
|
||||||
self.assertEqual(poll.amount_users_voted(), 2)
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), 2)
|
||||||
self.assertEqual(poll.votesvalid, Decimal("2"))
|
self.assertEqual(poll.votesvalid, Decimal("2"))
|
||||||
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
self.assertEqual(poll.votescast, Decimal("2"))
|
self.assertEqual(poll.votescast, Decimal("2"))
|
||||||
|
@ -763,6 +763,36 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.assertEqual(option.abstain, Decimal("0"))
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
vote = option.votes.get()
|
vote = option.votes.get()
|
||||||
self.assertEqual(vote.user, self.admin)
|
self.assertEqual(vote.user, self.admin)
|
||||||
|
self.assertEqual(vote.weight, Decimal("1"))
|
||||||
|
|
||||||
|
def test_vote_with_voteweight(self):
|
||||||
|
config["users_activate_vote_weight"] = True
|
||||||
|
self.start_poll()
|
||||||
|
self.make_admin_delegate()
|
||||||
|
self.make_admin_present()
|
||||||
|
self.admin.vote_weight = weight = Decimal("3.5")
|
||||||
|
self.admin.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, weight)
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, weight)
|
||||||
|
self.assertEqual(poll.get_votes().count(), 1)
|
||||||
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), weight)
|
||||||
|
option = poll.options.get()
|
||||||
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option.no, Decimal("0"))
|
||||||
|
self.assertEqual(option.abstain, weight)
|
||||||
|
vote = option.votes.get()
|
||||||
|
self.assertEqual(vote.weight, weight)
|
||||||
|
|
||||||
|
def test_vote_without_voteweight(self):
|
||||||
|
self.admin.vote_weight = Decimal("3.5")
|
||||||
|
self.admin.save()
|
||||||
|
self.test_vote()
|
||||||
|
|
||||||
def test_change_vote(self):
|
def test_change_vote(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
@ -1155,7 +1185,7 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
self.assertEqual(poll.votescast, Decimal("1"))
|
self.assertEqual(poll.votescast, Decimal("1"))
|
||||||
self.assertEqual(poll.get_votes().count(), 1)
|
self.assertEqual(poll.get_votes().count(), 1)
|
||||||
self.assertEqual(poll.amount_users_voted(), 1)
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), 1)
|
||||||
option = poll.options.get()
|
option = poll.options.get()
|
||||||
self.assertEqual(option.yes, Decimal("0"))
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
self.assertEqual(option.no, Decimal("1"))
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
@ -1378,7 +1408,7 @@ class PseudoanonymizeMotionPoll(TestCase):
|
|||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
self.assertEqual(poll.get_votes().count(), 2)
|
self.assertEqual(poll.get_votes().count(), 2)
|
||||||
self.assertEqual(poll.amount_users_voted(), 2)
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), 2)
|
||||||
self.assertEqual(poll.votesvalid, Decimal("2"))
|
self.assertEqual(poll.votesvalid, Decimal("2"))
|
||||||
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
self.assertEqual(poll.votescast, Decimal("2"))
|
self.assertEqual(poll.votescast, Decimal("2"))
|
||||||
@ -1446,7 +1476,7 @@ class ResetMotionPoll(TestCase):
|
|||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
self.assertEqual(poll.get_votes().count(), 0)
|
self.assertEqual(poll.get_votes().count(), 0)
|
||||||
self.assertEqual(poll.amount_users_voted(), 0)
|
self.assertEqual(poll.amount_users_voted_with_individual_weight(), 0)
|
||||||
self.assertEqual(poll.votesvalid, None)
|
self.assertEqual(poll.votesvalid, None)
|
||||||
self.assertEqual(poll.votesinvalid, None)
|
self.assertEqual(poll.votesinvalid, None)
|
||||||
self.assertEqual(poll.votescast, None)
|
self.assertEqual(poll.votescast, None)
|
||||||
|
Loading…
Reference in New Issue
Block a user