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>
</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
mat-icon-button
*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 { ViewMotion } from '../../models/view-motion';
import { BaseViewComponent } from 'app/site/base/base-view';
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
/**
* Component for the motion comments view
@ -63,6 +64,7 @@ export class MotionCommentsComponent extends BaseViewComponent {
* @param commentRepo The repository that handles server communication
* @param formBuilder Form builder to handle text editing
* @param operator service to get the sections
* @param pdfService service to export a comment section to pdf
* @param titleService set the browser title
* @param translate the translation service
* @param matSnackBar showing errors and information
@ -71,6 +73,7 @@ export class MotionCommentsComponent extends BaseViewComponent {
private commentRepo: MotionCommentSectionRepositoryService,
private formBuilder: FormBuilder,
private operator: OperatorService,
private pdfService: MotionPdfExportService,
titleService: Title,
translate: TranslateService,
matSnackBar: MatSnackBar
@ -177,4 +180,13 @@ export class MotionCommentsComponent extends BaseViewComponent {
public isCommentEdited(section: ViewMotionCommentSection): boolean {
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="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="votingResult" #votingResultButton>
<mat-button-toggle value="poll" #votingResultButton>
<span translate>Voting result</span>
</mat-button-toggle>
<mat-button-toggle value="id"><span translate>Sequential number</span></mat-button-toggle>
</mat-button-toggle-group>
</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 />
</div>

View File

@ -4,6 +4,9 @@ import { MatDialogRef, MatButtonToggle } from '@angular/material';
import { ConfigService } from 'app/core/ui-services/config.service';
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.
@ -42,16 +45,23 @@ export class MotionExportDialogComponent implements OnInit {
/**
* Determine the default meta info to export.
*/
private defaultInfoToExport = [
private defaultInfoToExport: InfoToExport[] = [
'submitters',
'state',
'recommendation',
'category',
'origin',
'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.
*/
@ -82,11 +92,14 @@ export class MotionExportDialogComponent implements OnInit {
*
* @param formBuilder Creates the export form
* @param dialogRef Make the dialog available
* @param configService
* @param commentRepo
*/
public constructor(
public formBuilder: FormBuilder,
public dialogRef: MatDialogRef<MotionExportDialogComponent>,
public configService: ConfigService
public configService: ConfigService,
public commentRepo: MotionCommentSectionRepositoryService
) {
this.defaultLnMode = this.configService.instant('motions_default_line_numbering');
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').disable();
this.exportForm.get('comments').disable();
// 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
// just the normal motion text
@ -133,6 +148,7 @@ export class MotionExportDialogComponent implements OnInit {
// TODO: CSV Issues
// this.diffVersionButton.disabled = true;
} else if (value === 'pdf') {
this.exportForm.get('comments').enable();
this.exportForm.get('lnMode').enable();
this.exportForm.get('lnMode').setValue(this.defaultLnMode);
@ -157,7 +173,8 @@ export class MotionExportDialogComponent implements OnInit {
lnMode: [this.defaultLnMode],
crMode: [this.defaultCrMode],
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.crMode,
result.content,
result.metaInfo
result.metaInfo,
result.comments
);
} else if (result.format === 'csv') {
this.motionCsvExport.exportMotionList(

View File

@ -45,6 +45,11 @@ export class MotionPdfCatalogService {
* Public entry point to conversion of multiple motions
*
* @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
*/
public motionListToDocDef(
@ -52,7 +57,8 @@ export class MotionPdfCatalogService {
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[]
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object {
let doc = [];
const motionDocList = [];
@ -63,7 +69,8 @@ export class MotionPdfCatalogService {
lnMode,
crMode,
contentToExport,
infoToExport
infoToExport,
commentsToExport
);
// 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 { MotionPdfCatalogService } from './motion-pdf-catalog.service';
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.
@ -56,15 +57,24 @@ export class MotionPdfExportService {
* @param crMode Change Recommendation Mode
* @param contentToExport Determine to determine with text and/or reason
* @param infoToExport Determine the meta info to export
* @param commentsToExport Comments (by id) to export
*/
public exportMotionCatalog(
motions: ViewMotion[],
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[]
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): 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 metadata = {
title: filename
@ -101,4 +111,21 @@ export class MotionPdfExportService {
};
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 { ViewUnifiedChange } from '../models/view-unified-change';
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
*/
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
@ -43,6 +53,7 @@ export class MotionPdfService {
* @param htmlToPdfService To convert HTML text into pdfmake doc def
* @param pollService MotionPollService for rendering the polls
* @param linenumberingService Line numbers
* @param commentRepo MotionCommentSectionRepositoryService to print comments
*/
public constructor(
private translate: TranslateService,
@ -52,7 +63,8 @@ export class MotionPdfService {
private configService: ConfigService,
private htmlToPdfService: HtmlToPdfService,
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 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 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
*/
public motionToDocDef(
@ -70,7 +83,8 @@ export class MotionPdfService {
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[]
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object {
let motionPdfContent = [];
@ -85,7 +99,8 @@ export class MotionPdfService {
}
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];
@ -106,6 +121,13 @@ export class MotionPdfService {
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;
}
@ -129,18 +151,17 @@ export class MotionPdfService {
* Create the motion subtitle and sequential number part of the doc definition
*
* @param motion the target motion
* @param sequential set to true to include the sequential number
* @returns doc def for the subtitle
*/
private createSubtitle(motion: ViewMotion): object {
private createSubtitle(motion: ViewMotion, sequential?: boolean): object {
const subtitleLines = [];
const exportSequentialNumber = this.configService.instant('motions_export_sequential_number');
if (exportSequentialNumber) {
if (sequential) {
subtitleLines.push(`${this.translate.instant('Sequential number')}: ${motion.id}`);
}
if (motion.parent_id) {
if (exportSequentialNumber) {
if (sequential) {
subtitleLines.push(' • ');
}
subtitleLines.push(
@ -257,6 +278,7 @@ export class MotionPdfService {
]);
}
// voting results
if (motion.motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) {
const column1 = [];
const column2 = [];
@ -634,4 +656,19 @@ export class MotionPdfService {
};
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",
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",
)