Merge pull request #4609 from MaximilianKrambach/exportImportOrder

sort import/export columns for motions
This commit is contained in:
Sean 2019-04-18 11:01:01 +02:00 committed by GitHub
commit 16ea66e9ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 73 additions and 32 deletions

View File

@ -887,6 +887,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
} }
return rec; return rec;
} }
return '';
} }
/** /**

View File

@ -61,7 +61,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
/** /**
* The headers expected in the CSV matching import properties (in order) * The headers expected in the CSV matching import properties (in order)
*/ */
public expectedHeader: (string)[]; public expectedHeader: string[];
/** /**
* The minimimal number of header entries needed to successfully create an entry * The minimimal number of header entries needed to successfully create an entry

View File

@ -13,9 +13,10 @@
<span translate>Required comma or semicolon separated values with these column header names in the first row:</span> <span translate>Required comma or semicolon separated values with these column header names in the first row:</span>
<br /> <br />
<div class="code red-warning-text"> <div class="code red-warning-text">
<span translate>Identifier</span>, <span translate>Title</span>, <span translate>Text</span>, <span *ngFor="let header of this.expectedHeader; let last = last">
<span translate>Reason</span>, <span translate>Submitter</span>, <span translate>Category</span>, <span translate>{{ header }}</span
<span translate>Origin</span>, <span translate>Motion block</span> ><span *ngIf="!last">,&nbsp;</span>
</span>
</div> </div>
<ul> <ul>
<li translate> <li translate>

View File

@ -17,6 +17,22 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
templateUrl: './motion-import-list.component.html' templateUrl: './motion-import-list.component.html'
}) })
export class MotionImportListComponent extends BaseImportListComponent<ViewMotion> { export class MotionImportListComponent extends BaseImportListComponent<ViewMotion> {
/**
* Fetach a list of the headers expected by the importer, and prepare them
* to be translateable (upper case)
*
* @returns a list of strings matching the expected headers
*/
public get expectedHeader(): string[] {
return this.importer.expectedHeader.map(header => {
if (header === 'motion_block') {
return 'Motion block';
} else {
return header.charAt(0).toUpperCase() + header.slice(1);
}
});
}
/** /**
* Constructor for list view bases * Constructor for list view bases
* *

View File

@ -52,7 +52,7 @@
<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="tags"> <span translate>Tags</span> </mat-button-toggle> <mat-button-toggle value="tags"> <span translate>Tags</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="motion_block"> <span translate>Motion block</span> </mat-button-toggle>
<mat-button-toggle value="polls" #votingResultButton> <mat-button-toggle value="polls" #votingResultButton>
<span translate>Voting result</span> <span translate>Voting result</span>
</mat-button-toggle> </mat-button-toggle>

View File

@ -52,7 +52,7 @@ export class MotionExportDialogComponent implements OnInit {
'category', 'category',
'origin', 'origin',
'tags', 'tags',
'block', 'motion_block',
'polls', 'polls',
'id' 'id'
]; ];

View File

@ -223,7 +223,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion, Motio
result.comments result.comments
); );
} else if (result.format === 'csv') { } else if (result.format === 'csv') {
this.motionCsvExport.exportMotionList(data, result.content, result.metaInfo); const content = ['identifier', ...result.content, ...result.metaInfo];
this.motionCsvExport.exportMotionList(data, content);
} else if (result.format === 'xlsx') { } else if (result.format === 'xlsx') {
this.motionXlsxExport.exportMotionList(data, result.metaInfo); this.motionXlsxExport.exportMotionList(data, result.metaInfo);
} }

View File

@ -0,0 +1,23 @@
/**
* Defines the column order for csv/xlsx export/import of motions.
*/
export const motionImportExportHeaderOrder: string[] = [
'id',
'identifier',
'title',
'text',
'reason',
'submitters',
'category',
'origin',
'motion_block',
'tags',
'recommendation',
'state'
];
/**
* Subset of {@link motionImportExportHeaderOrder} properties that are
* restricted to export only due to database or workflow limitations
*/
export const motionExportOnly: string[] = ['id', 'recommendation', 'state', 'tags'];

View File

@ -7,7 +7,7 @@ import {
CsvColumnDefinitionProperty, CsvColumnDefinitionProperty,
CsvColumnDefinitionMap CsvColumnDefinitionMap
} from 'app/core/ui-services/csv-export.service'; } from 'app/core/ui-services/csv-export.service';
import { InfoToExport } from './motion-pdf.service'; import { motionImportExportHeaderOrder } from '../motion-import-export-order';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { ViewMotion } from '../models/view-motion'; import { ViewMotion } from '../models/view-motion';
@ -35,10 +35,10 @@ export class MotionCsvExportService {
* *
* @param motions Motions to export * @param motions Motions to export
* @param contentToExport content properties to export * @param contentToExport content properties to export
* @param infoToExport meta info to export
*/ */
public exportMotionList(motions: ViewMotion[], contentToExport: string[], infoToExport: InfoToExport[]): void { public exportMotionList(motions: ViewMotion[], contentToExport: string[]): void {
const propertyList = ['identifier', 'title'].concat(contentToExport, infoToExport); // reorders the exported properties according to motionImportExportHeaderOrder
const propertyList = motionImportExportHeaderOrder.filter(property => contentToExport.includes(property));
const exportProperties: ( const exportProperties: (
| CsvColumnDefinitionProperty<ViewMotion> | CsvColumnDefinitionProperty<ViewMotion>
| CsvColumnDefinitionMap<ViewMotion>)[] = propertyList.map(option => { | CsvColumnDefinitionMap<ViewMotion>)[] = propertyList.map(option => {
@ -52,6 +52,11 @@ export class MotionCsvExportService {
label: 'state', label: 'state',
map: motion => this.motionRepo.getExtendedStateLabel(motion) map: motion => this.motionRepo.getExtendedStateLabel(motion)
}; };
} else if (option === 'motion_block') {
return {
label: 'Motion block',
map: motion => (motion.motion_block ? motion.motion_block.getTitle() : '')
};
} else { } else {
return { property: option } as CsvColumnDefinitionProperty<ViewMotion>; return { property: option } as CsvColumnDefinitionProperty<ViewMotion>;
} }
@ -83,6 +88,7 @@ export class MotionCsvExportService {
); );
} }
// TODO does not reflect updated export order. any more. Hard coded for now
public exportDummyMotion(): void { public exportDummyMotion(): void {
const headerRow = ['Identifier', 'Title', 'Text', 'Reason', 'Submitters', 'Category', 'Origin', 'Motion block']; const headerRow = ['Identifier', 'Title', 'Text', 'Reason', 'Submitters', 'Category', 'Origin', 'Motion block'];
const rows = [ const rows = [

View File

@ -4,15 +4,16 @@ import { MatSnackBar } from '@angular/material';
import { Papa } from 'ngx-papaparse'; import { Papa } from 'ngx-papaparse';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
import { Category } from 'app/shared/models/motions/category'; import { Category } from 'app/shared/models/motions/category';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service'; import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { CreateMotion } from '../models/create-motion'; import { CreateMotion } from '../models/create-motion';
import { MotionBlock } from 'app/shared/models/motions/motion-block'; import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service'; import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { motionExportOnly, motionImportExportHeaderOrder } from '../motion-import-export-order';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
import { ViewCsvCreateMotion, CsvMapping } from '../models/view-csv-create-motion'; import { ViewCsvCreateMotion, CsvMapping } from '../models/view-csv-create-motion';
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
import { ViewMotion } from '../models/view-motion'; import { ViewMotion } from '../models/view-motion';
/** /**
@ -74,17 +75,7 @@ export class MotionImportService extends BaseImportService<ViewMotion> {
matSnackbar: MatSnackBar matSnackbar: MatSnackBar
) { ) {
super(translate, papa, matSnackbar); super(translate, papa, matSnackbar);
this.expectedHeader = motionImportExportHeaderOrder.filter(head => !motionExportOnly.includes(head));
this.expectedHeader = [
'identifier',
'title',
'text',
'reason',
'submitters',
'category',
'origin',
'motion_block'
];
} }
/** /**

View File

@ -22,7 +22,7 @@ export type InfoToExport =
| 'state' | 'state'
| 'recommendation' | 'recommendation'
| 'category' | 'category'
| 'block' | 'motion_block'
| 'origin' | 'origin'
| 'tags' | 'tags'
| 'polls' | 'polls'
@ -274,7 +274,7 @@ export class MotionPdfService {
} }
// motion block // motion block
if (motion.motion_block && (!infoToExport || infoToExport.includes('block'))) { if (motion.motion_block && (!infoToExport || infoToExport.includes('motion_block'))) {
metaTableBody.push([ metaTableBody.push([
{ {
text: `${this.translate.instant('Motion block')}:`, text: `${this.translate.instant('Motion block')}:`,

View File

@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { Workbook } from 'exceljs/dist/exceljs.min.js'; import { Workbook } from 'exceljs/dist/exceljs.min.js';
import { InfoToExport } from './motion-pdf.service'; import { InfoToExport } from './motion-pdf.service';
import { motionImportExportHeaderOrder } from '../motion-import-export-order';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ViewMotion } from '../models/view-motion'; import { ViewMotion } from '../models/view-motion';
@ -51,7 +52,9 @@ export class MotionXlsxExportService {
*/ */
public exportMotionList(motions: ViewMotion[], infoToExport: InfoToExport[]): void { public exportMotionList(motions: ViewMotion[], infoToExport: InfoToExport[]): void {
const workbook = new Workbook(); const workbook = new Workbook();
const properties = ['identifier', 'title'].concat(infoToExport); const propertyList = ['identifier', 'title'].concat(infoToExport);
// reorders the exported properties according to motionImportExportHeaderOrder
const properties = motionImportExportHeaderOrder.filter(property => propertyList.includes(property));
const worksheet = workbook.addWorksheet(this.translate.instant('Motions'), { const worksheet = workbook.addWorksheet(this.translate.instant('Motions'), {
pageSetup: { pageSetup: {
paperSize: 9, paperSize: 9,
@ -63,14 +66,13 @@ export class MotionXlsxExportService {
} }
}); });
// if the ID was exported as well, shift it to the first position
if (properties[properties.length - 1] === 'id') {
properties.unshift(properties.pop());
}
worksheet.columns = properties.map(property => { worksheet.columns = properties.map(property => {
const propertyHeader =
property === 'motion_block'
? 'Motion block'
: property.charAt(0).toLocaleUpperCase() + property.slice(1);
return { return {
header: this.translate.instant(property.charAt(0).toLocaleUpperCase() + property.slice(1)) header: this.translate.instant(propertyHeader)
}; };
}); });