Merge pull request #4609 from MaximilianKrambach/exportImportOrder
sort import/export columns for motions
This commit is contained in:
commit
16ea66e9ed
@ -887,6 +887,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
}
|
}
|
||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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">, </span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li translate>
|
<li translate>
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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>
|
||||||
|
@ -52,7 +52,7 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
'category',
|
'category',
|
||||||
'origin',
|
'origin',
|
||||||
'tags',
|
'tags',
|
||||||
'block',
|
'motion_block',
|
||||||
'polls',
|
'polls',
|
||||||
'id'
|
'id'
|
||||||
];
|
];
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
23
client/src/app/site/motions/motion-import-export-order.ts
Normal file
23
client/src/app/site/motions/motion-import-export-order.ts
Normal 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'];
|
@ -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 = [
|
||||||
|
@ -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'
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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')}:`,
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user