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 '';
}
/**

View File

@ -61,7 +61,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
/**
* 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

View File

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

View File

@ -17,6 +17,22 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
templateUrl: './motion-import-list.component.html'
})
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
*

View File

@ -52,7 +52,7 @@
<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="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>
<span translate>Voting result</span>
</mat-button-toggle>

View File

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

View File

@ -223,7 +223,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion, Motio
result.comments
);
} 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') {
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,
CsvColumnDefinitionMap
} 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 { ViewMotion } from '../models/view-motion';
@ -35,10 +35,10 @@ export class MotionCsvExportService {
*
* @param motions Motions to export
* @param contentToExport content properties to export
* @param infoToExport meta info to export
*/
public exportMotionList(motions: ViewMotion[], contentToExport: string[], infoToExport: InfoToExport[]): void {
const propertyList = ['identifier', 'title'].concat(contentToExport, infoToExport);
public exportMotionList(motions: ViewMotion[], contentToExport: string[]): void {
// reorders the exported properties according to motionImportExportHeaderOrder
const propertyList = motionImportExportHeaderOrder.filter(property => contentToExport.includes(property));
const exportProperties: (
| CsvColumnDefinitionProperty<ViewMotion>
| CsvColumnDefinitionMap<ViewMotion>)[] = propertyList.map(option => {
@ -52,6 +52,11 @@ export class MotionCsvExportService {
label: 'state',
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 {
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 {
const headerRow = ['Identifier', 'Title', 'Text', 'Reason', 'Submitters', 'Category', 'Origin', 'Motion block'];
const rows = [

View File

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

View File

@ -22,7 +22,7 @@ export type InfoToExport =
| 'state'
| 'recommendation'
| 'category'
| 'block'
| 'motion_block'
| 'origin'
| 'tags'
| 'polls'
@ -274,7 +274,7 @@ export class MotionPdfService {
}
// motion block
if (motion.motion_block && (!infoToExport || infoToExport.includes('block'))) {
if (motion.motion_block && (!infoToExport || infoToExport.includes('motion_block'))) {
metaTableBody.push([
{
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 { InfoToExport } from './motion-pdf.service';
import { motionImportExportHeaderOrder } from '../motion-import-export-order';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { TranslateService } from '@ngx-translate/core';
import { ViewMotion } from '../models/view-motion';
@ -51,7 +52,9 @@ export class MotionXlsxExportService {
*/
public exportMotionList(motions: ViewMotion[], infoToExport: InfoToExport[]): void {
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'), {
pageSetup: {
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 => {
const propertyHeader =
property === 'motion_block'
? 'Motion block'
: property.charAt(0).toLocaleUpperCase() + property.slice(1);
return {
header: this.translate.instant(property.charAt(0).toLocaleUpperCase() + property.slice(1))
header: this.translate.instant(propertyHeader)
};
});