Merge pull request #4032 from FinnStutzenstein/new_configs

new configs for statute amendments; improved the majorityMethod confi…
This commit is contained in:
Sean 2018-11-23 11:15:07 +01:00 committed by GitHub
commit 10714bbbe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 156 additions and 59 deletions

View File

@ -67,10 +67,10 @@ export class ConfigService extends OpenSlidesComponent {
* *
* @param key The config value to get from. * @param key The config value to get from.
*/ */
public get(key: string): Observable<any> { public get<T = any>(key: string): Observable<T> {
if (!this.configSubjects[key]) { if (!this.configSubjects[key]) {
this.configSubjects[key] = new BehaviorSubject<any>(this.instant(key)); this.configSubjects[key] = new BehaviorSubject<any>(this.instant(key));
} }
return this.configSubjects[key].asObservable(); return this.configSubjects[key].asObservable() as Observable<T>;
} }
} }

View File

@ -1,12 +1,14 @@
<mat-form-field [formGroup]="form"> <mat-form-field [formGroup]="form">
<mat-select [formControl]="formControl" [placeholder]="listname" [multiple]="multiple" #thisSelector> <mat-select [formControl]="formControl" placeholder="{{ listname | translate }}" [multiple]="multiple" #thisSelector>
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search> <ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
<div *ngIf="!multiple"> <div *ngIf="!multiple && includeNone">
<mat-option [value]="null"><span translate>None</span></mat-option> <mat-option [value]="null">
<span translate>None</span>
</mat-option>
<mat-divider></mat-divider> <mat-divider></mat-divider>
</div> </div>
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id"> <mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id">
{{selectedItem.getTitle(translate)}} {{ selectedItem.getTitle(translate) }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -15,7 +17,8 @@
<span translate>Selected values</span>: <span translate>Selected values</span>:
</p> </p>
<mat-chip-list #chipList> <mat-chip-list #chipList>
<mat-chip *ngFor="let selectedItem of thisSelector?.value" (removed)="remove(selectedItem)">{{selectedItem.name}} <mat-chip *ngFor="let selectedItem of thisSelector?.value" (removed)="remove(selectedItem)">
{{ selectedItem.name }}
<mat-icon (click)="remove(selectedItem)">cancel</mat-icon> <mat-icon (click)="remove(selectedItem)">cancel</mat-icon>
</mat-chip> </mat-chip>
</mat-chip-list> </mat-chip-list>

View File

@ -22,8 +22,8 @@ import { Selectable } from '../selectable';
* ngDefaultControl * ngDefaultControl
* [multiple]="true" * [multiple]="true"
* placeholder="Placeholder" * placeholder="Placeholder"
* [InputListValues]="myListValues", * [InputListValues]="myListValues"
* [form]="myform_name", * [form]="myform_name"
* [formControl]="myformcontrol"> * [formControl]="myformcontrol">
* </os-search-value-selector> * </os-search-value-selector>
* ``` * ```
@ -68,6 +68,12 @@ export class SearchValueSelectorComponent implements OnInit, OnDestroy {
@Input() @Input()
public multiple: boolean; public multiple: boolean;
/**
* Decide, if none should be included, if multiple is false.
*/
@Input()
public includeNone = false;
/** /**
* The inputlist subject. Subscribes to it and updates the selector, if the subject * The inputlist subject. Subscribes to it and updates the selector, if the subject
* changes its values. * changes its values.

View File

@ -10,6 +10,10 @@ import { DataStoreService } from 'app/core/services/data-store.service';
import { AgendaRepositoryService } from '../../services/agenda-repository.service'; import { AgendaRepositoryService } from '../../services/agenda-repository.service';
import { ViewItem } from '../../models/view-item'; import { ViewItem } from '../../models/view-item';
import { OperatorService } from 'app/core/services/operator.service'; import { OperatorService } from 'app/core/services/operator.service';
import { BaseViewComponent } from 'app/site/base/base-view';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { MatSnackBar } from '@angular/material';
/** /**
* The list of speakers for agenda items. * The list of speakers for agenda items.
@ -19,7 +23,7 @@ import { OperatorService } from 'app/core/services/operator.service';
templateUrl: './speaker-list.component.html', templateUrl: './speaker-list.component.html',
styleUrls: ['./speaker-list.component.scss'] styleUrls: ['./speaker-list.component.scss']
}) })
export class SpeakerListComponent implements OnInit { export class SpeakerListComponent extends BaseViewComponent implements OnInit {
/** /**
* Holds the view item to the given topic * Holds the view item to the given topic
*/ */
@ -52,17 +56,24 @@ export class SpeakerListComponent implements OnInit {
/** /**
* Constructor for speaker list component * Constructor for speaker list component
* @param title
* @param translate
* @param snackBar
* @param route Angulars ActivatedRoute * @param route Angulars ActivatedRoute
* @param DS the DataStore * @param DS the DataStore
* @param itemRepo Repository fpr agenda items * @param itemRepo Repository fpr agenda items
* @param op the current operator * @param op the current operator
*/ */
public constructor( public constructor(
title: Title,
translate: TranslateService,
snackBar: MatSnackBar,
private route: ActivatedRoute, private route: ActivatedRoute,
private DS: DataStoreService, private DS: DataStoreService,
private itemRepo: AgendaRepositoryService, private itemRepo: AgendaRepositoryService,
private op: OperatorService private op: OperatorService
) { ) {
super(title, translate, snackBar)
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) }); this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
this.getAgendaItemByUrl(); this.getAgendaItemByUrl();
} }
@ -115,9 +126,8 @@ export class SpeakerListComponent implements OnInit {
* Create a speaker out of an id * Create a speaker out of an id
* @param userId the user id to add to the list. No parameter adds the operators user as speaker. * @param userId the user id to add to the list. No parameter adds the operators user as speaker.
*/ */
public async addNewSpeaker(userId?: number): Promise<void> { public addNewSpeaker(userId?: number): void {
await this.itemRepo.addSpeaker(userId, this.viewItem.item); this.itemRepo.addSpeaker(userId, this.viewItem.item).then(() => this.addSpeakerForm.reset(), this.raiseError);
this.addSpeakerForm.reset();
} }
/** /**
@ -128,7 +138,7 @@ export class SpeakerListComponent implements OnInit {
public onSortingChange(listInNewOrder: ViewSpeaker[]): void { public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
// extract the ids from the ViewSpeaker array // extract the ids from the ViewSpeaker array
const userIds = listInNewOrder.map(speaker => speaker.id); const userIds = listInNewOrder.map(speaker => speaker.id);
this.itemRepo.sortSpeakers(userIds, this.viewItem.item); this.itemRepo.sortSpeakers(userIds, this.viewItem.item).then(null, this.raiseError);
} }
/** /**
@ -136,14 +146,14 @@ export class SpeakerListComponent implements OnInit {
* @param item the speaker marked in the list * @param item the speaker marked in the list
*/ */
public onStartButton(item: ViewSpeaker): void { public onStartButton(item: ViewSpeaker): void {
this.itemRepo.startSpeaker(item.id, this.viewItem.item); this.itemRepo.startSpeaker(item.id, this.viewItem.item).then(null, this.raiseError);
} }
/** /**
* Click on the mic-cross button * Click on the mic-cross button
*/ */
public onStopButton(): void { public onStopButton(): void {
this.itemRepo.stopSpeaker(this.viewItem.item); this.itemRepo.stopSpeaker(this.viewItem.item).then(null, this.raiseError);
} }
/** /**
@ -151,7 +161,7 @@ export class SpeakerListComponent implements OnInit {
* @param item * @param item
*/ */
public onMarkButton(item: ViewSpeaker): void { public onMarkButton(item: ViewSpeaker): void {
this.itemRepo.markSpeaker(item.user.id, !item.marked, this.viewItem.item); this.itemRepo.markSpeaker(item.user.id, !item.marked, this.viewItem.item).then(null, this.raiseError);
} }
/** /**
@ -159,7 +169,7 @@ export class SpeakerListComponent implements OnInit {
* @param item * @param item
*/ */
public onDeleteButton(item?: ViewSpeaker): void { public onDeleteButton(item?: ViewSpeaker): void {
this.itemRepo.deleteSpeaker(this.viewItem.item, item ? item.id : null); this.itemRepo.deleteSpeaker(this.viewItem.item, item ? item.id : null).then(null, this.raiseError);
} }
/** /**

View File

@ -15,7 +15,6 @@ type ConfigInputType =
| 'boolean' | 'boolean'
| 'markupText' | 'markupText'
| 'integer' | 'integer'
| 'majorityMethod'
| 'choice' | 'choice'
| 'datetimepicker' | 'datetimepicker'
| 'colorpicker' | 'colorpicker'

View File

@ -220,8 +220,7 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
* @param config * @param config
*/ */
public createViewModel(config: Config): ViewConfig { public createViewModel(config: Config): ViewConfig {
const vm = new ViewConfig(config); return new ViewConfig(config);
return vm;
} }
/** /**

View File

@ -79,8 +79,7 @@
<mat-accordion multi='true' class='on-transition-fade'> <mat-accordion multi='true' class='on-transition-fade'>
<!-- MetaInfo Panel--> <!-- MetaInfo Panel-->
<mat-expansion-panel #metaInfoPanel [expanded]="this.editMotion" class='meta-info-block meta-info-panel'> <mat-expansion-panel #metaInfoPanel [expanded]="editMotion" class='meta-info-block meta-info-panel'>
<!-- <mat-expansion-panel #metaInfoPanel [expanded]="this.editReco && this.newReco" class='meta-info-block meta-info-panel'> -->
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
<mat-icon>info</mat-icon> <mat-icon>info</mat-icon>
@ -155,8 +154,8 @@
<div *ngIf="motion && motion.submitters || newMotion"> <div *ngIf="motion && motion.submitters || newMotion">
<div *ngIf="newMotion"> <div *ngIf="newMotion">
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']"> <div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('submitters_id')" <os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('submitters_id')"
[multiple]="true" listname="{{ 'Submitters' | translate }}" [InputListValues]="this.submitterObserver"></os-search-value-selector> [multiple]="true" listname="{{ 'Submitters' | translate }}" [InputListValues]="submitterObserver"></os-search-value-selector>
</div> </div>
</div> </div>
<div *ngIf="!editMotion && !newMotion"> <div *ngIf="!editMotion && !newMotion">
@ -172,8 +171,8 @@
<!-- print all motion supporters --> <!-- print all motion supporters -->
<div *ngIf="editMotion"> <div *ngIf="editMotion">
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']"> <div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('supporters_id')" <os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('supporters_id')"
[multiple]="true" listname="{{ 'Supporters' | translate }}" [InputListValues]="this.supporterObserver"></os-search-value-selector> [multiple]="true" listname="{{ 'Supporters' | translate }}" [InputListValues]="supporterObserver"></os-search-value-selector>
</div> </div>
</div> </div>
<div *ngIf="!editMotion && motion.hasSupporters()"> <div *ngIf="!editMotion && motion.hasSupporters()">
@ -230,8 +229,16 @@
{{ motion.category }} {{ motion.category }}
</div> </div>
<div *ngIf="editMotion || newMotion"> <div *ngIf="editMotion || newMotion">
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('category_id')" <os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('category_id')"
[multiple]="false" listname="{{ 'Category' | translate }}" [InputListValues]="this.categoryObserver"></os-search-value-selector> [multiple]="false" listname="{{ 'Category' | translate }}" [InputListValues]="categoryObserver" includeNone="true"></os-search-value-selector>
</div>
</div>
<!-- Workflow (just during creation) -->
<div *ngIf="editMotion">
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('workflow_id')"
[multiple]="false" listname="{{ 'Workflow' | translate }}" [InputListValues]="workflowObserver"></os-search-value-selector>
</div> </div>
</div> </div>

View File

@ -11,7 +11,7 @@ import { User } from '../../../../shared/models/users/user';
import { DataStoreService } from '../../../../core/services/data-store.service'; import { DataStoreService } from '../../../../core/services/data-store.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Motion } from '../../../../shared/models/motions/motion'; import { Motion } from '../../../../shared/models/motions/motion';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject, Subscription, ReplaySubject, concat } from 'rxjs';
import { LineRange } from '../../services/diff.service'; import { LineRange } from '../../services/diff.service';
import { import {
MotionChangeRecommendationComponent, MotionChangeRecommendationComponent,
@ -26,6 +26,8 @@ import { BaseViewComponent } from '../../../base/base-view';
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph'; import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service'; import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
import { ConfigService } from '../../../../core/services/config.service'; import { ConfigService } from '../../../../core/services/config.service';
import { Workflow } from 'app/shared/models/motions/workflow';
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
/** /**
* Component for the motion detail view * Component for the motion detail view
@ -71,9 +73,25 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
public newMotion = false; public newMotion = false;
/** /**
* Target motion. Might be new or old * Sets the motions, e.g. via an autoupdate. Reload important things here:
* - Reload the recommendation. Not changed with autoupdates, but if the motion is loaded this needs to run.
*/ */
public motion: ViewMotion; public set motion(value: ViewMotion) {
this._motion = value;
this.setupRecommender();
}
/**
* Returns the target motion. Might be the new one or old.
*/
public get motion(): ViewMotion {
return this._motion;
}
/**
* Saves the target motion. Accessed via the getter and setter.
*/
private _motion: ViewMotion;
/** /**
* Value of the configuration variable `motions_statutes_enabled` - are statutes enabled? * Value of the configuration variable `motions_statutes_enabled` - are statutes enabled?
@ -121,6 +139,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
*/ */
public categoryObserver: BehaviorSubject<Category[]>; public categoryObserver: BehaviorSubject<Category[]>;
/**
* Subject for the Categories
*/
public workflowObserver: BehaviorSubject<Workflow[]>;
/** /**
* Subject for the Submitters * Subject for the Submitters
*/ */
@ -141,6 +164,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
*/ */
public recommender: string; public recommender: string;
/**
* The subscription to the recommender config variable.
*/
private recommenderSubscription: Subscription;
/** /**
* Constuct the detail view. * Constuct the detail view.
* *
@ -185,15 +213,17 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
this.submitterObserver = new BehaviorSubject(DS.getAll(User)); this.submitterObserver = new BehaviorSubject(DS.getAll(User));
this.supporterObserver = new BehaviorSubject(DS.getAll(User)); this.supporterObserver = new BehaviorSubject(DS.getAll(User));
this.categoryObserver = new BehaviorSubject(DS.getAll(Category)); this.categoryObserver = new BehaviorSubject(DS.getAll(Category));
this.workflowObserver = new BehaviorSubject(DS.getAll(Workflow));
// Make sure the subjects are updated, when a new Model for the type arrives // Make sure the subjects are updated, when a new Model for the type arrives
this.DS.changeObservable.subscribe(newModel => { this.DS.changeObservable.subscribe(newModel => {
if (newModel instanceof User) { if (newModel instanceof User) {
this.submitterObserver.next(DS.getAll(User)); this.submitterObserver.next(DS.getAll(User));
this.supporterObserver.next(DS.getAll(User)); this.supporterObserver.next(DS.getAll(User));
} } else if (newModel instanceof Category) {
if (newModel instanceof Category) {
this.categoryObserver.next(DS.getAll(Category)); this.categoryObserver.next(DS.getAll(Category));
} else if (newModel instanceof Workflow) {
this.workflowObserver.next(DS.getAll(Workflow));
} }
}); });
this.configService.get('motions_statutes_enabled').subscribe( this.configService.get('motions_statutes_enabled').subscribe(
@ -282,6 +312,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
recommendation_id: [''], recommendation_id: [''],
submitters_id: [], submitters_id: [],
supporters_id: [], supporters_id: [],
workflow_id: [],
origin: [''] origin: ['']
}); });
this.contentForm = this.formBuilder.group({ this.contentForm = this.formBuilder.group({
@ -291,6 +322,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
statute_amendment: [''], // Internal value for the checkbox, not saved to the model statute_amendment: [''], // Internal value for the checkbox, not saved to the model
statute_paragraph_id: [''] statute_paragraph_id: ['']
}); });
this.updateWorkflowIdForCreateForm();
} }
/** /**
@ -474,6 +506,21 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
} }
} }
public updateWorkflowIdForCreateForm(): void {
const isStatuteAmendment = !!this.contentForm.get('statute_amendment').value;
const configKey = isStatuteAmendment ? 'motions_statute_amendments_workflow' : 'motions_workflow';
// TODO: This should just be a takeWhile(id => !id), but should include the last one where the id is OK.
// takeWhile will get a inclusive parameter, see https://github.com/ReactiveX/rxjs/pull/4115
this.configService.get<string>(configKey).pipe(multicast(
() => new ReplaySubject(1),
(ids) => ids.pipe(takeWhile(id => !id), o => concat(o, ids.pipe(take(1))))
), skipWhile(id => !id)).subscribe(id => {
this.metaInfoForm.patchValue({
workflow_id: parseInt(id, 10),
});
});
}
/** /**
* If the checkbox is deactivated, the statute_paragraph_id-field needs to be reset, as only that field is saved * If the checkbox is deactivated, the statute_paragraph_id-field needs to be reset, as only that field is saved
* @param {MatCheckboxChange} $event * @param {MatCheckboxChange} $event
@ -482,6 +529,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
this.contentForm.patchValue({ this.contentForm.patchValue({
statute_paragraph_id: null statute_paragraph_id: null
}); });
this.updateWorkflowIdForCreateForm();
} }
/** /**
@ -548,9 +596,13 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
/** /**
* Observes the repository for changes in the motion recommender * Observes the repository for changes in the motion recommender
*/ */
public getRecommender(): void { public setupRecommender(): void {
this.repo.getRecommenderObservable().subscribe(newRecommender => { const configKey = this.motion.isStatuteAmendment() ? 'motions_statute_recommendations_by' : 'motions_recommendations_by';
this.recommender = newRecommender; if (this.recommenderSubscription) {
this.recommenderSubscription.unsubscribe();
}
this.recommenderSubscription = this.configService.get(configKey).subscribe(recommender => {
this.recommender = recommender;
}); });
} }
@ -571,10 +623,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
/** /**
* Init. * Init.
* Calls getRecommender and sets the surrounding motions to navigate back and forth * Sets the surrounding motions to navigate back and forth
*/ */
public ngOnInit(): void { public ngOnInit(): void {
this.getRecommender();
this.repo.getViewModelListObservable().subscribe(newMotionList => { this.repo.getViewModelListObservable().subscribe(newMotionList => {
if (newMotionList) { if (newMotionList) {
this.allMotions = newMotionList; this.allMotions = newMotionList;

View File

@ -126,6 +126,10 @@ export class ViewMotion extends BaseViewModel {
return this._workflow; return this._workflow;
} }
public get workflow_id(): number {
return this.motion ? this.motion.workflow_id : null;
}
public get state(): WorkflowState { public get state(): WorkflowState {
return this._state; return this._state;
} }

View File

@ -18,8 +18,6 @@ import { ViewStatuteParagraph } from '../models/view-statute-paragraph';
import { Identifiable } from '../../../shared/models/base/identifiable'; import { Identifiable } from '../../../shared/models/base/identifiable';
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service'; import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
import { HttpService } from 'app/core/services/http.service'; import { HttpService } from 'app/core/services/http.service';
import { ConfigService } from 'app/core/services/config.service';
import { Observable } from 'rxjs';
import { Item } from 'app/shared/models/agenda/item'; import { Item } from 'app/shared/models/agenda/item';
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component'; import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
@ -52,7 +50,6 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
mapperService: CollectionStringModelMapperService, mapperService: CollectionStringModelMapperService,
private dataSend: DataSendService, private dataSend: DataSendService,
private httpService: HttpService, private httpService: HttpService,
private configService: ConfigService,
private readonly lineNumbering: LinenumberingService, private readonly lineNumbering: LinenumberingService,
private readonly diff: DiffService private readonly diff: DiffService
) { ) {
@ -144,15 +141,6 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
await this.httpService.put(restPath, { recommendation: stateId }); await this.httpService.put(restPath, { recommendation: stateId });
} }
/**
* Returns the motions_recommendations_by observable from the config service
*
* @return an observable that contains the motions "Recommended by" string
*/
public getRecommenderObservable(): Observable<string> {
return this.configService.get('motions_recommendations_by');
}
/** /**
* Sends the changed nodes to the server. * Sends the changed nodes to the server.
* *

View File

@ -1,6 +1,7 @@
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from openslides.core.config import ConfigVariable from openslides.core.config import ConfigVariable
from openslides.poll.majority import majorityMethods
def get_config_variables(): def get_config_variables():
@ -47,8 +48,9 @@ def get_config_variables():
# TODO: Add server side validation of the choices. # TODO: Add server side validation of the choices.
yield ConfigVariable( yield ConfigVariable(
name='assignments_poll_default_majority_method', name='assignments_poll_default_majority_method',
default_value='simple_majority', default_value=majorityMethods[0]['value'],
input_type='majorityMethod', input_type='choice',
choices=majorityMethods,
label='Required majority', label='Required majority',
help_text='Default method to check whether a candidate has reached the required majority.', help_text='Default method to check whether a candidate has reached the required majority.',
weight=425, weight=425,

View File

@ -29,7 +29,6 @@ INPUT_TYPE_MAPPING = {
'choice': str, 'choice': str,
'colorpicker': str, 'colorpicker': str,
'datetimepicker': int, 'datetimepicker': int,
'majorityMethod': str,
'static': dict, 'static': dict,
'translations': list, 'translations': list,
} }

View File

@ -1,6 +1,7 @@
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from openslides.core.config import ConfigVariable from openslides.core.config import ConfigVariable
from openslides.poll.majority import majorityMethods
from .models import Workflow from .models import Workflow
@ -34,6 +35,16 @@ def get_config_variables():
group='Motions', group='Motions',
subgroup='General') subgroup='General')
yield ConfigVariable(
name='motions_statute_amendments_workflow',
default_value='1',
input_type='choice',
label='Workflow of new statute amendments',
choices=get_workflow_choices,
weight=312,
group='Motions',
subgroup='General')
yield ConfigVariable( yield ConfigVariable(
name='motions_identifier', name='motions_identifier',
default_value='per_category', default_value='per_category',
@ -124,6 +135,16 @@ def get_config_variables():
group='Motions', group='Motions',
subgroup='General') subgroup='General')
yield ConfigVariable(
name='motions_statute_recommendations_by',
default_value='',
label='Name of statute recommender',
help_text='Will be displayed as label before selected statute recommendation. ' +
'Use an empty value to disable the statute recommendation system.',
weight=333,
group='Motions',
subgroup='General')
yield ConfigVariable( yield ConfigVariable(
name='motions_recommendation_text_mode', name='motions_recommendation_text_mode',
default_value='original', default_value='original',
@ -134,7 +155,7 @@ def get_config_variables():
{'value': 'changed', 'display_name': 'Changed version'}, {'value': 'changed', 'display_name': 'Changed version'},
{'value': 'diff', 'display_name': 'Diff version'}, {'value': 'diff', 'display_name': 'Diff version'},
{'value': 'agreed', 'display_name': 'Final version'}), {'value': 'agreed', 'display_name': 'Final version'}),
weight=333, weight=334,
group='Motions', group='Motions',
subgroup='General') subgroup='General')
@ -145,7 +166,7 @@ def get_config_variables():
default_value=False, default_value=False,
input_type='boolean', input_type='boolean',
label='Activate statutes', label='Activate statutes',
weight=334, weight=335,
group='Motions', group='Motions',
subgroup='General') subgroup='General')
@ -155,7 +176,7 @@ def get_config_variables():
default_value=False, default_value=False,
input_type='boolean', input_type='boolean',
label='Activate amendments', label='Activate amendments',
weight=335, weight=336,
group='Motions', group='Motions',
subgroup='Amendments') subgroup='Amendments')
@ -233,8 +254,9 @@ def get_config_variables():
# TODO: Add server side validation of the choices. # TODO: Add server side validation of the choices.
yield ConfigVariable( yield ConfigVariable(
name='motions_poll_default_majority_method', name='motions_poll_default_majority_method',
default_value='simple_majority', default_value=majorityMethods[0]['value'],
input_type='majorityMethod', input_type='choice',
choices=majorityMethods,
label='Required majority', label='Required majority',
help_text='Default method to check whether a motion has reached the required majority.', help_text='Default method to check whether a motion has reached the required majority.',
weight=357, weight=357,

View File

@ -0,0 +1,7 @@
# Common majority methods for all apps using polls. The first one should be the default.
majorityMethods = (
{'value': 'simple_majority', 'display_name': 'Simple majority'},
{'value': 'two-thirds_majority', 'display_name': 'Two-thirds majority'},
{'value': 'three-quarters_majority', 'display_name': 'Three-quarters majority'},
{'value': 'disabled', 'display_name': 'Disabled'},
)