Merge pull request #4210 from MaximilianKrambach/agendalistpdf

agenda pdf export
This commit is contained in:
Sean 2019-01-30 16:25:54 +01:00 committed by GitHub
commit d705b2a137
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 5 deletions

View File

@ -468,6 +468,14 @@ export class PdfDocumentService {
tableHeader: {
bold: true,
fillColor: 'white'
},
listParent: {
fontSize: 14,
margin: [0, 5]
},
listChild: {
fontSize: 12,
margin: [0, 5]
}
};
}

View File

@ -110,10 +110,17 @@
<span translate>Sort</span>
</button>
</div>
<!-- csv export -->
<button mat-menu-item (click)="csvExportItemList()">
<mat-icon>archive</mat-icon>
<span translate>Export as CSV</span>
</button>
<!-- PDF export -->
<button mat-menu-item (click)="onDownloadPdf()">
<mat-icon>picture_as_pdf</mat-icon>
<span translate>Export as PDF</span>
</button>
<!-- Import -->
<button mat-menu-item *osPerms="'agenda.can_manage'" routerLink="import">
<mat-icon>save_alt</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>

View File

@ -11,9 +11,11 @@ import { PromptService } from '../../../../core/services/prompt.service';
import { ViewItem } from '../../models/view-item';
import { AgendaCsvExportService } from '../../services/agenda-csv-export.service';
import { AgendaPdfService } from '../../services/agenda-pdf.service';
import { ConfigService } from 'app/core/services/config.service';
import { DurationService } from 'app/core/services/duration.service';
import { ItemInfoDialogComponent } from '../item-info-dialog/item-info-dialog.component';
import { PdfDocumentService } from 'app/core/services/pdf-document.service';
import { ViewportService } from 'app/core/services/viewport.service';
/**
@ -52,6 +54,8 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
* @param durationService Converts numbers to readable duration strings
* @param csvExport Handles the exporting into csv
* @param filterService: service for filtering data
* @param agendaPdfService: service for preparing a pdf of the agenda
* @param pdfService: Service for exporting a pdf
*/
public constructor(
titleService: Title,
@ -66,7 +70,9 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
public vp: ViewportService,
public durationService: DurationService,
private csvExport: AgendaCsvExportService,
public filterService: AgendaFilterListService
public filterService: AgendaFilterListService,
private agendaPdfService: AgendaPdfService,
private pdfService: PdfDocumentService
) {
super(titleService, translate, matSnackBar);
@ -227,4 +233,13 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
public csvExportItemList(): void {
this.csvExport.exportItemList(this.dataSource.filteredData);
}
/**
* Triggers the export of the agenda. Currently filtered items and 'hidden'
* items will not be exported
*/
public onDownloadPdf(): void {
const filename = this.translate.instant('Agenda');
this.pdfService.download(this.agendaPdfService.agendaListToDocDef(this.dataSource.filteredData), filename);
}
}

View File

@ -67,10 +67,7 @@ export class ViewItem extends BaseViewModel {
}
public get verboseCsvType(): string {
if (this.item) {
return this.item.verboseCsvType;
}
return '';
return this.item ? this.item.verboseCsvType : '';
}
/**

View File

@ -0,0 +1,17 @@
import { TestBed } from '@angular/core/testing';
import { AgendaPdfService } from './agenda-pdf.service';
import { E2EImportsModule } from 'e2e-imports.module';
describe('AgendaPdfService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [E2EImportsModule]
})
);
it('should be created', () => {
const service: AgendaPdfService = TestBed.get(AgendaPdfService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,103 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { TreeService, OSTreeNode } from 'app/core/services/tree.service';
import { ViewItem } from '../models/view-item';
/**
* pdfMake structure for a content line in the pdf document.
*/
interface AgendaTreePdfEntry {
style: string;
columns: { width?: number; text: string }[];
}
/**
* Converts a list of agenda items to pdf, indenting according to a hierarchy tree.
* Provides the public method `agendaListToDocDef(items: ViewItems[])` which should be convenient to use.
*
* @example
* ```ts
* const pdfMakeCompatibleDocDef = this.AgendaPdfService.agendaListToDocDef(this.dataSource.data);
* ```
*/
@Injectable({
providedIn: 'root'
})
export class AgendaPdfService {
/**
* Constructor
*
* @param translate handle translations
* @param treeService create hierarchy between items
*/
public constructor(private translate: TranslateService, private treeService: TreeService) {}
/**
* Creates pdfMake definitions for a agenda list pdf from the given agenda items
*
* @param items A list of viewItems to be included in this agenda list. Items with the property 'hidden'
* will be ignored, all other items will be sorted by their parents and weight
* @returns definitions ready to be opened or exported via {@link PdfDocumentService}
*/
public agendaListToDocDef(items: ViewItem[]): object {
const tree: OSTreeNode<ViewItem>[] = this.treeService.makeTree(items, 'weight', 'parent_id');
const title = {
text: this.translate.instant('Agenda'),
style: 'title'
};
const entries = this.createEntries(tree);
return [title, entries];
}
/**
* Traverses the given nodeTree and creates an array of entries for all items
*
* @param tree
* @returns hierarchical pdfMake definitions for topic entries
*/
private createEntries(tree: OSTreeNode<ViewItem>[]): AgendaTreePdfEntry[] {
const content: AgendaTreePdfEntry[] = [];
tree.forEach(treeitem => content.push(...this.parseItem(treeitem, 0)));
return content;
}
/**
* Parses an entry line and triggers parsing of any children
* (TODO: Check assumption: items with 'is_hidden' are not to be exported)
*
* @param nodeItem the item for the head line
* @param level: The hierarchy index (beginning at 0 for top level agenda topics)
* @returns pdfMake definitions for the number/title strings, indented according to hierarchy
*/
private parseItem(nodeItem: OSTreeNode<ViewItem>, level: number): AgendaTreePdfEntry[] {
const itemList: AgendaTreePdfEntry[] = [];
if (!nodeItem.item.item.is_hidden) {
// don't include hidden items and their subitems
const resultString: AgendaTreePdfEntry = {
style: level ? 'listChild' : 'listParent',
columns: [
{
width: level * 15,
text: ''
},
{
width: 60,
text: nodeItem.item.itemNumber
},
{
text: nodeItem.item.getTitle()
}
]
};
itemList.push(resultString);
if (nodeItem.children && nodeItem.children.length) {
nodeItem.children.forEach(child => {
itemList.push(...this.parseItem(child, level + 1));
});
}
}
return itemList;
}
}