Projector for polls: Server, client structure and data modeling
This commit is contained in:
parent
84a39ccb62
commit
e2585fb757
|
@ -33,14 +33,13 @@ export class ViewAssignmentPoll extends ViewBasePoll<AssignmentPoll> 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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ export class ViewMotionPoll extends ViewBasePoll<MotionPoll> implements MotionPo
|
|||
getIdentifiers: () => ['name', 'id']
|
||||
}),
|
||||
slideOptions: [],
|
||||
projectionDefaultName: 'motion-poll',
|
||||
projectionDefaultName: 'motion_poll',
|
||||
getDialogTitle: this.getTitle
|
||||
};
|
||||
}
|
||||
|
|
|
@ -87,6 +87,8 @@
|
|||
<div>{{ 'Required majority' | translate }}: {{ poll.majorityMethodVerbose | translate }}</div>
|
||||
<div>{{ '100% base' | translate }}: {{ poll.percentBaseVerbose | translate }}</div>
|
||||
</div>
|
||||
|
||||
<os-projector-button [object]="poll"></os-projector-button>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<div *ngIf="data">
|
||||
|
||||
<pre>{{ verboseData }}</pre>
|
||||
|
||||
</div>
|
|
@ -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<AssignmentPollSlideComponent>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -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<AssignmentPollSlideData> {
|
||||
public get verboseData(): string {
|
||||
return JSON.stringify(this.data, null, 2);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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 {}
|
|
@ -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[];
|
||||
};
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<div *ngIf="data">
|
||||
<div class="slidetitle">
|
||||
<h1>{{ data.data.title }}</h1>
|
||||
<h2 translate>Election result</h2>
|
||||
</div>
|
||||
|
||||
<div class="spacer-top-10"></div>
|
||||
<div *ngIf="!data.data.poll.published"><span translate>Waiting for results</span><span>...</span></div>
|
||||
<div *ngIf="data.data.poll.published">
|
||||
<div *ngIf="data.data.poll.has_votes" class="result-table">
|
||||
<div class="row">
|
||||
<div class="option-name heading">
|
||||
<h3 translate>Candidates</h3>
|
||||
</div>
|
||||
<div class="option-percents heading">
|
||||
<h3 translate>Votes</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let option of data.data.poll.options">
|
||||
<div class="row">
|
||||
<div class="option-name">
|
||||
<span class="bold">{{ option.user }}</span>
|
||||
<mat-icon *ngIf="option.is_elected">star</mat-icon>
|
||||
</div>
|
||||
<div class="option-percents">
|
||||
<div *ngFor="let vote of option.votes" class="bold">
|
||||
<span *ngIf="vote.value !== 'Votes'">{{ getLabel(vote.value) }}:</span>
|
||||
<span> {{ getVotePercent(vote.value, option) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let value of pollValues" class="row">
|
||||
<div class="option-name grey">
|
||||
<span>{{ getLabel(value) }}</span>
|
||||
</div>
|
||||
<div class="option-percents grey">
|
||||
{{ getPollPercent(value) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<PollSlideData> {
|
||||
private _data: SlideData<PollSlideData>;
|
||||
|
||||
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<PollSlideData>) {
|
||||
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<PollSlideData> {
|
||||
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');
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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 {}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<div *ngIf="data">
|
||||
|
||||
<pre>{{ verboseData }}</pre>
|
||||
|
||||
</div>
|
|
@ -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<PollSlideComponent>;
|
||||
describe('MotionPollSlideComponent', () => {
|
||||
let component: MotionPollSlideComponent;
|
||||
let fixture: ComponentFixture<MotionPollSlideComponent>;
|
||||
|
||||
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();
|
||||
});
|
|
@ -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<MotionPollSlideData> {
|
||||
public get verboseData(): string {
|
||||
return JSON.stringify(this.data, null, 2);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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 {}
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue