Merge pull request #4409 from FinnStutzenstein/motionBlockSlide
Add referenced motions by the server (fixes #4383)
This commit is contained in:
commit
367df3b095
@ -92,6 +92,22 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||||
|
if (motion.identifier) {
|
||||||
|
return motion.identifier + ': ' + motion.title;
|
||||||
|
} else {
|
||||||
|
return motion.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getIdentifierOrTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||||
|
if (motion.identifier) {
|
||||||
|
return motion.identifier;
|
||||||
|
} else {
|
||||||
|
return motion.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public getAgendaTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
public getAgendaTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||||
// if the identifier is set, the title will be 'Motion <identifier>'.
|
// if the identifier is set, the title will be 'Motion <identifier>'.
|
||||||
if (motion.identifier) {
|
if (motion.identifier) {
|
||||||
@ -149,6 +165,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
tags,
|
tags,
|
||||||
parent
|
parent
|
||||||
);
|
);
|
||||||
|
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
|
||||||
|
viewMotion.getTitle = () => this.getTitle(viewMotion);
|
||||||
viewMotion.getVerboseName = this.getVerboseName;
|
viewMotion.getVerboseName = this.getVerboseName;
|
||||||
viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion);
|
viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion);
|
||||||
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
||||||
@ -759,7 +777,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
}
|
}
|
||||||
let state = this.translate.instant(motion.state.name);
|
let state = this.translate.instant(motion.state.name);
|
||||||
if (motion.stateExtension && motion.state.show_state_extension_field) {
|
if (motion.stateExtension && motion.state.show_state_extension_field) {
|
||||||
state += ' ' + this.solveExtensionPlaceHolder(motion.stateExtension);
|
state += ' ' + this.parseMotionPlaceholders(motion.stateExtension);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -775,7 +793,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
if (motion.recommendation) {
|
if (motion.recommendation) {
|
||||||
let rec = this.translate.instant(motion.recommendation.recommendation_label);
|
let rec = this.translate.instant(motion.recommendation.recommendation_label);
|
||||||
if (motion.recommendationExtension && motion.recommendation.show_recommendation_extension_field) {
|
if (motion.recommendationExtension && motion.recommendation.show_recommendation_extension_field) {
|
||||||
rec += ' ' + this.solveExtensionPlaceHolder(motion.recommendationExtension);
|
rec += ' ' + this.parseMotionPlaceholders(motion.recommendationExtension);
|
||||||
}
|
}
|
||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
@ -785,20 +803,16 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
* Replaces any motion placeholder (`[motion:id]`) with the motion's title(s)
|
* Replaces any motion placeholder (`[motion:id]`) with the motion's title(s)
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
* @returns the string with the motion titles replacing the placeholders, '??' strings for errors
|
* @returns the string with the motion titles replacing the placeholders
|
||||||
*/
|
*/
|
||||||
public solveExtensionPlaceHolder(value: string): string {
|
public parseMotionPlaceholders(value: string): string {
|
||||||
const beg = value.indexOf('[motion:');
|
return value.replace(/\[motion:(\d+)\]/g, (match, id) => {
|
||||||
const end = value.indexOf(']');
|
const motion = this.getViewModel(id);
|
||||||
if (beg > -1 && (end > -1 && end > beg)) {
|
if (motion) {
|
||||||
const id = Number(value.substring(beg + 8, end));
|
return motion.getIdentifierOrTitle();
|
||||||
const referedMotion = Number.isNaN(id) ? null : this.getViewModel(id);
|
|
||||||
const title = referedMotion ? referedMotion.identifier : '??';
|
|
||||||
value = value.substring(0, beg) + title + value.substring(end + 1);
|
|
||||||
// recursively check for additional occurrences
|
|
||||||
return this.solveExtensionPlaceHolder(value);
|
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return this.translate.instant('<unknown motion>');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,11 +278,11 @@
|
|||||||
*ngIf="motion.recommendation && motion.recommendation.show_recommendation_extension_field"
|
*ngIf="motion.recommendation && motion.recommendation.show_recommendation_extension_field"
|
||||||
class="spacer-top-10"
|
class="spacer-top-10"
|
||||||
>
|
>
|
||||||
<form [formGroup]="searchMotionForm">
|
<form [formGroup]="recommendationExtensionForm">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
[formControl]="searchMotionForm.get('recoExtension')"
|
[formControl]="recommendationExtensionForm.get('recoExtension')"
|
||||||
placeholder="{{ 'Extension' | translate }}"
|
placeholder="{{ 'Extension' | translate }}"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -291,8 +291,8 @@
|
|||||||
</button>
|
</button>
|
||||||
<os-search-value-selector
|
<os-search-value-selector
|
||||||
ngDefaultControl
|
ngDefaultControl
|
||||||
[form]="searchMotionForm"
|
[form]="recommendationExtensionForm"
|
||||||
[formControl]="searchMotionForm.get('motion_id')"
|
[formControl]="recommendationExtensionForm.get('motion_id')"
|
||||||
[multiple]="false"
|
[multiple]="false"
|
||||||
listname="{{ 'Motions' | translate }}"
|
listname="{{ 'Motions' | translate }}"
|
||||||
[InputListValues]="motionObserver"
|
[InputListValues]="motionObserver"
|
||||||
|
@ -70,7 +70,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
/**
|
/**
|
||||||
* To search other motions as extension via search value selector
|
* To search other motions as extension via search value selector
|
||||||
*/
|
*/
|
||||||
public searchMotionForm: FormGroup;
|
public recommendationExtensionForm: FormGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the motion is edited
|
* Determine if the motion is edited
|
||||||
@ -636,7 +636,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
const statuteAmendmentFieldName = 'statute_amendment';
|
const statuteAmendmentFieldName = 'statute_amendment';
|
||||||
contentPatch[statuteAmendmentFieldName] = formMotion.isStatuteAmendment();
|
contentPatch[statuteAmendmentFieldName] = formMotion.isStatuteAmendment();
|
||||||
this.contentForm.patchValue(contentPatch);
|
this.contentForm.patchValue(contentPatch);
|
||||||
this.searchMotionForm.get('recoExtension').setValue(this.motion.recommendationExtension);
|
this.recommendationExtensionForm.get('recoExtension').setValue(this.motion.recommendationExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -679,13 +679,13 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
// create the search motion form
|
// create the search motion form
|
||||||
this.searchMotionForm = this.formBuilder.group({
|
this.recommendationExtensionForm = this.formBuilder.group({
|
||||||
motion_id: [],
|
motion_id: [],
|
||||||
recoExtension: []
|
recoExtension: []
|
||||||
});
|
});
|
||||||
|
|
||||||
// Detect changes in in search motion form
|
// Detect changes in in search motion form
|
||||||
this.searchMotionForm.get('motion_id').valueChanges.subscribe(change => {
|
this.recommendationExtensionForm.get('motion_id').valueChanges.subscribe(change => {
|
||||||
this.addMotionExtension(change);
|
this.addMotionExtension(change);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1167,8 +1167,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
* @param id the ID of a selected motion returned by a search value selector
|
* @param id the ID of a selected motion returned by a search value selector
|
||||||
*/
|
*/
|
||||||
public addMotionExtension(id: number): void {
|
public addMotionExtension(id: number): void {
|
||||||
const recoExtensionValue = this.searchMotionForm.get('recoExtension').value;
|
const recoExtensionValue = this.recommendationExtensionForm.get('recoExtension').value || '';
|
||||||
this.searchMotionForm.get('recoExtension').setValue(`${recoExtensionValue}[motion:${id}]`);
|
this.recommendationExtensionForm.get('recoExtension').setValue(`${recoExtensionValue}[motion:${id}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1185,7 +1185,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
* in {@link newRecommendationExtension}
|
* in {@link newRecommendationExtension}
|
||||||
*/
|
*/
|
||||||
public setRecommendationExtension(): void {
|
public setRecommendationExtension(): void {
|
||||||
this.repo.setRecommendationExtension(this.motion, this.searchMotionForm.get('recoExtension').value);
|
this.repo.setRecommendationExtension(this.motion, this.recommendationExtensionForm.get('recoExtension').value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -323,17 +323,27 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
/**
|
/**
|
||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getAgendaTitle;
|
public getTitle: () => string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getAgendaTitleWithType;
|
public getIdentifierOrTitle: () => string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getVerboseName;
|
public getAgendaTitle: () => string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set by the repository
|
||||||
|
*/
|
||||||
|
public getAgendaTitleWithType: () => string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set by the repository
|
||||||
|
*/
|
||||||
|
public getVerboseName: () => string;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
motion: Motion,
|
motion: Motion,
|
||||||
@ -362,14 +372,6 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle = () => {
|
|
||||||
if (this.identifier) {
|
|
||||||
return this.identifier + ': ' + this.title;
|
|
||||||
} else {
|
|
||||||
return this.title;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public getAgendaItem(): ViewItem {
|
public getAgendaItem(): ViewItem {
|
||||||
return this.item;
|
return this.item;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
export interface MotionBlockSlideMotionRepresentation {
|
export interface MotionTitleInformation {
|
||||||
title: string;
|
title: string;
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MotionBlockSlideMotionRepresentation extends MotionTitleInformation {
|
||||||
recommendation?: {
|
recommendation?: {
|
||||||
name: string;
|
name: string;
|
||||||
css_class: string;
|
css_class: string;
|
||||||
@ -11,4 +14,5 @@ export interface MotionBlockSlideMotionRepresentation {
|
|||||||
export interface MotionBlockSlideData {
|
export interface MotionBlockSlideData {
|
||||||
title: string;
|
title: string;
|
||||||
motions: MotionBlockSlideMotionRepresentation[];
|
motions: MotionBlockSlideMotionRepresentation[];
|
||||||
|
referenced_motions: { [id: number]: MotionTitleInformation };
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div *ngFor="let motion of data.data.motions">
|
<div *ngFor="let motion of data.data.motions">
|
||||||
<div class="ellipsis-overflow">
|
<div class="ellipsis-overflow">
|
||||||
{{ motion.identifier }}: {{ motion.title }}
|
{{ getMotionTitle(motion) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="white ellipsis-overflow">
|
<div class="white ellipsis-overflow">
|
||||||
<mat-basic-chip *ngIf="motion.recommendation" disableRipple [ngClass]="getStateCssColor(motion)">
|
<mat-basic-chip *ngIf="motion.recommendation" disableRipple [ngClass]="getStateCssColor(motion)">
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data';
|
import {
|
||||||
import { Motion } from 'app/shared/models/motions/motion';
|
MotionBlockSlideData,
|
||||||
|
MotionBlockSlideMotionRepresentation,
|
||||||
|
MotionTitleInformation
|
||||||
|
} from './motion-block-slide-data';
|
||||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
|
import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -17,8 +20,8 @@ export class MotionBlockSlideComponent extends BaseSlideComponent<MotionBlockSli
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMotionTitle(motion: Partial<Motion>): string {
|
public getMotionTitle(motion: MotionTitleInformation): string {
|
||||||
return this.motionRepo.getAgendaTitle(motion);
|
return this.motionRepo.getTitle(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStateCssColor(motion: MotionBlockSlideMotionRepresentation): string {
|
public getStateCssColor(motion: MotionBlockSlideMotionRepresentation): string {
|
||||||
@ -28,7 +31,16 @@ export class MotionBlockSlideComponent extends BaseSlideComponent<MotionBlockSli
|
|||||||
public getRecommendationLabel(motion: MotionBlockSlideMotionRepresentation): string {
|
public getRecommendationLabel(motion: MotionBlockSlideMotionRepresentation): string {
|
||||||
let recommendation = this.translate.instant(motion.recommendation.name);
|
let recommendation = this.translate.instant(motion.recommendation.name);
|
||||||
if (motion.recommendation_extension) {
|
if (motion.recommendation_extension) {
|
||||||
recommendation += ' ' + this.motionRepo.solveExtensionPlaceHolder(motion.recommendation_extension);
|
const extension = motion.recommendation_extension.replace(/\[motion:(\d+)\]/g, (match, id) => {
|
||||||
|
const titleInformation = this.data.data.referenced_motions[id];
|
||||||
|
if (titleInformation) {
|
||||||
|
return this.motionRepo.getIdentifierOrTitle(titleInformation);
|
||||||
|
} else {
|
||||||
|
return this.translate.instant('<unknown motion>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
recommendation += ' ' + extension;
|
||||||
}
|
}
|
||||||
return recommendation;
|
return recommendation;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import re
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from ..users.projector import get_user_name
|
from ..users.projector import get_user_name
|
||||||
@ -224,7 +225,17 @@ def motion_block_slide(
|
|||||||
f"motion block with id {motion_block_id} does not exist"
|
f"motion block with id {motion_block_id} does not exist"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# All motions in this motion block
|
||||||
motions = []
|
motions = []
|
||||||
|
|
||||||
|
# All motions meantioned in recommendations of the above motions.
|
||||||
|
# Only title_informations will be collected. With this, the client can
|
||||||
|
# replace the placeholders in the recommendation correctly
|
||||||
|
# This maps the motion id to the title information
|
||||||
|
referenced_motions: Dict[int, Dict[str, str]] = {}
|
||||||
|
motion_placeholder_regex = re.compile(r"\[motion:(\d+)\]")
|
||||||
|
|
||||||
|
# Search motions.
|
||||||
for motion in all_data["motions/motion"].values():
|
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 = {
|
motion_object = {
|
||||||
@ -242,13 +253,35 @@ def motion_block_slide(
|
|||||||
"css_class": recommendation["css_class"],
|
"css_class": recommendation["css_class"],
|
||||||
}
|
}
|
||||||
if recommendation["show_recommendation_extension_field"]:
|
if recommendation["show_recommendation_extension_field"]:
|
||||||
motion_object["recommendation_extension"] = motion[
|
recommendation_extension = motion["recommendation_extension"]
|
||||||
"recommendation_extension"
|
|
||||||
|
motion_object["recommendation_extension"] = recommendation_extension
|
||||||
|
# Collect all meantioned motions via [motion:<id>]
|
||||||
|
referenced_ids = [
|
||||||
|
int(id)
|
||||||
|
for id in motion_placeholder_regex.findall(
|
||||||
|
recommendation_extension
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
for id in referenced_ids:
|
||||||
|
if (
|
||||||
|
id not in referenced_motions
|
||||||
|
and id in all_data["motions/motion"]
|
||||||
|
):
|
||||||
|
referenced_motions[id] = {
|
||||||
|
"title": all_data["motions/motion"][id]["title"],
|
||||||
|
"identifier": all_data["motions/motion"][id][
|
||||||
|
"identifier"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
motions.append(motion_object)
|
motions.append(motion_object)
|
||||||
|
|
||||||
return {"title": motion_block["title"], "motions": motions}
|
return {
|
||||||
|
"title": motion_block["title"],
|
||||||
|
"motions": motions,
|
||||||
|
"referenced_motions": referenced_motions,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def register_projector_slides() -> None:
|
def register_projector_slides() -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user