diff --git a/client/src/app/site/assignments/models/view-assignment-poll.ts b/client/src/app/site/assignments/models/view-assignment-poll.ts index 002dd9bab..3d739e694 100644 --- a/client/src/app/site/assignments/models/view-assignment-poll.ts +++ b/client/src/app/site/assignments/models/view-assignment-poll.ts @@ -33,14 +33,13 @@ export class ViewAssignmentPoll extends ViewBasePoll implements // TODO: update to new voting system? return { getBasicProjectorElement: options => ({ - name: 'assignments/assignment-poll', - assignment_id: this.assignment_id, - poll_id: this.id, - getIdentifiers: () => ['name', 'assignment_id', 'poll_id'] + name: AssignmentPoll.COLLECTIONSTRING, + id: this.id, + getIdentifiers: () => ['name', 'id'] }), slideOptions: [], - projectionDefaultName: 'assignment-poll', - getDialogTitle: () => 'TODO' + projectionDefaultName: 'assignment_poll', + getDialogTitle: this.getTitle }; } diff --git a/client/src/app/site/motions/models/view-motion-poll.ts b/client/src/app/site/motions/models/view-motion-poll.ts index 97bfd5a5a..d80818cf2 100644 --- a/client/src/app/site/motions/models/view-motion-poll.ts +++ b/client/src/app/site/motions/models/view-motion-poll.ts @@ -62,7 +62,7 @@ export class ViewMotionPoll extends ViewBasePoll implements MotionPo getIdentifiers: () => ['name', 'id'] }), slideOptions: [], - projectionDefaultName: 'motion-poll', + projectionDefaultName: 'motion_poll', getDialogTitle: this.getTitle }; } 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 fb4b65c77..087663db5 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 @@ -87,6 +87,8 @@
{{ 'Required majority' | translate }}: {{ poll.majorityMethodVerbose | translate }}
{{ '100% base' | translate }}: {{ poll.percentBaseVerbose | translate }}
+ + diff --git a/client/src/app/slides/all-slide-configurations.ts b/client/src/app/slides/all-slide-configurations.ts index a81627a6d..34ebe55df 100644 --- a/client/src/app/slides/all-slide-configurations.ts +++ b/client/src/app/slides/all-slide-configurations.ts @@ -25,6 +25,11 @@ export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[ scaleable: true, scrollable: true }, + { + slide: 'motions/motion-poll', + scaleable: true, + scrollable: true + }, { slide: 'users/user', scaleable: true, @@ -83,7 +88,7 @@ export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[ scrollable: true }, { - slide: 'assignments/poll', + slide: 'assignments/assignment-poll', scaleable: true, scrollable: true }, diff --git a/client/src/app/slides/all-slides.ts b/client/src/app/slides/all-slides.ts index 409f9f231..c2c22c5aa 100644 --- a/client/src/app/slides/all-slides.ts +++ b/client/src/app/slides/all-slides.ts @@ -41,6 +41,14 @@ export const allSlides: SlideManifest[] = [ elementIdentifiers: ['name', 'id'], canBeMappedToModel: true }, + { + slide: 'motions/motion-poll', + path: 'motions/motion-poll', + loadChildren: './slides/motions/motion-poll/motion-poll-slide.module#MotionPollSlideModule', + verboseName: 'Motion Poll', + elementIdentifiers: ['name', 'id'], + canBeMappedToModel: true + }, { slide: 'users/user', path: 'users/user', @@ -126,12 +134,12 @@ export const allSlides: SlideManifest[] = [ canBeMappedToModel: true }, { - slide: 'assignments/poll', - path: 'assignments/poll', + slide: 'assignments/assignment-poll', + path: 'assignments/assignment-poll', loadChildren: () => import('./assignments/poll/poll-slide.module').then(m => m.PollSlideModule), - verboseName: 'Poll', - elementIdentifiers: ['name', 'assignment_id', 'poll_id'], - canBeMappedToModel: false + verboseName: 'Assignment Poll', + elementIdentifiers: ['name', 'id'], + canBeMappedToModel: true }, { slide: 'mediafiles/mediafile', diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts new file mode 100644 index 000000000..f26720fb3 --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts @@ -0,0 +1,31 @@ +import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll'; +import { AssignmentTitleInformation } from 'app/site/assignments/models/view-assignment'; + +export interface AssignmentPollSlideData { + assignment: AssignmentTitleInformation; + poll: { + title: string; + type: PollType; + pollmethod: AssignmentPollMethods; + votes_amount: number; + description: string; + state: PollState; + onehundered_percent_base: PercentBase; + majority_method: MajorityMethod; + + options: { + user: string; + yes?: string; + no?: string; + abstain?: string; + }[]; + + // optional for published polls: + amount_global_no?: string; + amount_global_abstain: string; + votesvalid: string; + votesinvalid: string; + votescast: string; + }; +} diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.html b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.html new file mode 100644 index 000000000..09eeb2634 --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.html @@ -0,0 +1,5 @@ +
+ +
{{ verboseData }}
+ +
diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.scss b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.spec.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.spec.ts new file mode 100644 index 000000000..2c8b67d07 --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AssignmentPollSlideComponent } from './assignment-poll-slide.component'; +import { E2EImportsModule } from '../../../../e2e-imports.module'; + +describe('AssignmentPollSlideComponent', () => { + let component: AssignmentPollSlideComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + declarations: [AssignmentPollSlideComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AssignmentPollSlideComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.ts new file mode 100644 index 000000000..45a3f915a --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; + +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { AssignmentPollSlideData } from './assignment-poll-slide-data'; + +@Component({ + selector: 'os-assignment-poll-slide', + templateUrl: './assignment-poll-slide.component.html', + styleUrls: ['./assignment-poll-slide.component.scss'] +}) +export class AssignmentPollSlideComponent extends BaseSlideComponent { + public get verboseData(): string { + return JSON.stringify(this.data, null, 2); + } +} diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.spec.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.spec.ts new file mode 100644 index 000000000..a6c06a558 --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { AssignmentPollSlideModule } from './assignment-poll-slide.module'; + +describe('AssignmentPollSlideModule', () => { + let assignmentPollSlideModule: AssignmentPollSlideModule; + + beforeEach(() => { + assignmentPollSlideModule = new AssignmentPollSlideModule(); + }); + + it('should create an instance', () => { + expect(assignmentPollSlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.ts new file mode 100644 index 000000000..a31657771 --- /dev/null +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { AssignmentPollSlideComponent } from './assignment-poll-slide.component'; + +@NgModule(makeSlideModule(AssignmentPollSlideComponent)) +export class AssignmentPollSlideModule {} diff --git a/client/src/app/slides/assignments/poll/poll-slide-data.ts b/client/src/app/slides/assignments/poll/poll-slide-data.ts deleted file mode 100644 index 37f201dae..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide-data.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { PollVoteValue } from 'app/site/polls/services/poll.service'; - -export interface PollSlideOption { - user: string; - is_elected: boolean; - votes: { - weight: string; - value: PollVoteValue; - }[]; -} - -export interface PollSlideData { - title: string; - assignments_poll_100_percent_base: any /*AssignmentPercentBase*/; - poll: { - published: boolean; - description?: string; - has_votes?: boolean; - pollmethod?: any /*AssignmentPollmethods*/; - votesno?: string; - votesabstain?: string; - votesvalid?: string; - votesinvalid?: string; - votescast?: string; - options?: PollSlideOption[]; - }; -} diff --git a/client/src/app/slides/assignments/poll/poll-slide.component.html b/client/src/app/slides/assignments/poll/poll-slide.component.html deleted file mode 100644 index 17c44405b..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide.component.html +++ /dev/null @@ -1,43 +0,0 @@ -
-
-

{{ data.data.title }}

-

Election result

-
- -
-
Waiting for results...
-
-
-
-
-

Candidates

-
-
-

Votes

-
-
-
-
-
- {{ option.user }} - star -
-
-
- {{ getLabel(vote.value) }}: - {{ getVotePercent(vote.value, option) }} -
-
-
-
-
-
- {{ getLabel(value) }} -
-
- {{ getPollPercent(value) }} -
-
-
-
-
diff --git a/client/src/app/slides/assignments/poll/poll-slide.component.scss b/client/src/app/slides/assignments/poll/poll-slide.component.scss deleted file mode 100644 index c22687f65..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide.component.scss +++ /dev/null @@ -1,31 +0,0 @@ -.row { - border-top: 1px solid #ddd; - display: table; - width: 100%; - - .heading { - text-transform: uppercase; - - h3 { - font-weight: normal; - } - } - - .option-name { - display: table-cell; - padding: 5px; - vertical-align: middle; - width: 70%; - } - - .option-percents { - display: table-cell; - padding: 5px; - width: 30%; - } - - .grey { - background-color: #ddd !important; - color: rgba(0, 0, 0, 0.87) !important; - } -} diff --git a/client/src/app/slides/assignments/poll/poll-slide.component.ts b/client/src/app/slides/assignments/poll/poll-slide.component.ts deleted file mode 100644 index 2556ef0c8..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Component, Input } from '@angular/core'; - -import { SlideData } from 'app/core/core-services/projector-data.service'; -import { CalculablePollKey, PollVoteValue } from 'app/site/polls/services/poll.service'; -import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { PollSlideData, PollSlideOption } from './poll-slide-data'; - -@Component({ - selector: 'os-poll-slide', - templateUrl: './poll-slide.component.html', - styleUrls: ['./poll-slide.component.scss'] -}) -export class PollSlideComponent extends BaseSlideComponent { - private _data: SlideData; - - public get pollValues(): any { - // SummaryPollKey[] { - if (!this.data) { - return []; - } - const values: any /*SummaryPollKey[]*/ = ['votesno', 'votesabstain', 'votesvalid', 'votesinvalid', 'votescast']; - return values.filter(val => this.data.data.poll[val] !== null); - } - - @Input() - public set data(data: SlideData) { - this._data = data; - /*this.calculationData = { - pollMethod: data.data.poll.pollmethod, - votesno: parseFloat(data.data.poll.votesno), - votesabstain: parseFloat(data.data.poll.votesabstain), - votescast: parseFloat(data.data.poll.votescast), - votesvalid: parseFloat(data.data.poll.votesvalid), - votesinvalid: parseFloat(data.data.poll.votesinvalid), - pollOptions: data.data.poll.options.map(opt => { - return { - votes: opt.votes.map(vote => { - return { - weight: parseFloat(vote.weight), - value: vote.value - }; - }) - }; - }), - percentBase: data.data.assignments_poll_100_percent_base - };*/ - } - - public get data(): SlideData { - return this._data; - } - - /** - * get a vote's numerical or special label, including percent values if these are to - * be displayed - * - * @param key - * @param option - */ - public getVotePercent(key: PollVoteValue, option: PollSlideOption): string { - /*const calcOption = { - votes: option.votes.map(vote => { - return { weight: parseFloat(vote.weight), value: vote.value }; - }) - }; - const percent = this.pollService.getPercent(this.calculationData, calcOption, key); - const number = this.translate.instant( - this.pollService.getSpecialLabel(parseFloat(option.votes.find(v => v.value === key).weight)) - ); - return percent === null ? number : `${number} (${percent}%)`;*/ - throw new Error('TODO'); - } - - public getPollPercent(key: CalculablePollKey): string { - /*const percent = this.pollService.getValuePercent(this.calculationData, key); - const number = this.translate.instant(this.pollService.getSpecialLabel(this.calculationData[key])); - return percent === null ? number : `${number} (${percent}%)`;*/ - throw new Error('TODO'); - } - - /** - * @returns a translated label for a key - */ - public getLabel(key: CalculablePollKey): string { - // return this.translate.instant(this.pollService.getLabel(key)); - throw new Error('TODO'); - } -} diff --git a/client/src/app/slides/assignments/poll/poll-slide.module.spec.ts b/client/src/app/slides/assignments/poll/poll-slide.module.spec.ts deleted file mode 100644 index da322455d..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide.module.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { PollSlideModule } from './poll-slide.module'; - -describe('PollSlideModule', () => { - let pollSlideModule: PollSlideModule; - - beforeEach(() => { - pollSlideModule = new PollSlideModule(); - }); - - it('should create an instance', () => { - expect(pollSlideModule).toBeTruthy(); - }); -}); diff --git a/client/src/app/slides/assignments/poll/poll-slide.module.ts b/client/src/app/slides/assignments/poll/poll-slide.module.ts deleted file mode 100644 index 0d538cde8..000000000 --- a/client/src/app/slides/assignments/poll/poll-slide.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { makeSlideModule } from 'app/slides/base-slide-module'; -import { PollSlideComponent } from './poll-slide.component'; - -@NgModule(makeSlideModule(PollSlideComponent)) -export class PollSlideModule {} diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts new file mode 100644 index 000000000..1ea5937ea --- /dev/null +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts @@ -0,0 +1,26 @@ +import { MotionPollMethods } from 'app/shared/models/motions/motion-poll'; +import { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll'; +import { MotionTitleInformation } from 'app/site/motions/models/view-motion'; + +export interface MotionPollSlideData { + motion: MotionTitleInformation; + poll: { + title: string; + type: PollType; + pollmethod: MotionPollMethods; + state: PollState; + onehundered_percent_base: PercentBase; + majority_method: MajorityMethod; + + options: { + yes?: string; + no?: string; + abstain?: string; + }[]; + + // optional for published polls: + votesvalid: string; + votesinvalid: string; + votescast: string; + }; +} diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.html b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.html new file mode 100644 index 000000000..09eeb2634 --- /dev/null +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.html @@ -0,0 +1,5 @@ +
+ +
{{ verboseData }}
+ +
diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.scss b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/slides/assignments/poll/poll-slide.component.spec.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.spec.ts similarity index 58% rename from client/src/app/slides/assignments/poll/poll-slide.component.spec.ts rename to client/src/app/slides/motions/motion-poll/motion-poll-slide.component.spec.ts index 3d56db30e..f21eea502 100644 --- a/client/src/app/slides/assignments/poll/poll-slide.component.spec.ts +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { E2EImportsModule } from '../../../../e2e-imports.module'; -import { PollSlideComponent } from './poll-slide.component'; +import { MotionPollSlideComponent } from './motion-poll-slide.component'; -describe('PollSlideComponent', () => { - let component: PollSlideComponent; - let fixture: ComponentFixture; +describe('MotionPollSlideComponent', () => { + let component: MotionPollSlideComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [E2EImportsModule], - declarations: [PollSlideComponent] + declarations: [MotionPollSlideComponent] }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(PollSlideComponent); + fixture = TestBed.createComponent(MotionPollSlideComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.ts new file mode 100644 index 000000000..8ed6216dd --- /dev/null +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; + +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { MotionPollSlideData } from './motion-poll-slide-data'; + +@Component({ + selector: 'os-motion-poll-slide', + templateUrl: './motion-poll-slide.component.html', + styleUrls: ['./motion-poll-slide.component.scss'] +}) +export class MotionPollSlideComponent extends BaseSlideComponent { + public get verboseData(): string { + return JSON.stringify(this.data, null, 2); + } +} diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.spec.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.spec.ts new file mode 100644 index 000000000..b030f82b3 --- /dev/null +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { MotionPollSlideModule } from './motion-poll-slide.module'; + +describe('MotionPollSlideModule', () => { + let motionPollSlideModule: MotionPollSlideModule; + + beforeEach(() => { + motionPollSlideModule = new MotionPollSlideModule(); + }); + + it('should create an instance', () => { + expect(motionPollSlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.ts new file mode 100644 index 000000000..cba9881d9 --- /dev/null +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { MotionPollSlideComponent } from './motion-poll-slide.component'; + +@NgModule(makeSlideModule(MotionPollSlideComponent)) +export class MotionPollSlideModule {} diff --git a/openslides/assignments/projector.py b/openslides/assignments/projector.py index 581166f25..5dd9055ea 100644 --- a/openslides/assignments/projector.py +++ b/openslides/assignments/projector.py @@ -1,12 +1,8 @@ from typing import Any, Dict, List from ..users.projector import get_user_name -from ..utils.projector import ( - AllData, - ProjectorElementException, - get_config, - register_projector_slide, -) +from ..utils.projector import AllData, get_model, register_projector_slide +from .models import AssignmentPoll # Important: All functions have to be prune. This means, that thay can only @@ -14,24 +10,13 @@ from ..utils.projector import ( # side effects. -def get_assignment(all_data: AllData, id: Any) -> Dict[str, Any]: - if id is None: - raise ProjectorElementException("id is required for assignment slide") - - try: - assignment = all_data["assignments/assignment"][id] - except KeyError: - raise ProjectorElementException(f"assignment with id {id} does not exist") - return assignment - - async def assignment_slide( all_data: AllData, element: Dict[str, Any], projector_id: int ) -> Dict[str, Any]: """ Assignment slide. """ - assignment = get_assignment(all_data, element.get("id")) + assignment = get_model(all_data, "assignments/assignment", element.get("id")) assignment_related_users: List[Dict[str, Any]] = [ { @@ -52,57 +37,52 @@ async def assignment_slide( } -async def poll_slide( +async def assignment_poll_slide( all_data: AllData, element: Dict[str, Any], projector_id: int ) -> Dict[str, Any]: """ Poll slide. """ - assignment = get_assignment(all_data, element.get("assignment_id")) + poll = get_model(all_data, "assignments/assignment-poll", element.get("id")) + assignment = get_model(all_data, "assignments/assignment", poll["assignment_id"]) - # get poll - poll_id = element.get("poll_id") - if poll_id is None: - raise ProjectorElementException("id is required for poll slide") + poll_data = { + key: poll[key] + for key in ( + "title", + "type", + "pollmethod", + "votes_amount", + "description", + "state", + "onehundred_percent_base", + "majority_method", + ) + } - poll = None - for p in assignment["polls"]: - if p["id"] == poll_id: - poll = p - break - if poll is None: - raise ProjectorElementException(f"poll with id {poll_id} does not exist") + # Add options: + poll_data["options"] = [] + for option in sorted(poll["options"], key=lambda option: option["weight"]): + option_data = {"user": await get_user_name(all_data, option["user_id"])} + if poll["state"] == AssignmentPoll.STATE_PUBLISHED: + option_data["yes"] = option["yes"] + option_data["no"] = option["no"] + option_data["abstain"] = option["abstain"] + poll_data["options"].append(option_data) - poll_data = {"published": poll["published"]} - - if poll["published"]: - poll_data["description"] = poll["description"] - poll_data["has_votes"] = poll["has_votes"] - poll_data["pollmethod"] = poll["pollmethod"] - poll_data["votesno"] = poll["votesno"] - poll_data["votesabstain"] = poll["votesabstain"] + if poll["state"] == AssignmentPoll.STATE_PUBLISHED: + poll_data["amount_global_no"] = poll["amount_global_no"] + poll_data["amount_global_abstain"] = poll["amount_global_abstain"] poll_data["votesvalid"] = poll["votesvalid"] poll_data["votesinvalid"] = poll["votesinvalid"] poll_data["votescast"] = poll["votescast"] - poll_data["options"] = [ - { - "user": await get_user_name(all_data, option["candidate_id"]), - "is_elected": option["is_elected"], - "votes": option["votes"], - } - for option in sorted(poll["options"], key=lambda option: option["weight"]) - ] - return { - "title": assignment["title"], - "assignments_poll_100_percent_base": await get_config( - all_data, "assignments_poll_100_percent_base" - ), + "assignment": {"title": assignment["title"]}, "poll": poll_data, } def register_projector_slides() -> None: register_projector_slide("assignments/assignment", assignment_slide) - register_projector_slide("assignments/poll", poll_slide) + register_projector_slide("assignments/assignment-poll", assignment_poll_slide) diff --git a/openslides/core/migrations/0030_voting_projection_defaults.py b/openslides/core/migrations/0030_voting_projection_defaults.py new file mode 100644 index 000000000..1b779d837 --- /dev/null +++ b/openslides/core/migrations/0030_voting_projection_defaults.py @@ -0,0 +1,40 @@ +# Generated by Fin Stutzenstein on 2019-20-11 16:30 +from django.db import migrations + + +def add_poll_projection_defaults(apps, schema_editor): + """ + Adds projectiondefaults for messages and countdowns. + """ + Projector = apps.get_model("core", "Projector") + ProjectionDefault = apps.get_model("core", "ProjectionDefault") + default_projector = Projector.objects.order_by("pk").first() + + projectiondefaults = [] + + projectiondefaults.append( + ProjectionDefault( + name="assignment_poll", + display_name="Assignment poll", + projector=default_projector, + ) + ) + projectiondefaults.append( + ProjectionDefault( + name="motion_poll", display_name="Motion Poll", projector=default_projector + ) + ) + + # Create all new projectiondefaults + ProjectionDefault.objects.bulk_create(projectiondefaults) + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0029_remove_history_restricted"), + ] + + operations = [ + migrations.RunPython(add_poll_projection_defaults), + ] diff --git a/openslides/motions/projector.py b/openslides/motions/projector.py index 5c33eb077..3b29e903f 100644 --- a/openslides/motions/projector.py +++ b/openslides/motions/projector.py @@ -6,8 +6,10 @@ from ..utils.projector import ( AllData, ProjectorElementException, get_config, + get_model, register_projector_slide, ) +from .models import MotionPoll motion_placeholder_regex = re.compile(r"\[motion:(\d+)\]") @@ -91,11 +93,7 @@ async def get_amendments_for_motion(motion, all_data): async def get_amendment_base_motion(amendment, all_data): - try: - motion = all_data["motions/motion"][amendment["parent_id"]] - except KeyError: - motion_id = amendment["parent_id"] - raise ProjectorElementException(f"motion with id {motion_id} does not exist") + motion = get_model(all_data, "motions/motion", amendment.get("parent_id")) return { "identifier": motion["identifier"], @@ -105,14 +103,9 @@ async def get_amendment_base_motion(amendment, all_data): async def get_amendment_base_statute(amendment, all_data): - try: - statute = all_data["motions/statute-paragraph"][ - amendment["statute_paragraph_id"] - ] - except KeyError: - statute_id = amendment["statute_paragraph_id"] - raise ProjectorElementException(f"statute with id {statute_id} does not exist") - + statute = get_model( + all_data, "motions/statute-paragraph", amendment.get("statute_paragraph_id") + ) return {"title": statute["title"], "text": statute["text"]} @@ -167,15 +160,7 @@ async def motion_slide( mode = element.get( "mode", await get_config(all_data, "motions_recommendation_text_mode") ) - motion_id = element.get("id") - - if motion_id is None: - raise ProjectorElementException("id is required for motion slide") - - try: - motion = all_data["motions/motion"][motion_id] - except KeyError: - raise ProjectorElementException(f"motion with id {motion_id} does not exist") + motion = get_model(all_data, "motions/motion", element.get("id")) # Add submitters submitters = [ @@ -270,7 +255,7 @@ async def motion_slide( # Add recommendation-referencing motions return_value[ "recommendation_referencing_motions" - ] = await get_recommendation_referencing_motions(all_data, motion_id) + ] = await get_recommendation_referencing_motions(all_data, motion["id"]) return return_value @@ -317,17 +302,7 @@ async def motion_block_slide( """ Motion block slide. """ - motion_block_id = element.get("id") - - if motion_block_id is None: - raise ProjectorElementException("id is required for motion block slide") - - try: - motion_block = all_data["motions/motion-block"][motion_block_id] - except KeyError: - raise ProjectorElementException( - f"motion block with id {motion_block_id} does not exist" - ) + motion_block = get_model(all_data, "motions/motion-block", element.get("id")) # All motions in this motion block motions = [] @@ -337,7 +312,7 @@ async def motion_block_slide( # Search motions. for motion in all_data["motions/motion"].values(): - if motion["motion_block_id"] == motion_block_id: + if motion["motion_block_id"] == motion_block["id"]: motion_object = { "title": motion["title"], "identifier": motion["identifier"], @@ -366,6 +341,40 @@ async def motion_block_slide( } +async def motion_poll_slide( + all_data: AllData, element: Dict[str, Any], projector_id: int +) -> Dict[str, Any]: + """ + Poll slide. + """ + poll = get_model(all_data, "motions/motion-poll", element.get("id")) + motion = get_model(all_data, "motions/motion", poll["motion_id"]) + + poll_data = { + key: poll[key] + for key in ( + "title", + "type", + "pollmethod", + "state", + "onehundred_percent_base", + "majority_method", + ) + } + + if poll["state"] == MotionPoll.STATE_PUBLISHED: + poll_data["options"] = poll["options"] + poll_data["votesvalid"] = poll["votesvalid"] + poll_data["votesinvalid"] = poll["votesinvalid"] + poll_data["votescast"] = poll["votescast"] + + return { + "motion": {"title": motion["title"], "identifier": motion["identifier"]}, + "poll": poll_data, + } + + def register_projector_slides() -> None: register_projector_slide("motions/motion", motion_slide) register_projector_slide("motions/motion-block", motion_block_slide) + register_projector_slide("motions/motion-poll", motion_poll_slide) diff --git a/openslides/utils/projector.py b/openslides/utils/projector.py index 98ba26702..7fb6da472 100644 --- a/openslides/utils/projector.py +++ b/openslides/utils/projector.py @@ -100,3 +100,18 @@ async def get_config(all_data: AllData, key: str) -> Any: config_id = (await config.async_get_key_to_id())[key] return all_data[config.get_collection_string()][config_id]["value"] + + +def get_model(all_data: AllData, collection: str, id: Any) -> Dict[str, Any]: + """ + Tries to get the model identified by the collection and id. + If the id is invalid or the model not found, ProjectorElementExceptions will be raised. + """ + if id is None: + raise ProjectorElementException(f"id is required for {collection} slide") + + try: + model = all_data[collection][id] + except KeyError: + raise ProjectorElementException(f"{collection} with id {id} does not exist") + return model