diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 7ae4992dc..c1da605fd 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -458,7 +458,8 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo await this.httpService.delete(url); } - /** Returns an observable returning the amendments to a given motion + /** + * Returns an observable returning the amendments to a given motion * * @param {number} motionId * @returns {Observable} @@ -473,6 +474,33 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo ); } + /** + * Returns an observable for all motions, that referencing the given motion (via id) + * in the recommendation. + */ + public getRecommendationReferencingMotions(motionId: number): Observable { + return this.getViewModelListObservable().pipe( + map((motions: ViewMotion[]): ViewMotion[] => { + return motions.filter((motion: ViewMotion): boolean => { + if (!motion.recommendationExtension) { + return false; + } + + // Check, if this motion has the motionId in it's recommendation + const placeholderRegex = /\[motion:(\d+)\]/g; + let match; + while ((match = placeholderRegex.exec(motion.recommendationExtension))) { + if (parseInt(match[1], 10) === motionId) { + return true; + } + } + + return false; + }); + }) + ); + } + /** * Returns the amendments to a given motion * diff --git a/client/src/app/site/motions/models/view-motion.ts b/client/src/app/site/motions/models/view-motion.ts index 2c2cbb4d5..d9f1f494d 100644 --- a/client/src/app/site/motions/models/view-motion.ts +++ b/client/src/app/site/motions/models/view-motion.ts @@ -296,7 +296,7 @@ export class ViewMotion extends BaseViewModelWithAgendaItemAndListOfSpeakers + +
+

Recommendation-referencing motions

+
+ {{ motion.identifierOrTitle }} +
+
+
diff --git a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts index 61ba6e4fa..aec9799e2 100644 --- a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts @@ -371,6 +371,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit, */ private navigationSubscription: Subscription; + public recommendationReferencingMotions: ViewMotion[] = []; + /** * Constructs the detail view. * @@ -614,6 +616,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit, this.amendments = amendments; this.recalcUnifiedChanges(); }), + this.repo + .getRecommendationReferencingMotions(motionId) + .subscribe(motions => (this.recommendationReferencingMotions = motions)), this.changeRecoRepo .getChangeRecosOfMotionObservable(motionId) .subscribe((recos: ViewMotionChangeRecommendation[]) => { diff --git a/client/src/app/slides/motions/motion/motion-slide-data.ts b/client/src/app/slides/motions/motion/motion-slide-data.ts index f44a14fb1..7cc6dbe62 100644 --- a/client/src/app/slides/motions/motion/motion-slide-data.ts +++ b/client/src/app/slides/motions/motion/motion-slide-data.ts @@ -1,4 +1,4 @@ -import { ChangeRecoMode, LineNumberingMode } from 'app/site/motions/models/view-motion'; +import { ChangeRecoMode, LineNumberingMode, MotionTitleInformation } from 'app/site/motions/models/view-motion'; import { ReferencedMotions } from '../base/base-motion-slide'; /** @@ -66,6 +66,7 @@ export interface MotionSlideData { recommender?: string; recommendation?: string; recommendation_extension?: string; + recommendation_referencing_motions?: MotionTitleInformation[]; referenced_motions?: ReferencedMotions; base_motion?: MotionSlideDataBaseMotion; base_statute?: MotionSlideDataBaseStatute; diff --git a/client/src/app/slides/motions/motion/motion-slide.component.html b/client/src/app/slides/motions/motion/motion-slide.component.html index 4b505878b..6c9c40293 100644 --- a/client/src/app/slides/motions/motion/motion-slide.component.html +++ b/client/src/app/slides/motions/motion/motion-slide.component.html @@ -11,6 +11,14 @@

{{ data.data.recommender }}

{{ getRecommendationLabel() }}
+ + +
+

Recommendation-referencing motions

+
+ {{ getIdentifierOrTitle(titleInformation) }} +
+
diff --git a/client/src/app/slides/motions/motion/motion-slide.component.ts b/client/src/app/slides/motions/motion/motion-slide.component.ts index 16c6075ff..c12a30c5f 100644 --- a/client/src/app/slides/motions/motion/motion-slide.component.ts +++ b/client/src/app/slides/motions/motion/motion-slide.component.ts @@ -4,7 +4,7 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; import { MotionSlideData, MotionSlideDataAmendment } from './motion-slide-data'; -import { ChangeRecoMode, LineNumberingMode } from 'app/site/motions/models/view-motion'; +import { ChangeRecoMode, LineNumberingMode, MotionTitleInformation } from 'app/site/motions/models/view-motion'; import { DiffLinesInParagraph, DiffService, LineRange } from 'app/core/ui-services/diff.service'; import { LinenumberingService } from 'app/core/ui-services/linenumbering.service'; import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change'; @@ -118,6 +118,10 @@ export class MotionSlideComponent extends BaseMotionSlideComponent Optional[List[Dict[str, Any]]]: + """ + Returns all title information for motions, that are referencing + the given motion (by id) in their recommendation. If there are no + motions, None is returned (instead of []). + """ + recommendation_referencing_motions = [] + for motion in all_data["motions/motion"].values(): + # Motion must have a recommendation and a recommendaiton extension + if not motion["recommendation_id"] or not motion["recommendation_extension"]: + continue + + # The recommendation must allow the extension field (there might be left-overs + # in a motions recommendation extension..) + recommendation = await get_state(all_data, motion, "recommendation_id") + if not recommendation["show_recommendation_extension_field"]: + continue + + # Find referenced motion ids + referenced_ids = [ + int(id) + for id in motion_placeholder_regex.findall( + motion["recommendation_extension"] + ) + ] + + # if one of the referenced ids is the given motion, add the current motion. + if motion_id in referenced_ids: + recommendation_referencing_motions.append( + {"title": motion["title"], "identifier": motion["identifier"]} + ) + return recommendation_referencing_motions or None + + async def motion_block_slide( all_data: AllData, element: Dict[str, Any], projector_id: int ) -> Dict[str, Any]: diff --git a/tests/unit/motions/test_projector.py b/tests/unit/motions/test_projector.py index a8b0eb1eb..e90d6dbf8 100644 --- a/tests/unit/motions/test_projector.py +++ b/tests/unit/motions/test_projector.py @@ -297,6 +297,7 @@ async def test_motion_slide(all_data): "line_length": 90, "line_numbering_mode": "none", "preamble": "The assembly may decide:", + "recommendation_referencing_motions": None, } @@ -322,6 +323,7 @@ async def test_amendment_slide(all_data): "line_length": 90, "line_numbering_mode": "none", "preamble": "The assembly may decide:", + "recommendation_referencing_motions": None, } @@ -347,4 +349,5 @@ async def test_statute_amendment_slide(all_data): "line_length": 90, "line_numbering_mode": "none", "preamble": "The assembly may decide:", + "recommendation_referencing_motions": None, }