Merge pull request #4304 from MaximilianKrambach/pdfExportComments

export motion comments (pdf) and sequential numbers (pdf, csv)
This commit is contained in:
Emanuel Schütze 2019-02-14 20:13:55 +01:00 committed by GitHub
commit 4c6f907b8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 142 additions and 29 deletions

View File

@ -27,6 +27,14 @@
> >
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button
mat-icon-button
*ngIf="!isEditMode && comments[section.id]?.comment"
(click)="pdfExportSection(section)"
matTooltip="{{ 'Export comment' | translate }}"
>
<mat-icon>picture_as_pdf</mat-icon>
</button>
<button <button
mat-icon-button mat-icon-button
*ngIf="isCommentEdited(section)" *ngIf="isCommentEdited(section)"

View File

@ -11,6 +11,7 @@ import { OperatorService } from 'app/core/core-services/operator.service';
import { MotionComment } from 'app/shared/models/motions/motion-comment'; import { MotionComment } from 'app/shared/models/motions/motion-comment';
import { ViewMotion } from '../../models/view-motion'; import { ViewMotion } from '../../models/view-motion';
import { BaseViewComponent } from 'app/site/base/base-view'; import { BaseViewComponent } from 'app/site/base/base-view';
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
/** /**
* Component for the motion comments view * Component for the motion comments view
@ -63,6 +64,7 @@ export class MotionCommentsComponent extends BaseViewComponent {
* @param commentRepo The repository that handles server communication * @param commentRepo The repository that handles server communication
* @param formBuilder Form builder to handle text editing * @param formBuilder Form builder to handle text editing
* @param operator service to get the sections * @param operator service to get the sections
* @param pdfService service to export a comment section to pdf
* @param titleService set the browser title * @param titleService set the browser title
* @param translate the translation service * @param translate the translation service
* @param matSnackBar showing errors and information * @param matSnackBar showing errors and information
@ -71,6 +73,7 @@ export class MotionCommentsComponent extends BaseViewComponent {
private commentRepo: MotionCommentSectionRepositoryService, private commentRepo: MotionCommentSectionRepositoryService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private operator: OperatorService, private operator: OperatorService,
private pdfService: MotionPdfExportService,
titleService: Title, titleService: Title,
translate: TranslateService, translate: TranslateService,
matSnackBar: MatSnackBar matSnackBar: MatSnackBar
@ -177,4 +180,13 @@ export class MotionCommentsComponent extends BaseViewComponent {
public isCommentEdited(section: ViewMotionCommentSection): boolean { public isCommentEdited(section: ViewMotionCommentSection): boolean {
return Object.keys(this.commentForms).includes('' + section.id); return Object.keys(this.commentForms).includes('' + section.id);
} }
/**
* Triggers a direct pdf export of this comment
*
* @param section
*/
public pdfExportSection(section: ViewMotionCommentSection): void {
this.pdfService.exportComment(section, this.motion);
}
} }

View File

@ -51,11 +51,25 @@
<mat-button-toggle value="category"> <span translate>Category</span> </mat-button-toggle> <mat-button-toggle value="category"> <span translate>Category</span> </mat-button-toggle>
<mat-button-toggle value="origin"> <span translate>Origin</span> </mat-button-toggle> <mat-button-toggle value="origin"> <span translate>Origin</span> </mat-button-toggle>
<mat-button-toggle value="block"> <span translate>Motion block</span> </mat-button-toggle> <mat-button-toggle value="block"> <span translate>Motion block</span> </mat-button-toggle>
<mat-button-toggle value="votingResult" #votingResultButton> <mat-button-toggle value="poll" #votingResultButton>
<span translate>Voting result</span> <span translate>Voting result</span>
</mat-button-toggle> </mat-button-toggle>
<mat-button-toggle value="id"><span translate>Sequential number</span></mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
<div *ngIf="commentsToExport.length && exportForm.get('format').value === 'pdf'">
<p class="toggle-group-head" translate>Comments information</p>
<mat-button-toggle-group
class="smaller-buttons"
multiple
formControlName="comments"
>
<mat-button-toggle *ngFor="let comment of commentsToExport" [value]="comment.id">
<span>{{ comment.name }}</span>
</mat-button-toggle>
</mat-button-toggle-group>
<!-- TODO only if not csv -->
</div>
<br /> <br />
</div> </div>

View File

@ -4,6 +4,9 @@ import { MatDialogRef, MatButtonToggle } from '@angular/material';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { LineNumberingMode, ChangeRecoMode } from '../../models/view-motion'; import { LineNumberingMode, ChangeRecoMode } from '../../models/view-motion';
import { InfoToExport } from '../../services/motion-pdf.service';
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
import { ViewMotionCommentSection } from '../../models/view-motion-comment-section';
/** /**
* Dialog component to determine exporting. * Dialog component to determine exporting.
@ -42,16 +45,23 @@ export class MotionExportDialogComponent implements OnInit {
/** /**
* Determine the default meta info to export. * Determine the default meta info to export.
*/ */
private defaultInfoToExport = [ private defaultInfoToExport: InfoToExport[] = [
'submitters', 'submitters',
'state', 'state',
'recommendation', 'recommendation',
'category', 'category',
'origin', 'origin',
'block', 'block',
'votingResult' 'polls',
'id'
]; ];
/**
* @returns a list of availavble commentSections
*/
public get commentsToExport(): ViewMotionCommentSection[] {
return this.commentRepo.getViewModelList();
}
/** /**
* Hold the default lnMode. Will be set by the constructor. * Hold the default lnMode. Will be set by the constructor.
*/ */
@ -82,11 +92,14 @@ export class MotionExportDialogComponent implements OnInit {
* *
* @param formBuilder Creates the export form * @param formBuilder Creates the export form
* @param dialogRef Make the dialog available * @param dialogRef Make the dialog available
* @param configService
* @param commentRepo
*/ */
public constructor( public constructor(
public formBuilder: FormBuilder, public formBuilder: FormBuilder,
public dialogRef: MatDialogRef<MotionExportDialogComponent>, public dialogRef: MatDialogRef<MotionExportDialogComponent>,
public configService: ConfigService public configService: ConfigService,
public commentRepo: MotionCommentSectionRepositoryService
) { ) {
this.defaultLnMode = this.configService.instant('motions_default_line_numbering'); this.defaultLnMode = this.configService.instant('motions_default_line_numbering');
this.defaultCrMode = this.configService.instant('motions_recommendation_text_mode'); this.defaultCrMode = this.configService.instant('motions_recommendation_text_mode');
@ -110,6 +123,8 @@ export class MotionExportDialogComponent implements OnInit {
this.exportForm.get('crMode').setValue(this.crMode.Original); this.exportForm.get('crMode').setValue(this.crMode.Original);
this.exportForm.get('crMode').disable(); this.exportForm.get('crMode').disable();
this.exportForm.get('comments').disable();
// remove the selection of "Diff Version" and set it to default or original // remove the selection of "Diff Version" and set it to default or original
// TODO: Use this over the disable block logic above when the export service supports more than // TODO: Use this over the disable block logic above when the export service supports more than
// just the normal motion text // just the normal motion text
@ -133,6 +148,7 @@ export class MotionExportDialogComponent implements OnInit {
// TODO: CSV Issues // TODO: CSV Issues
// this.diffVersionButton.disabled = true; // this.diffVersionButton.disabled = true;
} else if (value === 'pdf') { } else if (value === 'pdf') {
this.exportForm.get('comments').enable();
this.exportForm.get('lnMode').enable(); this.exportForm.get('lnMode').enable();
this.exportForm.get('lnMode').setValue(this.defaultLnMode); this.exportForm.get('lnMode').setValue(this.defaultLnMode);
@ -157,7 +173,8 @@ export class MotionExportDialogComponent implements OnInit {
lnMode: [this.defaultLnMode], lnMode: [this.defaultLnMode],
crMode: [this.defaultCrMode], crMode: [this.defaultCrMode],
content: [this.defaultContentToExport], content: [this.defaultContentToExport],
metaInfo: [this.defaultInfoToExport] metaInfo: [this.defaultInfoToExport],
comments: []
}); });
} }

View File

@ -216,7 +216,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
result.lnMode, result.lnMode,
result.crMode, result.crMode,
result.content, result.content,
result.metaInfo result.metaInfo,
result.comments
); );
} else if (result.format === 'csv') { } else if (result.format === 'csv') {
this.motionCsvExport.exportMotionList( this.motionCsvExport.exportMotionList(

View File

@ -45,6 +45,11 @@ export class MotionPdfCatalogService {
* Public entry point to conversion of multiple motions * Public entry point to conversion of multiple motions
* *
* @param motions the list of view motions to convert * @param motions the list of view motions to convert
* @param lnMode
* @param crMode
* @param contentToExport
* @param infoToExport
* @param commentsToExport
* @returns pdfmake doc definition as object * @returns pdfmake doc definition as object
*/ */
public motionListToDocDef( public motionListToDocDef(
@ -52,7 +57,8 @@ export class MotionPdfCatalogService {
lnMode?: LineNumberingMode, lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode, crMode?: ChangeRecoMode,
contentToExport?: string[], contentToExport?: string[],
infoToExport?: InfoToExport[] infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object { ): object {
let doc = []; let doc = [];
const motionDocList = []; const motionDocList = [];
@ -63,7 +69,8 @@ export class MotionPdfCatalogService {
lnMode, lnMode,
crMode, crMode,
contentToExport, contentToExport,
infoToExport infoToExport,
commentsToExport
); );
// add id field to the first page of a motion to make it findable over TOC // add id field to the first page of a motion to make it findable over TOC

View File

@ -8,6 +8,7 @@ import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-mo
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { MotionPdfCatalogService } from './motion-pdf-catalog.service'; import { MotionPdfCatalogService } from './motion-pdf-catalog.service';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { ViewMotionCommentSection } from '../models/view-motion-comment-section';
/** /**
* Export service to handle various kind of exporting necessities. * Export service to handle various kind of exporting necessities.
@ -56,15 +57,24 @@ export class MotionPdfExportService {
* @param crMode Change Recommendation Mode * @param crMode Change Recommendation Mode
* @param contentToExport Determine to determine with text and/or reason * @param contentToExport Determine to determine with text and/or reason
* @param infoToExport Determine the meta info to export * @param infoToExport Determine the meta info to export
* @param commentsToExport Comments (by id) to export
*/ */
public exportMotionCatalog( public exportMotionCatalog(
motions: ViewMotion[], motions: ViewMotion[],
lnMode?: LineNumberingMode, lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode, crMode?: ChangeRecoMode,
contentToExport?: string[], contentToExport?: string[],
infoToExport?: InfoToExport[] infoToExport?: InfoToExport[],
commentsToExport?: number[]
): void { ): void {
const doc = this.pdfCatalogService.motionListToDocDef(motions, lnMode, crMode, contentToExport, infoToExport); const doc = this.pdfCatalogService.motionListToDocDef(
motions,
lnMode,
crMode,
contentToExport,
infoToExport,
commentsToExport
);
const filename = this.translate.instant(this.configService.instant<string>('motions_export_title')); const filename = this.translate.instant(this.configService.instant<string>('motions_export_title'));
const metadata = { const metadata = {
title: filename title: filename
@ -101,4 +111,21 @@ export class MotionPdfExportService {
}; };
this.pdfDocumentService.download(doc, filename, metadata); this.pdfDocumentService.download(doc, filename, metadata);
} }
/**
* Exports the given comment with some short information about the
* motion the note refers to
*
* @param comment
* @param motion
*/
public exportComment(comment: ViewMotionCommentSection, motion: ViewMotion): void {
const motionComment = motion.getCommentForSection(comment);
if (motionComment && motionComment.comment) {
const doc = this.motionPdfService.textToDocDef(motionComment.comment, motion, comment.name);
const filename = `${motion.identifierOrTitle} - ${comment.name}`;
const metadata = { title: filename };
this.pdfDocumentService.download(doc, filename, metadata);
}
}
} }

View File

@ -11,11 +11,21 @@ import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion'; import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
import { ViewUnifiedChange } from '../models/view-unified-change'; import { ViewUnifiedChange } from '../models/view-unified-change';
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service'; import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
/** /**
* Type declaring which strings are valid options for metainfos to be exported into a pdf * Type declaring which strings are valid options for metainfos to be exported into a pdf
*/ */
export type InfoToExport = 'submitters' | 'state' | 'recommendation' | 'category' | 'block' | 'origin' | 'polls'; export type InfoToExport =
| 'submitters'
| 'state'
| 'recommendation'
| 'category'
| 'block'
| 'origin'
| 'polls'
| 'id'
| 'allcomments';
/** /**
* Converts a motion to pdf. Can be used from the motion detail view or executed on a list of motions * Converts a motion to pdf. Can be used from the motion detail view or executed on a list of motions
@ -43,6 +53,7 @@ export class MotionPdfService {
* @param htmlToPdfService To convert HTML text into pdfmake doc def * @param htmlToPdfService To convert HTML text into pdfmake doc def
* @param pollService MotionPollService for rendering the polls * @param pollService MotionPollService for rendering the polls
* @param linenumberingService Line numbers * @param linenumberingService Line numbers
* @param commentRepo MotionCommentSectionRepositoryService to print comments
*/ */
public constructor( public constructor(
private translate: TranslateService, private translate: TranslateService,
@ -52,7 +63,8 @@ export class MotionPdfService {
private configService: ConfigService, private configService: ConfigService,
private htmlToPdfService: HtmlToPdfService, private htmlToPdfService: HtmlToPdfService,
private pollService: MotionPollService, private pollService: MotionPollService,
private linenumberingService: LinenumberingService private linenumberingService: LinenumberingService,
private commentRepo: MotionCommentSectionRepositoryService
) {} ) {}
/** /**
@ -63,6 +75,7 @@ export class MotionPdfService {
* @param crMode determine the used change Recommendation mode * @param crMode determine the used change Recommendation mode
* @param contentToExport determine which content is to export. If left out, everything will be exported * @param contentToExport determine which content is to export. If left out, everything will be exported
* @param infoToExport determine which metaInfo to export. If left out, everything will be exported. * @param infoToExport determine which metaInfo to export. If left out, everything will be exported.
* @param commentsToExport comments to chose for export. If 'allcomments' is set in infoToExport, this selection will be ignored and all comments exported
* @returns doc def for the motion * @returns doc def for the motion
*/ */
public motionToDocDef( public motionToDocDef(
@ -70,7 +83,8 @@ export class MotionPdfService {
lnMode?: LineNumberingMode, lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode, crMode?: ChangeRecoMode,
contentToExport?: string[], contentToExport?: string[],
infoToExport?: InfoToExport[] infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object { ): object {
let motionPdfContent = []; let motionPdfContent = [];
@ -85,7 +99,8 @@ export class MotionPdfService {
} }
const title = this.createTitle(motion); const title = this.createTitle(motion);
const subtitle = this.createSubtitle(motion); const sequential = !infoToExport || infoToExport.includes('id');
const subtitle = this.createSubtitle(motion, sequential);
motionPdfContent = [title, subtitle]; motionPdfContent = [title, subtitle];
@ -106,6 +121,13 @@ export class MotionPdfService {
motionPdfContent.push(reason); motionPdfContent.push(reason);
} }
if (infoToExport && infoToExport.includes('allcomments')) {
commentsToExport = this.commentRepo.getViewModelList().map(vm => vm.id);
}
if (commentsToExport) {
motionPdfContent.push(this.createComments(motion, commentsToExport));
}
return motionPdfContent; return motionPdfContent;
} }
@ -129,18 +151,17 @@ export class MotionPdfService {
* Create the motion subtitle and sequential number part of the doc definition * Create the motion subtitle and sequential number part of the doc definition
* *
* @param motion the target motion * @param motion the target motion
* @param sequential set to true to include the sequential number
* @returns doc def for the subtitle * @returns doc def for the subtitle
*/ */
private createSubtitle(motion: ViewMotion): object { private createSubtitle(motion: ViewMotion, sequential?: boolean): object {
const subtitleLines = []; const subtitleLines = [];
const exportSequentialNumber = this.configService.instant('motions_export_sequential_number'); if (sequential) {
if (exportSequentialNumber) {
subtitleLines.push(`${this.translate.instant('Sequential number')}: ${motion.id}`); subtitleLines.push(`${this.translate.instant('Sequential number')}: ${motion.id}`);
} }
if (motion.parent_id) { if (motion.parent_id) {
if (exportSequentialNumber) { if (sequential) {
subtitleLines.push(' • '); subtitleLines.push(' • ');
} }
subtitleLines.push( subtitleLines.push(
@ -257,6 +278,7 @@ export class MotionPdfService {
]); ]);
} }
// voting results
if (motion.motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) { if (motion.motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) {
const column1 = []; const column1 = [];
const column2 = []; const column2 = [];
@ -634,4 +656,19 @@ export class MotionPdfService {
}; };
return [title, subtitle, metaInfo, subHeading, noteContent]; return [title, subtitle, metaInfo, subHeading, noteContent];
} }
private createComments(motion: ViewMotion, comments: number[]): object[] {
const result: object[] = [];
for (const comment of comments) {
const viewComment = this.commentRepo.getViewModel(comment);
const section = motion.getCommentForSection(viewComment);
if (section && section.comment) {
result.push({ text: viewComment.name, style: 'heading3' });
result.push({
text: this.htmlToPdfService.convertHtml(section.comment)
});
}
}
return result;
}
} }

View File

@ -368,13 +368,3 @@ def get_config_variables():
group="Motions", group="Motions",
subgroup="Export", subgroup="Export",
) )
yield ConfigVariable(
name="motions_export_sequential_number",
default_value=True,
input_type="boolean",
label="Include the sequential number in PDF and DOCX",
weight=380,
group="Motions",
subgroup="Export",
)