Voting - OpenSlides/OpenSlides GitHub Wiki

The general voting structure consists of three models: poll, option and vote. A poll can have many options (relation poll/option_ids) as well as an option can have many votes (relation option/vote_ids). It is voted about the options of a poll and every vote (the representation strongly depends on the type, see below) is saved into a vote object related to the option.

A poll can have a content_object_id referring to the object that is voted about. This may be an assignment or a motion, but the relation can also be empty. This would be a standalone poll.

A poll has a type, which can be analog, named or pseudoanonymous. The two latter types are forms of electronic voting, which are only allowed types if organization/enable_electronic_voting is true. If the analog type is chosen, poll results can directly be entered and updated after creation. This is only for editorial purposes and OpenSlides does not support some kind of single-votes entering. Only a ballot paper can be generated in the client. The electronic voting types are fully integrated in OpenSlides. Votes must be given by actual users, furthermore modifying them afterwards is not possible. There is the possibility of getting a record of each vote after the voting next to the accumulated result.

The pollmethod determines the way users can vote about options. Currently, there are four methods:

  • Y: It is allowed to give Yes-votes to options
  • N: It is allowed to give No-votes to options (this is a negative-voting)
  • YN: Yes- and No-votes can be given per option
  • YNA: Yes- No- and Abstain-votes can be given per option

Creating a poll

When creating a poll, the type and pollmethod are required and at least one option has to be provided. Next to creating the poll, each option is created. The option/weight is set accordingly to the order given in the payload.

An option must have one of text or content_object_id given. If text is given, the user can give own pieces of text to vote about (Some kind of free-hand quick polls), or it is voted about objects in OpenSlides such as users. After creating a poll, options cannot be added, removed or updated (Note: updating is only allowed for analog polls, see the analog poll section). The options must be unique in the way that each non-empty text is unique and each non-empty content_object_id is unique.

The global option is also created: This is an additional option object, which is linked to the poll with poll/global_option_id (it is not linked in poll/option_ids). This option neither has a text nor a content object. The weight can be set to 1.

The polls state is set to created.

States

There are four states:

  1. created: The initial state when a poll is created.
  2. started: The voting is open, so every entitled user can cast a ballot.
  3. finished: The voting is closed. The results are hidden and only managers can see them.
  4. published: The poll result is made public.

Not every action is possible in every state. The natural order is progressing from one state to the next one with the action poll.start (created to started), poll.stop (started to finished), poll.publish (finished to published) and poll.reset (published to created). These actions do nothing more than changing the state. They raise errors if the origin-state does not match (e.g. it is not allowed to publish a started poll). When a poll is reset, all votes are deleted.

Updating a poll

Updating a poll is always possible in the Created state. This includes all fields that can be set during creation, but one cannot alter options and the content object. These fields can be altered in every state:

  • title
  • description
  • onehundred_percent_base

For analog polls, six fields votes* and amount_global_* can also be given (see the section about analog polls).

Global votes

It is possible to give global votes, so a user must not vote for one or more options. Enabling each global vote is done with global_yes, global_no and global_abstain. If such a global vote is given, the value is saved to the global option (poll/global_option_id).

Electronic voting

The main flow of states and the way the models are organized is mainly focused on electronic votes. This type of votes fits well in the modellation, but analog votes (see below) do have many specialities.

Some fields can be given on creation, which have effects on electronic voting (they are ignored in the analog case):

  • entitled_group_ids: Determines which users are allowed to vote. If not provided, the relation is empty.
  • min_votes_amount: The minimum amount of votes a user has to provide. This value must be >= 1. The default is 1.
  • max_votes_amount: The maximum amount of votes a user has to provide. This value must be >= min_votes_amount. The default is 1.

The meeting model contains fields for defaults:

  • poll_default_type
  • poll_default_method
  • poll_default_100_percent_base
  • poll_default_group_ids The backend does not respect these defaults, they are only for the client. Also, if the content object if from motion or assignment, there are special defaults for them. Every setting for poll_ is also available with the prefixes motion_poll_ and assignment_poll_. These settings are also only relevant for the client.

Cast a ballot

Entitled users (see below for an explainations about users) can only vote if the poll's state is started. They can vote once and the vote cannot be changed. If it was successful, the request user is saved into poll/voted_ids, so it can be checked if a user has already voted.

A vote object can hold weight and value. weight saves the "amount of votes" from the vote/user_id (potentially empty for pseudoanonymous polls) for the one value. It can be Y, N, A. E.g. for the Y pollmethod, while processing one vote from one user, one vote object with value Y for each option with weight > 0 is created. No vote objects with values N or A are created (except for the global option). For the pollmethod YNA up to three vote objects, one for each value, can be created per option and per user.

The payload for poll.vote describes the possibilities on how to vote:

{
    id: Id;
    user_id: Id;

// pollmethod in (Y, N)
    value: {<option_id>: <amount>} | 'Y' | 'N' | 'A';

// pollmethod not in (Y, N)
    value: {<option_id>: 'Y' | 'N' | 'A'} | 'Y' | 'N' | 'A';
}

In general, exactly one of the four possibilities for value must be given and the last three ones are for global votes. If one is given and the corresponding global_* is false, this will raise an error. If an option is omitted, no votes are created for the option. At least one option with one vote has to be present. This is enforced through min_votes_amount since it must always be >= 1.

For the pollmethod Y and N the amount of votes is provided by the client and must be an integer >= 0. The sum of all votes must be >= min_votes_amount and <= max_votes_amount. On each option there the number can not be bigger then poll/max_votes_per_option.

For other poll methods, the value per candidate is provided. A is only valid for the pollmethod YNA. Currently, there is no possibility to provide multiple votes per candidate for the pollmethods YN and YNA. There is no check for min/max number of votes.

If the vote is valid, vote objects are created, one for each given option with the value (Y, N, A) and weight (the amount of votes). If a global vote was cast, instead a vote object is created for the global option of the poll.

User in vote objects

The key user_id in the vote payload allows the request user to vote for other users. This is the feature vote delegation. If the user id is omitted, the request user votes for himself. So there are two users, which may be equal:

  1. The request user, which votes about the vote user
  2. The user given by user_id (or the request user itself) is the vote user. This is the user for which the votes are created.

If the request user and vote user are different users, it must be checked if the request user actually can vote for the vote user. This is the case if the request user is the user linked in the vote users meeting_user/vote_delegated_to_id for the current implicit meeting. In contrast, if both users are equal (voting for ones own), the user must not have his vote delegated, so meeting_user/vote_delegated_to_id must be empty.

A request user can vote if he is present (relation user/is_present_in_meeting_ids with the implicit meeting) and the vote user is a member of one of the entitled groups (poll/entitled_group_ids). Entitled users are all users who can cast a ballot. So to be an entitled user one must be present and either in an entitled group or a user of meeting_user/vote_delegations_from_ids is in an entitled group. Additionally, a user cannot vote twice or change his votes, so the vote user must not be in poll/voted_ids.

The vote user is the one saved into the poll/voted_ids relation.

For named polls, both users are saved into the vote objects. The relation vote/user_id is used for the vote user and the relation vote/delegated_user_id for the request user.

For pseudoanonymous polls, both references are not saved into the created votes objects.

You can pseudoanonymize named polls (poll.pseudoanonymize), which removes both references in every vote object related to the poll.

Each vote object is assigned a user_token upon creation. This should be the same for all vote objects with the saem vote user and the same underlying poll and different in all other cases. This serves the purpose of displaying the whole ballot for pseudoanonymous polls, where multiple votes for different options by the same user could not be associated otherwise.

Vote weight

If meeting/users_enable_vote_weight is true, the vote weight system is active. Each user has a field meeting_user/vote_weight with their personal weights in each meeting. If a weight is not given for the current meeting, the field default_vote_weight should be used. If this is empty, too, 1 is implied. When creating vote objects, the actual weight of the vote object is multiplied with the vote_weight of the user.

Poll results

The results are summarized per option and globally in the poll. The fields yes, no and abstain in each options are the sum of every vote of the option with the right value (which can be Y, N, A).

The amounts of global votes can be obtained by yes, no and abstain of the global option.

Additionally there are three other fields:

  • votescast: This is the amount of users voted, so it is the length of poll/voted_ids (assuming no users were deleted).
  • votesvalid: If vote weight is disabled it is the same as votescast. Else, vote weight is considered when counting all users. It is the sum of all vote weights of all voted users.
  • votesinvalid: Always zero. There are no invalid votes when using electronic voting.

These fields are set at the stopping of the poll (see poll.stop). votescast is the exception, since it is needed for the progress bar during voting, so while the poll is running it reflects the current length of voted_ids.

Analog polls

This type is a bit special since there is no actual voting in OpenSlides. Poll results can be directly given when creating a poll. To do so, the six fields votes* and amount_global_* can directly be given. votes* fields are saved into the DB and global vote amounts are saved within the global option. Each global option is only accepted if the corresponding global* is true. Also yes, no and abstain can be given for each option. The values are directly saved as vote ojects, so for each option vote objects may be created.

Next to integers (>= 0) special values can be given:

  • -1: Majority
  • -2: Undocumented

Undocumented is the default value for all fields in an analog poll. So, every field, that is not set in the payload must be set to -2 in the action service.

The state is directly set to finished. If the flag publish_immediately is set to true in the payload, the state is set to published.

One can update the results in the poll with poll.update and the yes, no and abstain for each option with option.update. This changes the weight of the votes using the existing objects and does not create new ones with equal value.

100%-Base

The 100%-Base specifies what should be the base number for votes to calculate percentages on. The calculation of percentages is done by the client.

The 100%-Base can be Y, N, YN, YNA, valid, cast, entitled or disabled. There are some restrictions:

  • If the pollmethod is Y, the bases N, YN and YNA are forbidden.
  • If the pollmethod is N, the bases Y, YN and YNA are forbidden.
  • If the pollmethod is YN, the base YNA is forbidden.
  • For analog polls, entitled is forbidden since there is no list of entitled users.

The bases valid, cast and disabled can always be set.

⚠️ **GitHub.com Fallback** ⚠️