Add referenced motions by the server (fixes #4383)
This commit is contained in:
parent
23c1857fa6
commit
4cde0431ab
@ -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>) => {
|
||||
// if the identifier is set, the title will be 'Motion <identifier>'.
|
||||
if (motion.identifier) {
|
||||
@ -149,6 +165,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
tags,
|
||||
parent
|
||||
);
|
||||
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
|
||||
viewMotion.getTitle = () => this.getTitle(viewMotion);
|
||||
viewMotion.getVerboseName = this.getVerboseName;
|
||||
viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion);
|
||||
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
||||
@ -759,7 +777,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
}
|
||||
let state = this.translate.instant(motion.state.name);
|
||||
if (motion.stateExtension && motion.state.show_state_extension_field) {
|
||||
state += ' ' + this.solveExtensionPlaceHolder(motion.stateExtension);
|
||||
state += ' ' + this.parseMotionPlaceholders(motion.stateExtension);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@ -775,7 +793,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
if (motion.recommendation) {
|
||||
let rec = this.translate.instant(motion.recommendation.recommendation_label);
|
||||
if (motion.recommendationExtension && motion.recommendation.show_recommendation_extension_field) {
|
||||
rec += ' ' + this.solveExtensionPlaceHolder(motion.recommendationExtension);
|
||||
rec += ' ' + this.parseMotionPlaceholders(motion.recommendationExtension);
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
@ -785,20 +803,16 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
* Replaces any motion placeholder (`[motion:id]`) with the motion's title(s)
|
||||
*
|
||||
* @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 {
|
||||
const beg = value.indexOf('[motion:');
|
||||
const end = value.indexOf(']');
|
||||
if (beg > -1 && (end > -1 && end > beg)) {
|
||||
const id = Number(value.substring(beg + 8, end));
|
||||
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);
|
||||
public parseMotionPlaceholders(value: string): string {
|
||||
return value.replace(/\[motion:(\d+)\]/g, (match, id) => {
|
||||
const motion = this.getViewModel(id);
|
||||
if (motion) {
|
||||
return motion.getIdentifierOrTitle();
|
||||
} else {
|
||||
return value;
|
||||
return this.translate.instant('<unknown motion>');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -278,11 +278,11 @@
|
||||
*ngIf="motion.recommendation && motion.recommendation.show_recommendation_extension_field"
|
||||
class="spacer-top-10"
|
||||
>
|
||||
<form [formGroup]="searchMotionForm">
|
||||
<form [formGroup]="recommendationExtensionForm">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
[formControl]="searchMotionForm.get('recoExtension')"
|
||||
[formControl]="recommendationExtensionForm.get('recoExtension')"
|
||||
placeholder="{{ 'Extension' | translate }}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
@ -291,8 +291,8 @@
|
||||
</button>
|
||||
<os-search-value-selector
|
||||
ngDefaultControl
|
||||
[form]="searchMotionForm"
|
||||
[formControl]="searchMotionForm.get('motion_id')"
|
||||
[form]="recommendationExtensionForm"
|
||||
[formControl]="recommendationExtensionForm.get('motion_id')"
|
||||
[multiple]="false"
|
||||
listname="{{ 'Motions' | translate }}"
|
||||
[InputListValues]="motionObserver"
|
||||
|
@ -70,7 +70,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
/**
|
||||
* To search other motions as extension via search value selector
|
||||
*/
|
||||
public searchMotionForm: FormGroup;
|
||||
public recommendationExtensionForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Determine if the motion is edited
|
||||
@ -636,7 +636,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
const statuteAmendmentFieldName = 'statute_amendment';
|
||||
contentPatch[statuteAmendmentFieldName] = formMotion.isStatuteAmendment();
|
||||
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
|
||||
this.searchMotionForm = this.formBuilder.group({
|
||||
this.recommendationExtensionForm = this.formBuilder.group({
|
||||
motion_id: [],
|
||||
recoExtension: []
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
@ -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
|
||||
*/
|
||||
public addMotionExtension(id: number): void {
|
||||
const recoExtensionValue = this.searchMotionForm.get('recoExtension').value;
|
||||
this.searchMotionForm.get('recoExtension').setValue(`${recoExtensionValue}[motion:${id}]`);
|
||||
const recoExtensionValue = this.recommendationExtensionForm.get('recoExtension').value || '';
|
||||
this.recommendationExtensionForm.get('recoExtension').setValue(`${recoExtensionValue}[motion:${id}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1185,7 +1185,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
* in {@link newRecommendationExtension}
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public getAgendaTitle;
|
||||
public getTitle: () => string;
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getAgendaTitleWithType;
|
||||
public getIdentifierOrTitle: () => string;
|
||||
|
||||
/**
|
||||
* 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(
|
||||
motion: Motion,
|
||||
@ -362,14 +372,6 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
this._parent = parent;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
if (this.identifier) {
|
||||
return this.identifier + ': ' + this.title;
|
||||
} else {
|
||||
return this.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.item;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
export interface MotionBlockSlideMotionRepresentation {
|
||||
export interface MotionTitleInformation {
|
||||
title: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
export interface MotionBlockSlideMotionRepresentation extends MotionTitleInformation {
|
||||
recommendation?: {
|
||||
name: string;
|
||||
css_class: string;
|
||||
@ -11,4 +14,5 @@ export interface MotionBlockSlideMotionRepresentation {
|
||||
export interface MotionBlockSlideData {
|
||||
title: string;
|
||||
motions: MotionBlockSlideMotionRepresentation[];
|
||||
referenced_motions: { [id: number]: MotionTitleInformation };
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<div *ngFor="let motion of data.data.motions">
|
||||
<div class="ellipsis-overflow">
|
||||
{{ motion.identifier }}: {{ motion.title }}
|
||||
{{ getMotionTitle(motion) }}
|
||||
</div>
|
||||
<div class="white ellipsis-overflow">
|
||||
<mat-basic-chip *ngIf="motion.recommendation" disableRipple [ngClass]="getStateCssColor(motion)">
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||
import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data';
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
import {
|
||||
MotionBlockSlideData,
|
||||
MotionBlockSlideMotionRepresentation,
|
||||
MotionTitleInformation
|
||||
} from './motion-block-slide-data';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -17,8 +20,8 @@ export class MotionBlockSlideComponent extends BaseSlideComponent<MotionBlockSli
|
||||
super();
|
||||
}
|
||||
|
||||
public getMotionTitle(motion: Partial<Motion>): string {
|
||||
return this.motionRepo.getAgendaTitle(motion);
|
||||
public getMotionTitle(motion: MotionTitleInformation): string {
|
||||
return this.motionRepo.getTitle(motion);
|
||||
}
|
||||
|
||||
public getStateCssColor(motion: MotionBlockSlideMotionRepresentation): string {
|
||||
@ -28,7 +31,16 @@ export class MotionBlockSlideComponent extends BaseSlideComponent<MotionBlockSli
|
||||
public getRecommendationLabel(motion: MotionBlockSlideMotionRepresentation): string {
|
||||
let recommendation = this.translate.instant(motion.recommendation.name);
|
||||
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;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Any, Dict
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
# All motions in this motion block
|
||||
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():
|
||||
if motion["motion_block_id"] == motion_block_id:
|
||||
motion_object = {
|
||||
@ -242,13 +253,35 @@ def motion_block_slide(
|
||||
"css_class": recommendation["css_class"],
|
||||
}
|
||||
if recommendation["show_recommendation_extension_field"]:
|
||||
motion_object["recommendation_extension"] = motion[
|
||||
"recommendation_extension"
|
||||
recommendation_extension = motion["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)
|
||||
|
||||
return {"title": motion_block["title"], "motions": motions}
|
||||
return {
|
||||
"title": motion_block["title"],
|
||||
"motions": motions,
|
||||
"referenced_motions": referenced_motions,
|
||||
}
|
||||
|
||||
|
||||
def register_projector_slides() -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user