Merge pull request #4766 from GabrielInTheWorld/pdf-make-enhancement

Implementation of exporting submitters and recommendation
This commit is contained in:
Emanuel Schütze 2019-06-14 14:17:13 +02:00 committed by GitHub
commit e2e5fe07d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 44 deletions

View File

@ -151,9 +151,11 @@ _('Number of all delegates');
_('Number of all participants'); _('Number of all participants');
_('Use the following custom number'); _('Use the following custom number');
_('Custom number of ballot papers'); _('Custom number of ballot papers');
// subgroup export // subgroup PDF export
_('Title for PDF and DOCX documents (all motions)'); _('PDF export');
_('Preamble text for PDF and DOCX documents (all motions)'); _('Title for PDF documents of motions');
_('Preamble text for PDF documents of motions');
_('Show submitters and recommendation in table of contents');
_('Sort categories by'); _('Sort categories by');
_('Sort motions by'); _('Sort motions by');
_('Include the sequential number in PDF and DOCX'); _('Include the sequential number in PDF and DOCX');

View File

@ -13,9 +13,20 @@ import { HttpService } from '../core-services/http.service';
*/ */
export enum StyleType { export enum StyleType {
DEFAULT = 'tocEntry', DEFAULT = 'tocEntry',
SUBTITLE = 'subtitle',
SUB_ENTRY = 'tocSubEntry',
CATEGORY_SECTION = 'tocCategorySection' CATEGORY_SECTION = 'tocCategorySection'
} }
/**
* Enumeration to describe the type of borders.
*/
export enum BorderType {
DEFAULT = 'noBorders',
LIGHT_HORIZONTAL_LINES = 'lightHorizontalLines',
HEADER_ONLY = 'headerLineOnly'
}
/** /**
* Custom PDF error class to handle errors in a safer way * Custom PDF error class to handle errors in a safer way
*/ */
@ -445,9 +456,10 @@ export class PdfDocumentService {
* @returns an object that contains all pdf styles * @returns an object that contains all pdf styles
*/ */
private getStandardPaperStyles(): object { private getStandardPaperStyles(): object {
const pageSize = this.configService.instant('general_export_pdf_pagesize');
return { return {
title: { title: {
fontSize: 18, fontSize: pageSize === 'A5' ? 14 : 16,
margin: [0, 0, 0, 20], margin: [0, 0, 0, 20],
bold: true bold: true
}, },
@ -475,12 +487,12 @@ export class PdfDocumentService {
fontSize: 8 fontSize: 8
}, },
heading2: { heading2: {
fontSize: 14, fontSize: pageSize === 'A5' ? 12 : 14,
margin: [0, 0, 0, 10], margin: [0, 0, 0, 10],
bold: true bold: true
}, },
heading3: { heading3: {
fontSize: 12, fontSize: pageSize === 'A5' ? 10 : 12,
margin: [0, 10, 0, 0], margin: [0, 10, 0, 0],
bold: true bold: true
}, },
@ -498,17 +510,25 @@ export class PdfDocumentService {
margin: [15, 5] margin: [15, 5]
}, },
tocEntry: { tocEntry: {
fontSize: 12, fontSize: pageSize === 'A5' ? 10 : 11,
margin: [0, 0, 0, 0], margin: [0, 0, 0, 0],
bold: false bold: false
}, },
tocHeaderRow: {
fontSize: 8,
italics: true
},
tocSubEntry: {
fontSize: pageSize === 'A5' ? 9 : 10,
color: '#404040'
},
tocCategoryEntry: { tocCategoryEntry: {
fontSize: 12, fontSize: pageSize === 'A5' ? 10 : 11,
margin: [10, 0, 0, 0], margin: [10, 0, 0, 0],
bold: false bold: false
}, },
tocCategoryTitle: { tocCategoryTitle: {
fontSize: 12, fontSize: pageSize === 'A5' ? 10 : 11,
margin: [0, 0, 0, 4], margin: [0, 0, 0, 4],
bold: true bold: true
}, },
@ -610,6 +630,7 @@ export class PdfDocumentService {
*/ */
public createTitle(configVariable: string): object { public createTitle(configVariable: string): object {
const titleText = this.translate.instant(this.configService.instant<string>(configVariable)); const titleText = this.translate.instant(this.configService.instant<string>(configVariable));
return { return {
text: titleText, text: titleText,
style: 'title' style: 'title'
@ -647,13 +668,21 @@ export class PdfDocumentService {
* @param tocBody the body of the table * @param tocBody the body of the table
* @returns The table of contents as doc definition * @returns The table of contents as doc definition
*/ */
public createTocTableDef(tocBody: object, style: StyleType = StyleType.DEFAULT): object { public createTocTableDef(
tocBody: object[],
style: StyleType = StyleType.DEFAULT,
borderStyle: BorderType = BorderType.DEFAULT,
...header: object[]
): object {
return { return {
table: { table: {
headerRows: header[0] ? header.length : 0,
keepWithHeaderRows: header[0] ? header.length : 0,
dontBreakRows: true,
widths: ['auto', '*', 'auto'], widths: ['auto', '*', 'auto'],
body: tocBody body: header[0] ? [...header, ...tocBody] : tocBody
}, },
layout: 'noBorders', layout: borderStyle,
style: style style: style
}; };
} }
@ -672,15 +701,16 @@ export class PdfDocumentService {
identifier: string, identifier: string,
title: string, title: string,
pageReference: string, pageReference: string,
style: StyleType = StyleType.DEFAULT style: StyleType = StyleType.DEFAULT,
): Object { ...subTitle: object[]
): Array<Object> {
return [ return [
{ {
text: identifier, text: identifier,
style: style style: style
}, },
{ {
text: title, text: [title, ...subTitle],
style: 'tocEntry' style: 'tocEntry'
}, },
{ {
@ -690,4 +720,19 @@ export class PdfDocumentService {
} }
]; ];
} }
/**
* Function to create an inline line in the toc.
*
* @param text The text for the line.
* @param bold Optional boolean, if the text should be bold - defaults to `false`.
*
* @returns {Object} An object for `DocDefinition` for `pdf-make`.
*/
public createTocLineInline(text: string): Object {
return {
text: '\n' + text,
style: StyleType.SUB_ENTRY
};
}
} }

View File

@ -6,7 +6,8 @@ import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-mo
import { MotionPdfService, InfoToExport } from './motion-pdf.service'; import { MotionPdfService, InfoToExport } from './motion-pdf.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { ViewCategory } from '../models/view-category'; import { ViewCategory } from '../models/view-category';
import { PdfError, PdfDocumentService, StyleType } from 'app/core/ui-services/pdf-document.service'; import { PdfError, PdfDocumentService, StyleType, BorderType } from 'app/core/ui-services/pdf-document.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
/** /**
* Service to export a list of motions. * Service to export a list of motions.
@ -31,7 +32,8 @@ export class MotionPdfCatalogService {
private translate: TranslateService, private translate: TranslateService,
private configService: ConfigService, private configService: ConfigService,
private motionPdfService: MotionPdfService, private motionPdfService: MotionPdfService,
private pdfService: PdfDocumentService private pdfService: PdfDocumentService,
private motionRepo: MotionRepositoryService
) {} ) {}
/** /**
@ -116,13 +118,15 @@ export class MotionPdfCatalogService {
text: this.translate.instant('Table of contents'), text: this.translate.instant('Table of contents'),
style: 'heading2' style: 'heading2'
}; };
const exportSubmitterRecommendation = this.configService.instant<boolean>(
'motions_export_submitter_recommendation'
);
if (!sorting) { // Initialize the header and the layout for border-style.
sorting = this.configService.instant<string>('motions_export_category_sorting'); const header = exportSubmitterRecommendation ? this.getTocHeaderDefinition() : undefined;
} const layout = exportSubmitterRecommendation ? BorderType.LIGHT_HORIZONTAL_LINES : BorderType.DEFAULT;
const exportCategory = sorting === 'identifier' || sorting === 'prefix';
if (exportCategory && categories) { if (categories && categories.length) {
const catTocBody = []; const catTocBody = [];
for (const category of categories) { for (const category of categories) {
// push the name of the category // push the name of the category
@ -138,27 +142,38 @@ export class MotionPdfCatalogService {
] ]
] ]
}, },
layout: 'noBorders' layout: exportSubmitterRecommendation ? 'lightHorizontalLines' : 'noBorders'
}); });
const tocBody = motions const tocBody = [];
.filter(motion => category === motion.category) for (const motion of motions.filter(motionIn => category === motionIn.category)) {
.map(motion => if (exportSubmitterRecommendation) {
tocBody.push(this.appendSubmittersAndRecommendation(motion, StyleType.CATEGORY_SECTION));
} else {
tocBody.push(
this.pdfService.createTocLine( this.pdfService.createTocLine(
`${motion.identifier}`, `${motion.identifier ? motion.identifier : ''}`,
motion.title, motion.title,
`${motion.id}`, `${motion.id}`,
StyleType.CATEGORY_SECTION StyleType.CATEGORY_SECTION
) )
); );
}
}
catTocBody.push(this.pdfService.createTocTableDef(tocBody, StyleType.CATEGORY_SECTION)); catTocBody.push(this.pdfService.createTocTableDef(tocBody, StyleType.CATEGORY_SECTION, layout, header));
} }
// handle those without category // handle those without category
const uncatTocBody = motions const uncatTocBody = motions
.filter(motion => !motion.category) .filter(motion => !motion.category)
.map(motion => this.pdfService.createTocLine(`${motion.identifier}`, motion.title, `${motion.id}`)); .map(motion =>
this.pdfService.createTocLine(
`${motion.identifier ? motion.identifier : ''}`,
motion.title,
`${motion.id}`
)
);
// only push this array if there is at least one entry // only push this array if there is at least one entry
if (uncatTocBody.length > 0) { if (uncatTocBody.length > 0) {
@ -168,15 +183,46 @@ export class MotionPdfCatalogService {
toc.push(catTocBody); toc.push(catTocBody);
} else { } else {
// all motions in the same table // all motions in the same table
const tocBody = motions.map(motion => const tocBody = [];
this.pdfService.createTocLine(`${motion.identifier}`, motion.title, `${motion.id}`) for (const motion of motions) {
if (exportSubmitterRecommendation) {
tocBody.push(this.appendSubmittersAndRecommendation(motion));
} else {
tocBody.push(
this.pdfService.createTocLine(
`${motion.identifier ? motion.identifier : ''}`,
motion.title,
`${motion.id}`
)
); );
toc.push(this.pdfService.createTocTableDef(tocBody, StyleType.CATEGORY_SECTION)); }
}
toc.push(this.pdfService.createTocTableDef(tocBody, StyleType.CATEGORY_SECTION, layout, header));
} }
return [tocTitle, toc, this.pdfService.getPageBreak()]; return [tocTitle, toc, this.pdfService.getPageBreak()];
} }
/**
* Function to get the definition for the header
* for exporting motion-list as PDF. Needed, if submitters
* and recommendation should also be exported to the `Table of Contents`.
*
* @returns {object} The DocDefinition for `pdfmake.js`.
*/
private getTocHeaderDefinition(): object {
return [
{ text: this.translate.instant('Identifier'), style: 'tocHeaderRow' },
{
text: `${this.translate.instant('Title')} · ${this.translate.instant(
'Submitters'
)} · ${this.translate.instant('Recommendation')}`,
style: 'tocHeaderRow'
},
{ text: this.translate.instant('Page'), style: 'tocHeaderRow', alignment: 'right' }
];
}
/** /**
* Extract the used categories from the given motion list. * Extract the used categories from the given motion list.
* *
@ -198,4 +244,31 @@ export class MotionPdfCatalogService {
return categories; return categories;
} }
/**
* Creates lines for the `Table of contents` containing submitters and recommendation.
*
* @param motion The motion containing the information
* @param style Optional `StyleType`. Defaults to `tocEntry`.
*
* @returns {Array<Object>} An array containing the `DocDefinitions` for `pdf-make`.
*/
private appendSubmittersAndRecommendation(motion: ViewMotion, style: StyleType = StyleType.DEFAULT): Array<Object> {
const recommendation = this.motionRepo.getExtendedRecommendationLabel(motion);
let submitterList = '';
for (let i = 0; i < motion.submitters.length; ++i) {
submitterList +=
i !== motion.submitters.length - 1
? motion.submitters[i].getTitle() + ', '
: motion.submitters[i].getTitle();
}
return this.pdfService.createTocLine(
`${motion.identifier ? motion.identifier : ''}`,
motion.title,
`${motion.id}`,
style,
this.pdfService.createTocLineInline(submitterList),
this.pdfService.createTocLineInline(recommendation)
);
}
} }

View File

@ -344,22 +344,32 @@ def get_config_variables():
validators=(MinValueValidator(1),), validators=(MinValueValidator(1),),
) )
# PDF and DOCX export # PDF export
yield ConfigVariable( yield ConfigVariable(
name="motions_export_title", name="motions_export_title",
default_value="Motions", default_value="Motions",
label="Title for PDF and DOCX documents (all motions)", label="Title for PDF document of motions",
weight=370, weight=370,
group="Motions", group="Motions",
subgroup="Export", subgroup="PDF export",
) )
yield ConfigVariable( yield ConfigVariable(
name="motions_export_preamble", name="motions_export_preamble",
default_value="", default_value="",
label="Preamble text for PDF and DOCX documents (all motions)", label="Preamble text for PDF documents of motions",
weight=375, weight=375,
group="Motions", group="Motions",
subgroup="Export", subgroup="PDF export",
)
yield ConfigVariable(
name="motions_export_submitter_recommendation",
default_value=False,
label="Show submitters and recommendation in table of contents",
input_type="boolean",
weight=378,
group="Motions",
subgroup="PDF export",
) )