Merge pull request #4470 from FinnStutzenstein/motionSLideRecommendationFix

Replace recommendation on client and server for motion slide
This commit is contained in:
Emanuel Schütze 2019-03-07 16:55:53 +01:00 committed by GitHub
commit 70191ce455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 46 deletions

View File

@ -0,0 +1,41 @@
import { TranslateService } from '@ngx-translate/core';
import { BaseSlideComponent } from 'app/slides/base-slide-component';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { MotionTitleInformation } from '../motion-block/motion-block-slide-data';
/**
* Format for referenced motions: A mapping of motion ids to their title information.
*/
export interface ReferencedMotions {
[id: number]: MotionTitleInformation;
}
/**
* Base slide for motions and motion blocks. This Provides the functionality of
* replacing referenced motions (format: `[motion:<id>]`) in strings.
*/
export class BaseMotionSlideComponent<T extends object> extends BaseSlideComponent<T> {
public constructor(protected translate: TranslateService, protected motionRepo: MotionRepositoryService) {
super();
}
/**
* Replaces all motion placeholders with the motion titles or `<unknown motion>` if the
* referenced motion doe snot exist.
*
* @param text the text to replace
* @param referencedMotions an object holding the title information for all referenced motions
* @returns the new string
*/
public replaceReferencedMotions(text: string, referencedMotions: ReferencedMotions): string {
return text.replace(/\[motion:(\d+)\]/g, (match, id) => {
const titleInformation = referencedMotions[id];
if (titleInformation) {
return this.motionRepo.getIdentifierOrTitle(titleInformation);
} else {
return this.translate.instant('<unknown motion>');
}
});
}
}

View File

@ -1,3 +1,5 @@
import { ReferencedMotions } from '../base/base-motion-slide';
export interface MotionTitleInformation { export interface MotionTitleInformation {
title: string; title: string;
identifier?: string; identifier?: string;
@ -14,5 +16,5 @@ export interface MotionBlockSlideMotionRepresentation extends MotionTitleInforma
export interface MotionBlockSlideData { export interface MotionBlockSlideData {
title: string; title: string;
motions: MotionBlockSlideMotionRepresentation[]; motions: MotionBlockSlideMotionRepresentation[];
referenced_motions: { [id: number]: MotionTitleInformation }; referenced_motions: ReferencedMotions;
} }

View File

@ -1,6 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { BaseSlideComponent } from 'app/slides/base-slide-component';
import { import {
MotionBlockSlideData, MotionBlockSlideData,
MotionBlockSlideMotionRepresentation, MotionBlockSlideMotionRepresentation,
@ -9,15 +8,16 @@ import {
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';
import { BaseMotionSlideComponent } from '../base/base-motion-slide';
@Component({ @Component({
selector: 'os-motion-block-slide', selector: 'os-motion-block-slide',
templateUrl: './motion-block-slide.component.html', templateUrl: './motion-block-slide.component.html',
styleUrls: ['./motion-block-slide.component.scss'] styleUrls: ['./motion-block-slide.component.scss']
}) })
export class MotionBlockSlideComponent extends BaseSlideComponent<MotionBlockSlideData> { export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBlockSlideData> {
public constructor(private motionRepo: MotionRepositoryService, private translate: TranslateService) { public constructor(translate: TranslateService, motionRepo: MotionRepositoryService) {
super(); super(translate, motionRepo);
} }
public getMotionTitle(motion: MotionTitleInformation): string { public getMotionTitle(motion: MotionTitleInformation): string {
@ -31,16 +31,8 @@ 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) {
const extension = motion.recommendation_extension.replace(/\[motion:(\d+)\]/g, (match, id) => { recommendation +=
const titleInformation = this.data.data.referenced_motions[id]; ' ' + this.replaceReferencedMotions(motion.recommendation_extension, this.data.data.referenced_motions);
if (titleInformation) {
return this.motionRepo.getIdentifierOrTitle(titleInformation);
} else {
return this.translate.instant('<unknown motion>');
}
});
recommendation += ' ' + extension;
} }
return recommendation; return recommendation;
} }

View File

@ -1,5 +1,6 @@
import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion'; import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion';
import { MergeAmendment } from '../../../shared/models/motions/workflow-state'; import { MergeAmendment } from '../../../shared/models/motions/workflow-state';
import { ReferencedMotions } from '../base/base-motion-slide';
/** /**
* This interface describes the data returned by the server about an amendment. * This interface describes the data returned by the server about an amendment.
@ -65,6 +66,7 @@ export interface MotionSlideData {
recommender?: string; recommender?: string;
recommendation?: string; recommendation?: string;
recommendation_extension?: string; recommendation_extension?: string;
referenced_motions?: ReferencedMotions;
base_motion?: MotionSlideDataBaseMotion; base_motion?: MotionSlideDataBaseMotion;
base_statute?: MotionSlideDataBaseStatute; base_statute?: MotionSlideDataBaseStatute;
amendment_paragraphs: string[]; amendment_paragraphs: string[];

View File

@ -9,7 +9,7 @@
<!-- Recommendation --> <!-- Recommendation -->
<div *ngIf="data.data.recommendation && data.data.recommender"> <div *ngIf="data.data.recommendation && data.data.recommender">
<h3>{{ data.data.recommender }}</h3> <h3>{{ data.data.recommender }}</h3>
{{ data.data.recommendation | translate }} {{ data.data.recommendation_extension }} {{ getRecommendationLabel() }}
</div> </div>
</div> </div>

View File

@ -1,7 +1,8 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { BaseSlideComponent } from 'app/slides/base-slide-component'; import { TranslateService } from '@ngx-translate/core';
import { MotionSlideData, MotionSlideDataAmendment } from './motion-slide-data'; import { MotionSlideData, MotionSlideDataAmendment } from './motion-slide-data';
import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion'; import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion';
import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-services/diff.service'; import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-services/diff.service';
@ -10,13 +11,15 @@ import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-c
import { MotionSlideObjChangeReco } from './motion-slide-obj-change-reco'; import { MotionSlideObjChangeReco } from './motion-slide-obj-change-reco';
import { SlideData } from '../../../core/core-services/projector-data.service'; import { SlideData } from '../../../core/core-services/projector-data.service';
import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph'; import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph';
import { BaseMotionSlideComponent } from '../base/base-motion-slide';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
@Component({ @Component({
selector: 'os-motion-slide', selector: 'os-motion-slide',
templateUrl: './motion-slide.component.html', templateUrl: './motion-slide.component.html',
styleUrls: ['./motion-slide.component.scss'] styleUrls: ['./motion-slide.component.scss']
}) })
export class MotionSlideComponent extends BaseSlideComponent<MotionSlideData> { export class MotionSlideComponent extends BaseMotionSlideComponent<MotionSlideData> {
/** /**
* Indicates the LineNumberingMode Mode. * Indicates the LineNumberingMode Mode.
*/ */
@ -67,11 +70,26 @@ export class MotionSlideComponent extends BaseSlideComponent<MotionSlideData> {
} }
public constructor( public constructor(
translate: TranslateService,
motionRepo: MotionRepositoryService,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
private lineNumbering: LinenumberingService, private lineNumbering: LinenumberingService,
private diff: DiffService private diff: DiffService
) { ) {
super(); super(translate, motionRepo);
}
public getRecommendationLabel(): string {
let recommendation = this.translate.instant(this.data.data.recommendation);
if (this.data.data.recommendation_extension) {
recommendation +=
' ' +
this.replaceReferencedMotions(
this.data.data.recommendation_extension,
this.data.data.referenced_motions
);
}
return recommendation;
} }
/** /**

View File

@ -10,6 +10,8 @@ from ..utils.projector import (
) )
motion_placeholder_regex = re.compile(r"\[motion:(\d+)\]")
# Important: All functions have to be prune. This means, that thay can only # Important: All functions have to be prune. This means, that thay can only
# access the data, that they get as argument and do not have any # access the data, that they get as argument and do not have any
# side effects. They are called from an async context. So they have # side effects. They are called from an async context. So they have
@ -98,6 +100,28 @@ def get_amendment_base_statute(amendment, all_data):
return {"title": statute["title"], "text": statute["text"]} return {"title": statute["title"], "text": statute["text"]}
def extend_reference_motion_dict(
all_data: AllData,
recommendation: str,
referenced_motions: Dict[int, Dict[str, str]],
) -> None:
"""
Extends a dict of motion ids mapped to their title information.
The client can replace the placeholders in the recommendation correctly.
"""
# Collect all meantioned motions via [motion:<id>]
referenced_ids = [
int(id) for id in motion_placeholder_regex.findall(recommendation)
]
for id in referenced_ids:
# Put every referenced motion into the referenced_motions dict
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"],
}
def motion_slide( def motion_slide(
all_data: AllData, element: Dict[str, Any], projector_id: int all_data: AllData, element: Dict[str, Any], projector_id: int
) -> Dict[str, Any]: ) -> Dict[str, Any]:
@ -189,9 +213,14 @@ def motion_slide(
"recommendation_label" "recommendation_label"
] ]
if recommendation_state["show_recommendation_extension_field"]: if recommendation_state["show_recommendation_extension_field"]:
return_value["recommendation_extension"] = motion[ recommendation_extension = motion["recommendation_extension"]
"recommendation_extension" # All title information for referenced motions in the recommendation
] referenced_motions: Dict[int, Dict[str, str]] = {}
extend_reference_motion_dict(
all_data, recommendation_extension, referenced_motions
)
return_value["recommendation_extension"] = recommendation_extension
return_value["referenced_motions"] = referenced_motions
return_value["recommender"] = get_config( return_value["recommender"] = get_config(
all_data, "motions_recommendations_by" all_data, "motions_recommendations_by"
@ -228,12 +257,8 @@ def motion_block_slide(
# All motions in this motion block # All motions in this motion block
motions = [] motions = []
# All motions meantioned in recommendations of the above motions. # All title information for referenced motions in the recommendation
# 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]] = {} referenced_motions: Dict[int, Dict[str, str]] = {}
motion_placeholder_regex = re.compile(r"\[motion:(\d+)\]")
# Search motions. # Search motions.
for motion in all_data["motions/motion"].values(): for motion in all_data["motions/motion"].values():
@ -254,26 +279,10 @@ def motion_block_slide(
} }
if recommendation["show_recommendation_extension_field"]: if recommendation["show_recommendation_extension_field"]:
recommendation_extension = motion["recommendation_extension"] recommendation_extension = motion["recommendation_extension"]
extend_reference_motion_dict(
all_data, recommendation_extension, referenced_motions
)
motion_object["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)