diff --git a/client/src/app/shared/models/base/base-decimal-model.ts b/client/src/app/shared/models/base/base-decimal-model.ts index 5a893f5c7..86ea4c11c 100644 --- a/client/src/app/shared/models/base/base-decimal-model.ts +++ b/client/src/app/shared/models/base/base-decimal-model.ts @@ -7,7 +7,7 @@ export abstract class BaseDecimalModel extends BaseModel { if (input && typeof input === 'object') { this.getDecimalFields().forEach(field => { if (input[field] !== undefined) { - input[field] = parseInt(input[field], 10); + input[field] = parseFloat(input[field]); } }); } diff --git a/client/src/app/shared/models/users/user.ts b/client/src/app/shared/models/users/user.ts index 00179e534..174c5b5aa 100644 --- a/client/src/app/shared/models/users/user.ts +++ b/client/src/app/shared/models/users/user.ts @@ -37,6 +37,10 @@ export class User extends BaseDecimalModel { public auth_type?: UserAuthType; public vote_weight: number; + public get isVoteWeightOne(): boolean { + return this.vote_weight === 1; + } + public constructor(input?: Partial) { super(User.COLLECTIONSTRING, input); } diff --git a/client/src/app/shared/pipes/parse-poll-number.pipe.ts b/client/src/app/shared/pipes/parse-poll-number.pipe.ts index 1f49249ae..c14763755 100644 --- a/client/src/app/shared/pipes/parse-poll-number.pipe.ts +++ b/client/src/app/shared/pipes/parse-poll-number.pipe.ts @@ -11,14 +11,13 @@ export class ParsePollNumberPipe implements PipeTransform { public constructor(private translate: TranslateService) {} public transform(value: number): number | string { - const input = Math.trunc(value); - switch (input) { + switch (value) { case VOTE_MAJORITY: return this.translate.instant('majority'); case VOTE_UNDOCUMENTED: return this.translate.instant('undocumented'); default: - return input; + return value; } } } diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html index ccd9afe98..483d891b2 100644 --- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html +++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html @@ -70,8 +70,13 @@
{{ vote.user.getShortName() }} -
- {{ vote.user.getLevelAndNumber() }} +
+
+ {{ vote.user.getLevelAndNumber() }} +
+
+ {{ 'Vote weight' | translate }}: {{ vote.user.vote_weight }} +
diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts index 954f1b0a1..2f4c28a00 100644 --- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts +++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts @@ -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 { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-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 { VoteValue } from 'app/shared/models/poll/base-vote'; import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component'; @@ -32,6 +33,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent('users_activate_vote_weight') + .subscribe(active => (this.isVoteWeightActive = active)); } protected createVotesData(): void { diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html index 6e13dca24..4f587078a 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html @@ -45,7 +45,7 @@ [filterProps]="filterProps" [allowProjector]="false" [fullScreen]="true" - [vScrollFixed]="60" + [vScrollFixed]="-1" listStorageKey="motion-poll-vote" [cssClasses]="{ 'single-votes-table': true }" > @@ -56,7 +56,18 @@
-
{{ vote.user.getFullName() }}
+
+ {{ vote.user.getShortName() }} +
+
+ {{ vote.user.getLevelAndNumber() }} +
+ +
+ {{ 'Vote weight' | translate }}: {{ vote.user.vote_weight }} +
+
+
{{ 'Anonymous' | translate }}
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts index b30ec81c3..cdd6072d8 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts @@ -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 { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-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 { ViewMotion } from 'app/site/motions/models/view-motion'; import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; @@ -41,6 +42,8 @@ export class MotionPollDetailComponent extends BasePollDetailComponent('users_activate_vote_weight') + .subscribe(active => (this.isVoteWeightActive = active)); } protected createVotesData(): void { diff --git a/client/src/app/site/motions/services/motion-pdf.service.ts b/client/src/app/site/motions/services/motion-pdf.service.ts index b9997c685..6c751db0b 100644 --- a/client/src/app/site/motions/services/motion-pdf.service.ts +++ b/client/src/app/site/motions/services/motion-pdf.service.ts @@ -381,7 +381,12 @@ export class MotionPdfService { column1.push(`${votingOption}:`); if (value.showPercent) { 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 { column2.push(''); } diff --git a/client/src/app/site/users/components/user-detail/user-detail.component.html b/client/src/app/site/users/components/user-detail/user-detail.component.html index 920b15fa1..27658f3c2 100644 --- a/client/src/app/site/users/components/user-detail/user-detail.component.html +++ b/client/src/app/site/users/components/user-detail/user-detail.component.html @@ -111,7 +111,13 @@
- + + - + + + + + + +
@@ -275,6 +299,12 @@
+ +
+

{{ 'Vote weight' | translate }}

+ {{ user.vote_weight }} +
+

{{ 'Initial password' | translate }}

diff --git a/client/src/app/site/users/components/user-detail/user-detail.component.ts b/client/src/app/site/users/components/user-detail/user-detail.component.ts index 94a5ca3be..89dcbff92 100644 --- a/client/src/app/site/users/components/user-detail/user-detail.component.ts +++ b/client/src/app/site/users/components/user-detail/user-detail.component.ts @@ -11,10 +11,12 @@ import { ConstantsService } from 'app/core/core-services/constants.service'; import { OperatorService } from 'app/core/core-services/operator.service'; import { GroupRepositoryService } from 'app/core/repositories/users/group-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 { genders } from 'app/shared/models/users/user'; import { OneOfValidator } from 'app/shared/validators/one-of-validator'; 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 { ViewGroup } from '../../models/view-group'; import { ViewUser } from '../../models/view-user'; @@ -76,6 +78,12 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit { private userBackends: UserBackends | null = null; + private isVoteWeightActive: boolean; + + public get showVoteWeight(): boolean { + return this.pollService.isElectronicVotingEnabled && this.isVoteWeightActive; + } + /** * Constructor for user * @@ -103,12 +111,17 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit { private promptService: PromptService, private pdfService: UserPdfExportService, private groupRepo: GroupRepositoryService, - private constantsService: ConstantsService + private constantsService: ConstantsService, + private pollService: PollService, + configService: ConfigService ) { super(title, translate, matSnackBar); this.createForm(); this.constantsService.get('UserBackends').subscribe(backends => (this.userBackends = backends)); + configService + .get('users_activate_vote_weight') + .subscribe(active => (this.isVoteWeightActive = active)); this.groupRepo.getViewModelListObservableWithoutDefaultGroup().subscribe(this.groups); } @@ -157,6 +170,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit { gender: [''], structure_level: [''], number: [''], + vote_weight: [], about_me: [''], groups_id: [''], is_present: [true], diff --git a/client/src/app/site/users/components/user-import/user-import-list.component.html b/client/src/app/site/users/components/user-import/user-import-list.component.html index 7af5c7bb3..841c2e618 100644 --- a/client/src/app/site/users/components/user-import/user-import-list.component.html +++ b/client/src/app/site/users/components/user-import/user-import-list.component.html @@ -51,20 +51,9 @@
- {{ 'Title' | translate }}, {{ 'Given name' | translate }}, {{ 'Surname' | translate }}, {{ 'Structure level' | translate }}, {{ 'Participant number' | translate }}, {{ 'Groups' | translate }}, {{ 'Comment' | translate }}, {{ 'Is active' | translate }}, {{ 'Is present' | translate }}, {{ 'Is committee' | translate }}, {{ 'Initial password' | translate }}, {{ 'Email' | translate }}, {{ 'Username' | translate }}, {{ 'Gender' | translate }} + + {{ entry | translate }}, +
  • @@ -282,35 +271,46 @@ + {{ 'Is present' | translate }} + {{ 'Is committee' | translate }} + {{ 'Initial password' | translate }} {{ entry.newEntry.default_password }} + {{ 'Email' | translate }} {{ entry.newEntry.email }} + {{ 'Username' | translate }} {{ entry.newEntry.username }} + {{ 'Gender' | translate }} {{ entry.newEntry.gender }} + + {{ 'Vote weight' | translate }} + {{ entry.newEntry.vote_weight }} + + diff --git a/client/src/app/site/users/components/user-import/user-import-list.component.ts b/client/src/app/site/users/components/user-import/user-import-list.component.ts index 488d2c065..be4987692 100644 --- a/client/src/app/site/users/components/user-import/user-import-list.component.ts +++ b/client/src/app/site/users/components/user-import/user-import-list.component.ts @@ -21,6 +21,24 @@ import { UserImportService } from '../../services/user-import.service'; export class UserImportListComponent extends BaseImportListComponentDirective { 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 * @@ -47,22 +65,6 @@ export class UserImportListComponent extends BaseImportListComponentDirective -
    +
    - {{ name }} -
    -
    - account_balance - block +
    +
    {{ user.short_name }}
    +
    + {{ 'Vote weight' | translate }}: {{ user.vote_weight }} +
    +
    +
    + + account_balance + + + block + +
    diff --git a/client/src/app/site/users/components/user-list/user-list.component.scss b/client/src/app/site/users/components/user-list/user-list.component.scss index 60e0ed38d..ed3df07ec 100644 --- a/client/src/app/site/users/components/user-list/user-list.component.scss +++ b/client/src/app/site/users/components/user-list/user-list.component.scss @@ -29,5 +29,6 @@ } .icon-group { + margin-left: 1em; z-index: 3; } diff --git a/client/src/app/site/users/components/user-list/user-list.component.ts b/client/src/app/site/users/components/user-list/user-list.component.ts index afb61c40d..7350fb726 100644 --- a/client/src/app/site/users/components/user-list/user-list.component.ts +++ b/client/src/app/site/users/components/user-list/user-list.component.ts @@ -19,6 +19,7 @@ import { PromptService } from 'app/core/ui-services/prompt.service'; import { genders } from 'app/shared/models/users/user'; import { infoDialogSettings } from 'app/shared/utils/dialog-settings'; 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 { UserPdfExportService } from '../../services/user-pdf-export.service'; import { UserSortListService } from '../../services/user-sort-list.service'; @@ -98,6 +99,8 @@ export class UserListComponent extends BaseListViewComponent implement return this._presenceViewConfigured && this.operator.hasPerms('users.can_manage'); } + private isVoteWeightActive: boolean; + /** * Helper to check for main button permissions * @@ -107,6 +110,10 @@ export class UserListComponent extends BaseListViewComponent implement return this.operator.hasPerms('users.can_manage'); } + public get showVoteWeight(): boolean { + return this.pollService.isElectronicVotingEnabled && this.isVoteWeightActive; + } + /** * Define the columns to show */ @@ -173,13 +180,15 @@ export class UserListComponent extends BaseListViewComponent implement public sortService: UserSortListService, config: ConfigService, private userPdf: UserPdfExportService, - private dialog: MatDialog + private dialog: MatDialog, + private pollService: PollService ) { super(titleService, translate, matSnackBar, storage); // enable multiSelect for this listView this.canMultiSelect = true; config.get('users_enable_presence_view').subscribe(state => (this._presenceViewConfigured = state)); + config.get('users_activate_vote_weight').subscribe(active => (this.isVoteWeightActive = active)); config.get(this.selfPresentConfStr).subscribe(allowed => (this.allowSelfSetPresent = allowed)); } diff --git a/client/src/app/site/users/services/user-filter-list.service.ts b/client/src/app/site/users/services/user-filter-list.service.ts index 520b7f9bd..8c4e7765a 100644 --- a/client/src/app/site/users/services/user-filter-list.service.ts +++ b/client/src/app/site/users/services/user-filter-list.service.ts @@ -85,6 +85,14 @@ export class UserFilterListService extends BaseFilterListService { { condition: true, label: this.translate.instant('Got 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); diff --git a/client/src/app/site/users/services/user-import.service.ts b/client/src/app/site/users/services/user-import.service.ts index dcdfbeb85..6e419f87d 100644 --- a/client/src/app/site/users/services/user-import.service.ts +++ b/client/src/app/site/users/services/user-import.service.ts @@ -33,7 +33,8 @@ export class UserImportService extends BaseImportService { 'default_password', 'email', 'username', - 'gender' + 'gender', + 'vote_weight' ]; /** @@ -117,6 +118,13 @@ export class UserImportService extends BaseImportService { case 'number': newViewUser.number = line[idx]; break; + case 'vote_weight': + if (!line[idx]) { + newViewUser[this.expectedHeader[idx]] = 1; + } else { + newViewUser[this.expectedHeader[idx]] = line[idx]; + } + break; default: newViewUser[this.expectedHeader[idx]] = line[idx]; break; diff --git a/client/src/app/site/users/services/user-sort-list.service.ts b/client/src/app/site/users/services/user-sort-list.service.ts index 22023ab90..e1805b5f0 100644 --- a/client/src/app/site/users/services/user-sort-list.service.ts +++ b/client/src/app/site/users/services/user-sort-list.service.ts @@ -31,6 +31,7 @@ export class UserSortListService extends BaseSortListService { { property: 'is_committee', label: 'Is committee' }, { property: 'number', label: 'Participant number' }, { property: 'structure_level', label: 'Structure level' }, + { property: 'vote_weight', label: 'Vote weight' }, { property: 'comment' } // TODO email send? ]; diff --git a/openslides/assignments/views.py b/openslides/assignments/views.py index 6aaba3356..48c5c0727 100644 --- a/openslides/assignments/views.py +++ b/openslides/assignments/views.py @@ -3,6 +3,7 @@ from decimal import Decimal from django.contrib.auth import get_user_model from django.db import transaction +from openslides.core.config import config from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet from openslides.utils.auth import has_perm from openslides.utils.autoupdate import inform_changed_data @@ -489,14 +490,20 @@ class AssignmentPollViewSet(BasePollViewSet): # skip creating votes with empty weights if amount == 0: continue + weight = Decimal(amount) + if config["users_activate_vote_weight"]: + weight *= user.vote_weight 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) else: # global_no or global_abstain option = options[0] + weight = ( + user.vote_weight if config["users_activate_vote_weight"] else Decimal(1) + ) 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(option) @@ -512,13 +519,15 @@ class AssignmentPollViewSet(BasePollViewSet): vote_user is the one put into the vote """ 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(): option = options.get(pk=option_id) vote = AssignmentVote.objects.create( - option=option, - user=vote_user, - value=result, - weight=check_user.vote_weight, + option=option, user=vote_user, value=result, weight=weight, ) inform_changed_data(vote, no_delete_on_restriction=True) inform_changed_data(option, no_delete_on_restriction=True) diff --git a/openslides/motions/views.py b/openslides/motions/views.py index 8f9189e18..a7a699242 100644 --- a/openslides/motions/views.py +++ b/openslides/motions/views.py @@ -1,3 +1,4 @@ +from decimal import Decimal from typing import List, Set import jsonschema @@ -1227,16 +1228,20 @@ class MotionPollViewSet(BasePollViewSet): VotedModel.objects.create(motionpoll=poll, user=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): - 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() - vote = MotionVote.objects.create(user=user, option=option) + vote = MotionVote.objects.create(user=vote_user, option=option) 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) inform_changed_data(option) diff --git a/openslides/poll/models.py b/openslides/poll/models.py index 305535b41..555d0494b 100644 --- a/openslides/poll/models.py +++ b/openslides/poll/models.py @@ -5,6 +5,7 @@ from django.conf import settings from django.core.validators import MinValueValidator from django.db import models +from ..core.config import config from ..utils.autoupdate import inform_changed_data, inform_deleted_data from ..utils.models import SET_NULL_AND_AUTOUPDATE @@ -184,7 +185,7 @@ class BasePoll(models.Model): if self.type == self.TYPE_ANALOG: return self.db_votesvalid else: - return Decimal(self.amount_users_voted()) + return Decimal(self.amount_users_voted_with_individual_weight()) def set_votesvalid(self, value): if self.type != self.TYPE_ANALOG: @@ -210,7 +211,7 @@ class BasePoll(models.Model): if self.type == self.TYPE_ANALOG: return self.db_votescast else: - return Decimal(self.amount_users_voted()) + return Decimal(self.amount_users_voted_with_individual_weight()) def set_votescast(self, value): if self.type != self.TYPE_ANALOG: @@ -219,8 +220,11 @@ class BasePoll(models.Model): votescast = property(get_votescast, set_votescast) - def amount_users_voted(self): - return len(self.voted.all()) + def amount_users_voted_with_individual_weight(self): + 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): """ Should be called after creation of this model. """ diff --git a/openslides/users/config_variables.py b/openslides/users/config_variables.py index e05a009cc..52d2d04f8 100644 --- a/openslides/users/config_variables.py +++ b/openslides/users/config_variables.py @@ -44,6 +44,15 @@ def get_config_variables(): group="Participants", ) + yield ConfigVariable( + name="users_activate_vote_weight", + default_value=False, + input_type="boolean", + label="Activate vote weight", + weight=513, + group="Participants", + ) + # PDF yield ConfigVariable( diff --git a/tests/integration/assignments/test_polls.py b/tests/integration/assignments/test_polls.py index 6d0575a06..579e514fb 100644 --- a/tests/integration/assignments/test_polls.py +++ b/tests/integration/assignments/test_polls.py @@ -15,6 +15,7 @@ from openslides.assignments.models import ( AssignmentPoll, AssignmentVote, ) +from openslides.core.config import config from openslides.poll.models import BasePoll from openslides.utils.auth import get_group_model from openslides.utils.autoupdate import inform_changed_data @@ -982,6 +983,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass): self.assertEqual(poll.votesinvalid, Decimal("0")) self.assertEqual(poll.votescast, Decimal("1")) self.assertEqual(poll.state, AssignmentPoll.STATE_STARTED) + self.assertEqual(poll.amount_users_voted_with_individual_weight(), Decimal("1")) option1 = poll.options.get(pk=1) option2 = poll.options.get(pk=2) option3 = poll.options.get(pk=3) @@ -995,6 +997,43 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass): self.assertEqual(option3.no, Decimal("0")) 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): self.start_poll() response = self.client.post( @@ -2233,7 +2272,7 @@ class PseudoanonymizeAssignmentPoll(TestCase): self.assertHttpStatusVerbose(response, status.HTTP_200_OK) poll = AssignmentPoll.objects.get() 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.votesinvalid, Decimal("0")) self.assertEqual(poll.votescast, Decimal("2")) diff --git a/tests/integration/motions/test_polls.py b/tests/integration/motions/test_polls.py index 245b33db0..3c2b46a91 100644 --- a/tests/integration/motions/test_polls.py +++ b/tests/integration/motions/test_polls.py @@ -763,6 +763,36 @@ class VoteMotionPollNamed(TestCase): self.assertEqual(option.abstain, Decimal("0")) vote = option.votes.get() 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): self.start_poll() @@ -1155,7 +1185,7 @@ class VoteMotionPollPseudoanonymous(TestCase): self.assertEqual(poll.votesinvalid, Decimal("0")) self.assertEqual(poll.votescast, Decimal("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() self.assertEqual(option.yes, Decimal("0")) self.assertEqual(option.no, Decimal("1")) @@ -1378,7 +1408,7 @@ class PseudoanonymizeMotionPoll(TestCase): self.assertHttpStatusVerbose(response, status.HTTP_200_OK) poll = MotionPoll.objects.get() 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.votesinvalid, Decimal("0")) self.assertEqual(poll.votescast, Decimal("2")) @@ -1446,7 +1476,7 @@ class ResetMotionPoll(TestCase): self.assertHttpStatusVerbose(response, status.HTTP_200_OK) poll = MotionPoll.objects.get() 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.votesinvalid, None) self.assertEqual(poll.votescast, None)