Add CSV import for supporters

Supporters can now be imported via csv
this was forgotten before
This commit is contained in:
Sean 2021-07-27 15:18:45 +02:00
parent 759e23e15b
commit 802ac1aee8
5 changed files with 118 additions and 32 deletions

View File

@ -32,6 +32,11 @@ export class ImportCreateMotion extends CreateMotion {
*/
public csvSubmitters: CsvMapping[];
/**
* Mapping for new/existing supporters.
*/
public csvSupporters: CsvMapping[];
/**
* Mapping for new/existing tags.
*/
@ -115,6 +120,30 @@ export class ImportCreateMotion extends CreateMotion {
return open;
}
public solveSupporters(supporters: CsvMapping[]): number {
let open = 0;
const ids: number[] = [];
this.csvSupporters.forEach(csvSupporter => {
if (csvSupporter.id) {
ids.push(csvSupporter.id);
return;
}
if (!supporters.length) {
open += 1;
return;
}
const mapped = supporters.find(newSupporter => newSupporter.name === csvSupporter.name);
if (mapped) {
csvSupporter.id = mapped.id;
ids.push(mapped.id);
} else {
open += 1;
}
});
this.supporters_id = ids;
return open;
}
/**
* Function to iterate over the found tags.
*

View File

@ -18,8 +18,7 @@
<br />
<div class="code red-warning-text">
<span *ngFor="let header of this.expectedHeader; let last = last">
<span>{{ header | translate }}</span
><span *ngIf="!last">,&nbsp;</span>
{{ header | translate }}<span *ngIf="!last">,&nbsp;</span>
</span>
</div>
<ul>
@ -41,11 +40,7 @@
<div class="wrapper">
<mat-form-field>
<mat-label>{{ 'Encoding of the file' | translate }}</mat-label>
<mat-select
class="selection"
(selectionChange)="selectEncoding($event)"
[value]="encodings[0].value"
>
<mat-select class="selection" (selectionChange)="selectEncoding($event)" [value]="encodings[0].value">
<mat-option *ngFor="let option of encodings" [value]="option.value">
{{ option.label | translate }}
</mat-option>
@ -220,6 +215,27 @@
</mat-cell>
</ng-container>
<!-- supporters column -->
<ng-container matColumnDef="supporters">
<mat-header-cell *matHeaderCellDef>{{ 'Supporters' | translate }}</mat-header-cell>
<mat-cell *matCellDef="let entry">
<div *ngIf="entry.newEntry?.csvSupporters?.length">
<mat-icon
color="warn"
*ngIf="hasError(entry, 'Supporters')"
matTooltip="{{ getVerboseError('Supporters') | translate }}"
>
warning
</mat-icon>
<div *ngFor="let supporters of entry.newEntry.csvSupporters">
{{ supporters.name }}
<mat-icon class="newBadge" color="accent" inline *ngIf="!supporters.id">add</mat-icon>
&nbsp;
</div>
</div>
</mat-cell>
</ng-container>
<!-- category column -->
<ng-container matColumnDef="category">
<mat-header-cell *matHeaderCellDef>{{ 'Category' | translate }}</mat-header-cell>

View File

@ -175,6 +175,7 @@ export class MotionCsvExportService {
const headerRow = [
'Identifier',
'Submitters',
'Supporters',
'Title',
'Text',
'Reason',
@ -187,6 +188,7 @@ export class MotionCsvExportService {
[
'A1',
'Submitter A',
'Supporter A',
'Title 1',
'Text 1',
'Reason 1',
@ -195,8 +197,19 @@ export class MotionCsvExportService {
'Block A',
'Last Year Conference A'
],
['B1', 'Submitter B', 'Title 2', 'Text 2', 'Reason 2', 'Category B', null, 'Block A', 'Origin B'],
['C2', null, 'Title 3', 'Text 3', null, null, null, null, null]
[
'B1',
'Submitter B',
'Supporter B',
'Title 2',
'Text 2',
'Reason 2',
'Category B',
null,
'Block A',
'Origin B'
],
['C2', null, null, 'Title 3', 'Text 3', null, null, null, null, null]
];
this.csvExport.dummyCSVExport(headerRow, rows, `${this.translate.instant('motions-example')}.csv`);
}

View File

@ -48,6 +48,11 @@ export class MotionImportService extends BaseImportService<Motion> {
*/
public newSubmitters: CsvMapping[] = [];
/**
* supporters that need to be created prior to importing
*/
public newSupporters: CsvMapping[] = [];
/**
* Categories that need to be created prior to importing
*/
@ -92,6 +97,7 @@ export class MotionImportService extends BaseImportService<Motion> {
*/
public clearData(): void {
this.newSubmitters = [];
this.newSupporters = [];
this.newCategories = [];
this.newMotionBlocks = [];
this.newTags = [];
@ -110,7 +116,10 @@ export class MotionImportService extends BaseImportService<Motion> {
for (let idx = 0; idx < headerLength; idx++) {
switch (this.expectedHeader[idx]) {
case 'submitters':
newEntry.csvSubmitters = this.getSubmitters(line[idx]);
newEntry.csvSubmitters = this.getUsers(line[idx], 'submitter');
break;
case 'supporters':
newEntry.csvSupporters = this.getUsers(line[idx], 'supporter');
break;
case 'category':
newEntry.csvCategory = this.getCategory(line[idx]);
@ -150,7 +159,8 @@ export class MotionImportService extends BaseImportService<Motion> {
public async doImport(): Promise<void> {
this.newMotionBlocks = await this.createNewMotionBlocks();
this.newCategories = await this.createNewCategories();
this.newSubmitters = await this.createNewUsers();
this.newSubmitters = await this.createNewUsers(this.newSubmitters);
this.newSupporters = await this.createNewUsers(this.newSupporters);
this.newTags = await this.createNewTags();
for (const entry of this.entries) {
@ -169,12 +179,18 @@ export class MotionImportService extends BaseImportService<Motion> {
this.updatePreview();
continue;
}
const openUsers = (entry.newEntry as ImportCreateMotion).solveSubmitters(this.newSubmitters);
if (openUsers) {
const openSubmitters = (entry.newEntry as ImportCreateMotion).solveSubmitters(this.newSubmitters);
if (openSubmitters) {
this.setError(entry, 'Submitters');
this.updatePreview();
continue;
}
const openSupporters = (entry.newEntry as ImportCreateMotion).solveSupporters(this.newSupporters);
if (openSupporters) {
this.setError(entry, 'Supporters');
this.updatePreview();
continue;
}
const openTags = (entry.newEntry as ImportCreateMotion).solveTags(this.newTags);
if (openTags) {
this.setError(entry, 'Tags');
@ -191,39 +207,51 @@ export class MotionImportService extends BaseImportService<Motion> {
* Checks the provided submitter(s) and returns an object with mapping of
* existing users and of users that need to be created
*
* @param submitterlist
* @param userList
* @returns a list of submitters mapped with (if already existing) their id
*/
public getSubmitters(submitterlist: string): CsvMapping[] {
public getUsers(userList: string, kind: 'submitter' | 'supporter'): CsvMapping[] {
console.log('kind: ', kind);
const result: CsvMapping[] = [];
if (!submitterlist) {
if (!userList) {
return result;
}
const submitterArray = submitterlist.split(','); // TODO fails with 'full name'
for (const submitter of submitterArray) {
const existingSubmitters = this.userRepo.getUsersByName(submitter.trim());
if (!existingSubmitters.length) {
if (!this.newSubmitters.find(listedSubmitter => listedSubmitter.name === submitter)) {
this.newSubmitters.push({ name: submitter });
const userArray = userList.split(','); // TODO fails with 'full name'
console.log('userArray: ', userList);
for (const user of userArray) {
const existingUsers = this.userRepo.getUsersByName(user.trim());
if (!existingUsers.length) {
if (kind === 'submitter') {
if (!this.newSubmitters.find(listedSubmitter => listedSubmitter.name === user)) {
this.newSubmitters.push({ name: user });
}
result.push({ name: user });
} else if (kind === 'supporter') {
if (!this.newSupporters.find(listedSupporter => listedSupporter.name === user)) {
this.newSupporters.push({ name: user });
}
result.push({ name: user });
}
result.push({ name: submitter });
}
if (existingSubmitters.length === 1) {
if (existingUsers.length === 1) {
result.push({
name: existingSubmitters[0].short_name,
id: existingSubmitters[0].id
name: existingUsers[0].short_name,
id: existingUsers[0].id
});
}
if (existingSubmitters.length > 1) {
if (existingUsers.length > 1) {
result.push({
name: submitter,
multiId: existingSubmitters.map(ex => ex.id)
name: user,
multiId: existingUsers.map(ex => ex.id)
});
this.matSnackbar.open('TODO: multiple possible users found for this string', 'ok');
// TODO How to handle several submitters ? Is this possible?
// should have some kind of choice dialog there
}
}
console.log('res: ', result);
return result;
}
@ -331,9 +359,9 @@ export class MotionImportService extends BaseImportService<Motion> {
*
* @returns a promise with list of new Submitters, updated with newly created ids
*/
private async createNewUsers(): Promise<CsvMapping[]> {
private async createNewUsers(list: CsvMapping[]): Promise<CsvMapping[]> {
const promises: Promise<CsvMapping>[] = [];
for (const user of this.newSubmitters) {
for (const user of list) {
promises.push(this.userRepo.createFromString(user.name));
}
return await Promise.all(promises);

View File

@ -700,7 +700,7 @@ button.mat-menu-item.selected {
.first-column {
flex: 1;
min-width: 0px;
min-width: 40px;
}
.filter-imports {