Merge pull request #5104 from jsangmeister/csv-import-fix
Fixed CSV import
This commit is contained in:
commit
d286378524
@ -209,9 +209,9 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User, UserTi
|
|||||||
*
|
*
|
||||||
* @param newEntries
|
* @param newEntries
|
||||||
*/
|
*/
|
||||||
public async bulkCreate(newEntries: NewEntry<ViewUser>[]): Promise<number[]> {
|
public async bulkCreate(newEntries: NewEntry<User>[]): Promise<number[]> {
|
||||||
const data = newEntries.map(entry => {
|
const data = newEntries.map(entry => {
|
||||||
return { ...entry.newEntry.user, importTrackId: entry.importTrackId };
|
return { ...entry.newEntry, importTrackId: entry.importTrackId };
|
||||||
});
|
});
|
||||||
const response = (await this.httpService.post(`/rest/users/user/mass_import/`, { users: data })) as {
|
const response = (await this.httpService.post(`/rest/users/user/mass_import/`, { users: data })) as {
|
||||||
detail: string;
|
detail: string;
|
||||||
|
@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Papa, ParseConfig } from 'ngx-papaparse';
|
import { Papa, ParseConfig } from 'ngx-papaparse';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for value- Label combinations.
|
* Interface for value- Label combinations.
|
||||||
@ -53,7 +53,7 @@ type CsvImportStatus = 'new' | 'error' | 'done';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export abstract class BaseImportService<V extends BaseViewModel> {
|
export abstract class BaseImportService<M extends BaseModel> {
|
||||||
/**
|
/**
|
||||||
* List of possible errors and their verbose explanation
|
* List of possible errors and their verbose explanation
|
||||||
*/
|
*/
|
||||||
@ -127,12 +127,12 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
/**
|
/**
|
||||||
* the list of parsed models that have been extracted from the opened file
|
* the list of parsed models that have been extracted from the opened file
|
||||||
*/
|
*/
|
||||||
private _entries: NewEntry<V>[] = [];
|
private _entries: NewEntry<M>[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BehaviorSubject for displaying a preview for the currently selected entries
|
* BehaviorSubject for displaying a preview for the currently selected entries
|
||||||
*/
|
*/
|
||||||
public newEntries = new BehaviorSubject<NewEntry<V>[]>([]);
|
public newEntries = new BehaviorSubject<NewEntry<M>[]>([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits an error string to display if a file import cannot be done
|
* Emits an error string to display if a file import cannot be done
|
||||||
@ -159,7 +159,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
* Returns the current entries. For internal use in extending classes, as it
|
* Returns the current entries. For internal use in extending classes, as it
|
||||||
* might not be filled with data at all times (see {@link newEntries} for a BehaviorSubject)
|
* might not be filled with data at all times (see {@link newEntries} for a BehaviorSubject)
|
||||||
*/
|
*/
|
||||||
protected get entries(): NewEntry<V>[] {
|
protected get entries(): NewEntry<M>[] {
|
||||||
return this._entries;
|
return this._entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
*
|
*
|
||||||
* @param entries: an array of prepared newEntry objects
|
* @param entries: an array of prepared newEntry objects
|
||||||
*/
|
*/
|
||||||
public setParsedEntries(entries: NewEntry<V>[]): void {
|
public setParsedEntries(entries: NewEntry<M>[]): void {
|
||||||
this.clearData();
|
this.clearData();
|
||||||
this.clearPreview();
|
this.clearPreview();
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
@ -236,7 +236,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
* returning a new entry object
|
* returning a new entry object
|
||||||
* @param line a line extracted by the CSV (not including the header)
|
* @param line a line extracted by the CSV (not including the header)
|
||||||
*/
|
*/
|
||||||
public abstract mapData(line: string): NewEntry<V>;
|
public abstract mapData(line: string): NewEntry<M>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger for executing the import.
|
* Trigger for executing the import.
|
||||||
@ -279,7 +279,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
*
|
*
|
||||||
* @returns an observable BehaviorSubject
|
* @returns an observable BehaviorSubject
|
||||||
*/
|
*/
|
||||||
public getNewEntries(): Observable<NewEntry<V>[]> {
|
public getNewEntries(): Observable<NewEntry<M>[]> {
|
||||||
return this.newEntries.asObservable();
|
return this.newEntries.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +357,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
/**
|
/**
|
||||||
* set a list of short names for error, indicating which column failed
|
* set a list of short names for error, indicating which column failed
|
||||||
*/
|
*/
|
||||||
public setError(entry: NewEntry<V>, error: string): void {
|
public setError(entry: NewEntry<M>, error: string): void {
|
||||||
if (this.errorList.hasOwnProperty(error)) {
|
if (this.errorList.hasOwnProperty(error)) {
|
||||||
if (!entry.errors) {
|
if (!entry.errors) {
|
||||||
entry.errors = [error];
|
entry.errors = [error];
|
||||||
@ -385,7 +385,7 @@ export abstract class BaseImportService<V extends BaseViewModel> {
|
|||||||
* @param error The error to check for
|
* @param error The error to check for
|
||||||
* @returns true if the error is present
|
* @returns true if the error is present
|
||||||
*/
|
*/
|
||||||
public hasError(entry: NewEntry<V>, error: string): boolean {
|
public hasError(entry: NewEntry<M>, error: string): boolean {
|
||||||
return entry.errors.includes(error);
|
return entry.errors.includes(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { AgendaImportListComponent } from './components/agenda-import-list/agenda-import-list.component';
|
|
||||||
import { AgendaListComponent } from './components/agenda-list/agenda-list.component';
|
import { AgendaListComponent } from './components/agenda-list/agenda-list.component';
|
||||||
import { AgendaSortComponent } from './components/agenda-sort/agenda-sort.component';
|
import { AgendaSortComponent } from './components/agenda-sort/agenda-sort.component';
|
||||||
import { WatchForChangesGuard } from 'app/shared/utils/watch-for-changes.guard';
|
import { WatchForChangesGuard } from 'app/shared/utils/watch-for-changes.guard';
|
||||||
|
import { TopicImportListComponent } from 'app/site/topics/components/topic-import-list/topic-import-list.component';
|
||||||
import { ListOfSpeakersComponent } from './components/list-of-speakers/list-of-speakers.component';
|
import { ListOfSpeakersComponent } from './components/list-of-speakers/list-of-speakers.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', component: AgendaListComponent, pathMatch: 'full' },
|
{ path: '', component: AgendaListComponent, pathMatch: 'full' },
|
||||||
{ path: 'import', component: AgendaImportListComponent, data: { basePerm: 'agenda.can_manage' } },
|
{ path: 'import', component: TopicImportListComponent, data: { basePerm: 'agenda.can_manage' } },
|
||||||
{
|
{
|
||||||
path: 'sort-agenda',
|
path: 'sort-agenda',
|
||||||
component: AgendaSortComponent,
|
component: AgendaSortComponent,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { AgendaImportListComponent } from './components/agenda-import-list/agenda-import-list.component';
|
|
||||||
import { AgendaListComponent } from './components/agenda-list/agenda-list.component';
|
import { AgendaListComponent } from './components/agenda-list/agenda-list.component';
|
||||||
import { AgendaRoutingModule } from './agenda-routing.module';
|
import { AgendaRoutingModule } from './agenda-routing.module';
|
||||||
import { AgendaSortComponent } from './components/agenda-sort/agenda-sort.component';
|
import { AgendaSortComponent } from './components/agenda-sort/agenda-sort.component';
|
||||||
|
import { TopicImportListComponent } from 'app/site/topics/components/topic-import-list/topic-import-list.component';
|
||||||
import { ItemInfoDialogComponent } from './components/item-info-dialog/item-info-dialog.component';
|
import { ItemInfoDialogComponent } from './components/item-info-dialog/item-info-dialog.component';
|
||||||
import { ListOfSpeakersComponent } from './components/list-of-speakers/list-of-speakers.component';
|
import { ListOfSpeakersComponent } from './components/list-of-speakers/list-of-speakers.component';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
@ -18,7 +18,7 @@ import { SharedModule } from '../../shared/shared.module';
|
|||||||
declarations: [
|
declarations: [
|
||||||
AgendaListComponent,
|
AgendaListComponent,
|
||||||
ItemInfoDialogComponent,
|
ItemInfoDialogComponent,
|
||||||
AgendaImportListComponent,
|
TopicImportListComponent,
|
||||||
AgendaSortComponent,
|
AgendaSortComponent,
|
||||||
ListOfSpeakersComponent
|
ListOfSpeakersComponent
|
||||||
]
|
]
|
||||||
|
@ -8,15 +8,15 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { auditTime } from 'rxjs/operators';
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BaseImportService, NewEntry, ValueLabelCombination } from 'app/core/ui-services/base-import.service';
|
import { BaseImportService, NewEntry, ValueLabelCombination } from 'app/core/ui-services/base-import.service';
|
||||||
|
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||||
import { getLongPreview, getShortPreview } from 'app/shared/utils/previewStrings';
|
import { getLongPreview, getShortPreview } from 'app/shared/utils/previewStrings';
|
||||||
import { BaseViewComponent } from './base-view';
|
import { BaseViewComponent } from './base-view';
|
||||||
import { BaseViewModel } from './base-view-model';
|
|
||||||
|
|
||||||
export abstract class BaseImportListComponent<V extends BaseViewModel> extends BaseViewComponent implements OnInit {
|
export abstract class BaseImportListComponent<M extends BaseModel> extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The data source for a table. Requires to be initialised with a BaseViewModel
|
* The data source for a table. Requires to be initialised with a BaseViewModel
|
||||||
*/
|
*/
|
||||||
public dataSource: MatTableDataSource<NewEntry<V>>;
|
public dataSource: MatTableDataSource<NewEntry<M>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for previews
|
* Helper function for previews
|
||||||
@ -48,7 +48,7 @@ export abstract class BaseImportListComponent<V extends BaseViewModel> extends B
|
|||||||
* The table itself
|
* The table itself
|
||||||
*/
|
*/
|
||||||
@ViewChild(MatTable, { static: false })
|
@ViewChild(MatTable, { static: false })
|
||||||
protected table: MatTable<NewEntry<V>>;
|
protected table: MatTable<NewEntry<M>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the amount of total item successfully parsed
|
* @returns the amount of total item successfully parsed
|
||||||
@ -112,7 +112,7 @@ export abstract class BaseImportListComponent<V extends BaseViewModel> extends B
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected importer: BaseImportService<V>,
|
protected importer: BaseImportService<M>,
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar
|
matSnackBar: MatSnackBar
|
||||||
@ -204,7 +204,7 @@ export abstract class BaseImportListComponent<V extends BaseViewModel> extends B
|
|||||||
* @param row a newEntry object with a current status
|
* @param row a newEntry object with a current status
|
||||||
* @returns a css class name
|
* @returns a css class name
|
||||||
*/
|
*/
|
||||||
public getStateClass(row: NewEntry<V>): string {
|
public getStateClass(row: NewEntry<M>): string {
|
||||||
switch (row.status) {
|
switch (row.status) {
|
||||||
case 'done':
|
case 'done':
|
||||||
return 'import-done import-decided';
|
return 'import-done import-decided';
|
||||||
@ -220,7 +220,7 @@ export abstract class BaseImportListComponent<V extends BaseViewModel> extends B
|
|||||||
* @param entry a newEntry object with a current status
|
* @param entry a newEntry object with a current status
|
||||||
* @eturn the icon for the action of the item
|
* @eturn the icon for the action of the item
|
||||||
*/
|
*/
|
||||||
public getActionIcon(entry: NewEntry<V>): string {
|
public getActionIcon(entry: NewEntry<M>): string {
|
||||||
switch (entry.status) {
|
switch (entry.status) {
|
||||||
case 'error': // no import possible
|
case 'error': // no import possible
|
||||||
return 'block';
|
return 'block';
|
||||||
@ -286,7 +286,7 @@ export abstract class BaseImportListComponent<V extends BaseViewModel> extends B
|
|||||||
* @param error An error as defined as key of {@link errorList}
|
* @param error An error as defined as key of {@link errorList}
|
||||||
* @returns true if the error is present in the entry described in the row
|
* @returns true if the error is present in the entry described in the row
|
||||||
*/
|
*/
|
||||||
public hasError(row: NewEntry<V>, error: string): boolean {
|
public hasError(row: NewEntry<M>, error: string): boolean {
|
||||||
return this.importer.hasError(row, error);
|
return this.importer.hasError(row, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,4 @@ import { Motion } from 'app/shared/models/motions/motion';
|
|||||||
*/
|
*/
|
||||||
export class CreateMotion extends Motion {
|
export class CreateMotion extends Motion {
|
||||||
public submitters_id: number[];
|
public submitters_id: number[];
|
||||||
|
|
||||||
public category_id: number;
|
|
||||||
|
|
||||||
public motion_block_id: number;
|
|
||||||
|
|
||||||
public tags_id: number[];
|
|
||||||
|
|
||||||
public constructor(input?: any) {
|
|
||||||
super(input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { CreateMotion } from './create-motion';
|
import { CreateMotion } from './create-motion';
|
||||||
import { ViewCreateMotion } from './view-create-motion';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for correlating between strings representing BaseModels and existing
|
* Interface for correlating between strings representing BaseModels and existing
|
||||||
@ -17,9 +16,7 @@ export interface CsvMapping {
|
|||||||
*
|
*
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ViewCsvCreateMotion extends ViewCreateMotion {
|
export class ImportCreateMotion extends CreateMotion {
|
||||||
protected _motion: CreateMotion;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping for a new/existing category.
|
* Mapping for a new/existing category.
|
||||||
*/
|
*/
|
||||||
@ -40,10 +37,6 @@ export class ViewCsvCreateMotion extends ViewCreateMotion {
|
|||||||
*/
|
*/
|
||||||
public csvTags: CsvMapping[];
|
public csvTags: CsvMapping[];
|
||||||
|
|
||||||
public constructor(motion?: CreateMotion) {
|
|
||||||
super(motion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* takes a list of motion block mappings to update the current csvMotionblock.
|
* takes a list of motion block mappings to update the current csvMotionblock.
|
||||||
* Returns the amount of entries that remain unmatched
|
* Returns the amount of entries that remain unmatched
|
||||||
@ -54,13 +47,13 @@ export class ViewCsvCreateMotion extends ViewCreateMotion {
|
|||||||
if (!this.csvMotionblock) {
|
if (!this.csvMotionblock) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (this.csvMotionblock.id) {
|
} else if (this.csvMotionblock.id) {
|
||||||
this.motion.motion_block_id = this.csvMotionblock.id;
|
this.motion_block_id = this.csvMotionblock.id;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
const newBlock = motionBlocks.find(newMotionBlock => newMotionBlock.name === this.csvMotionblock.name);
|
const newBlock = motionBlocks.find(newMotionBlock => newMotionBlock.name === this.csvMotionblock.name);
|
||||||
if (newBlock) {
|
if (newBlock) {
|
||||||
this.csvMotionblock = newBlock;
|
this.csvMotionblock = newBlock;
|
||||||
this.motion.motion_block_id = newBlock.id;
|
this.motion_block_id = newBlock.id;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
@ -78,13 +71,13 @@ export class ViewCsvCreateMotion extends ViewCreateMotion {
|
|||||||
if (!this.csvCategory) {
|
if (!this.csvCategory) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (this.csvCategory.id) {
|
} else if (this.csvCategory.id) {
|
||||||
this.motion.category_id = this.csvCategory.id;
|
this.category_id = this.csvCategory.id;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
const newCat = categories.find(newCategory => newCategory.name === this.csvCategory.name);
|
const newCat = categories.find(newCategory => newCategory.name === this.csvCategory.name);
|
||||||
if (newCat) {
|
if (newCat) {
|
||||||
this.csvCategory = newCat;
|
this.csvCategory = newCat;
|
||||||
this.motion.category_id = newCat.id;
|
this.category_id = newCat.id;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
@ -118,7 +111,7 @@ export class ViewCsvCreateMotion extends ViewCreateMotion {
|
|||||||
open += 1;
|
open += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.motion.submitters_id = ids;
|
this.submitters_id = ids;
|
||||||
return open;
|
return open;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +142,7 @@ export class ViewCsvCreateMotion extends ViewCreateMotion {
|
|||||||
++open;
|
++open;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.motion.tags_id = ids;
|
this.tags_id = ids;
|
||||||
return open;
|
return open;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,8 +4,8 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { Motion } from 'app/shared/models/motions/motion';
|
||||||
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
|
||||||
import { MotionCsvExportService } from 'app/site/motions/services/motion-csv-export.service';
|
import { MotionCsvExportService } from 'app/site/motions/services/motion-csv-export.service';
|
||||||
import { MotionImportService } from 'app/site/motions/services/motion-import.service';
|
import { MotionImportService } from 'app/site/motions/services/motion-import.service';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ import { MotionImportService } from 'app/site/motions/services/motion-import.ser
|
|||||||
selector: 'os-motion-import-list',
|
selector: 'os-motion-import-list',
|
||||||
templateUrl: './motion-import-list.component.html'
|
templateUrl: './motion-import-list.component.html'
|
||||||
})
|
})
|
||||||
export class MotionImportListComponent extends BaseImportListComponent<ViewMotion> {
|
export class MotionImportListComponent extends BaseImportListComponent<Motion> {
|
||||||
/**
|
/**
|
||||||
* Fetach a list of the headers expected by the importer, and prepare them
|
* Fetach a list of the headers expected by the importer, and prepare them
|
||||||
* to be translateable (upper case)
|
* to be translateable (upper case)
|
||||||
|
@ -4,8 +4,8 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||||
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
||||||
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
|
||||||
import { StatuteCsvExportService } from 'app/site/motions/services/statute-csv-export.service';
|
import { StatuteCsvExportService } from 'app/site/motions/services/statute-csv-export.service';
|
||||||
import { StatuteImportService } from 'app/site/motions/services/statute-import.service';
|
import { StatuteImportService } from 'app/site/motions/services/statute-import.service';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ import { StatuteImportService } from 'app/site/motions/services/statute-import.s
|
|||||||
selector: 'os-statute-import-list',
|
selector: 'os-statute-import-list',
|
||||||
templateUrl: './statute-import-list.component.html'
|
templateUrl: './statute-import-list.component.html'
|
||||||
})
|
})
|
||||||
export class StatuteImportListComponent extends BaseImportListComponent<ViewStatuteParagraph> {
|
export class StatuteImportListComponent extends BaseImportListComponent<StatuteParagraph> {
|
||||||
/**
|
/**
|
||||||
* Constructor for list view bases
|
* Constructor for list view bases
|
||||||
*
|
*
|
||||||
|
@ -12,11 +12,11 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito
|
|||||||
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
||||||
import { Tag } from 'app/shared/models/core/tag';
|
import { Tag } from 'app/shared/models/core/tag';
|
||||||
import { Category } from 'app/shared/models/motions/category';
|
import { Category } from 'app/shared/models/motions/category';
|
||||||
|
import { Motion } from 'app/shared/models/motions/motion';
|
||||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||||
import { CreateMotion } from '../models/create-motion';
|
import { CreateMotion } from '../models/create-motion';
|
||||||
|
import { CsvMapping, ImportCreateMotion } from '../models/import-create-motion';
|
||||||
import { motionExportOnly, motionImportExportHeaderOrder } from '../motions.constants';
|
import { motionExportOnly, motionImportExportHeaderOrder } from '../motions.constants';
|
||||||
import { CsvMapping, ViewCsvCreateMotion } from '../models/view-csv-create-motion';
|
|
||||||
import { ViewMotion } from '../models/view-motion';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for motion imports
|
* Service for motion imports
|
||||||
@ -24,7 +24,7 @@ import { ViewMotion } from '../models/view-motion';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MotionImportService extends BaseImportService<ViewMotion> {
|
export class MotionImportService extends BaseImportService<Motion> {
|
||||||
/**
|
/**
|
||||||
* List of possible errors and their verbose explanation
|
* List of possible errors and their verbose explanation
|
||||||
*/
|
*/
|
||||||
@ -104,8 +104,8 @@ export class MotionImportService extends BaseImportService<ViewMotion> {
|
|||||||
* @param line
|
* @param line
|
||||||
* @returns a new Entry representing a Motion
|
* @returns a new Entry representing a Motion
|
||||||
*/
|
*/
|
||||||
public mapData(line: string): NewEntry<ViewMotion> {
|
public mapData(line: string): NewEntry<Motion> {
|
||||||
const newEntry = new ViewCsvCreateMotion(new CreateMotion());
|
const newEntry = new ImportCreateMotion(new CreateMotion());
|
||||||
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
||||||
for (let idx = 0; idx < headerLength; idx++) {
|
for (let idx = 0; idx < headerLength; idx++) {
|
||||||
switch (this.expectedHeader[idx]) {
|
switch (this.expectedHeader[idx]) {
|
||||||
@ -122,11 +122,11 @@ export class MotionImportService extends BaseImportService<ViewMotion> {
|
|||||||
newEntry.csvTags = this.getTags(line[idx]);
|
newEntry.csvTags = this.getTags(line[idx]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
newEntry.motion[this.expectedHeader[idx]] = line[idx];
|
newEntry[this.expectedHeader[idx]] = line[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const hasDuplicates = this.repo.getViewModelList().some(motion => motion.identifier === newEntry.identifier);
|
const hasDuplicates = this.repo.getViewModelList().some(motion => motion.identifier === newEntry.identifier);
|
||||||
const entry: NewEntry<ViewMotion> = {
|
const entry: NewEntry<Motion> = {
|
||||||
newEntry: newEntry,
|
newEntry: newEntry,
|
||||||
hasDuplicates: hasDuplicates,
|
hasDuplicates: hasDuplicates,
|
||||||
status: hasDuplicates ? 'error' : 'new',
|
status: hasDuplicates ? 'error' : 'new',
|
||||||
@ -157,31 +157,31 @@ export class MotionImportService extends BaseImportService<ViewMotion> {
|
|||||||
if (entry.status !== 'new') {
|
if (entry.status !== 'new') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const openBlocks = (entry.newEntry as ViewCsvCreateMotion).solveMotionBlocks(this.newMotionBlocks);
|
const openBlocks = (entry.newEntry as ImportCreateMotion).solveMotionBlocks(this.newMotionBlocks);
|
||||||
if (openBlocks) {
|
if (openBlocks) {
|
||||||
this.setError(entry, 'MotionBlock');
|
this.setError(entry, 'MotionBlock');
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const openCategories = (entry.newEntry as ViewCsvCreateMotion).solveCategory(this.newCategories);
|
const openCategories = (entry.newEntry as ImportCreateMotion).solveCategory(this.newCategories);
|
||||||
if (openCategories) {
|
if (openCategories) {
|
||||||
this.setError(entry, 'Category');
|
this.setError(entry, 'Category');
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const openUsers = (entry.newEntry as ViewCsvCreateMotion).solveSubmitters(this.newSubmitters);
|
const openUsers = (entry.newEntry as ImportCreateMotion).solveSubmitters(this.newSubmitters);
|
||||||
if (openUsers) {
|
if (openUsers) {
|
||||||
this.setError(entry, 'Submitters');
|
this.setError(entry, 'Submitters');
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const openTags = (entry.newEntry as ViewCsvCreateMotion).solveTags(this.newTags);
|
const openTags = (entry.newEntry as ImportCreateMotion).solveTags(this.newTags);
|
||||||
if (openTags) {
|
if (openTags) {
|
||||||
this.setError(entry, 'Tags');
|
this.setError(entry, 'Tags');
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await this.repo.create((entry.newEntry as ViewCsvCreateMotion).motion);
|
await this.repo.create(entry.newEntry as ImportCreateMotion);
|
||||||
entry.status = 'done';
|
entry.status = 'done';
|
||||||
}
|
}
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
|
@ -7,7 +7,6 @@ import { Papa } from 'ngx-papaparse';
|
|||||||
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
|
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
|
||||||
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
||||||
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||||
import { ViewStatuteParagraph } from '../models/view-statute-paragraph';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for motion imports
|
* Service for motion imports
|
||||||
@ -15,7 +14,7 @@ import { ViewStatuteParagraph } from '../models/view-statute-paragraph';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class StatuteImportService extends BaseImportService<ViewStatuteParagraph> {
|
export class StatuteImportService extends BaseImportService<StatuteParagraph> {
|
||||||
/**
|
/**
|
||||||
* List of possible errors and their verbose explanation
|
* List of possible errors and their verbose explanation
|
||||||
*/
|
*/
|
||||||
@ -60,16 +59,16 @@ export class StatuteImportService extends BaseImportService<ViewStatuteParagraph
|
|||||||
* @param line
|
* @param line
|
||||||
* @returns a new Entry representing a Motion
|
* @returns a new Entry representing a Motion
|
||||||
*/
|
*/
|
||||||
public mapData(line: string): NewEntry<ViewStatuteParagraph> {
|
public mapData(line: string): NewEntry<StatuteParagraph> {
|
||||||
const newEntry = new ViewStatuteParagraph(new StatuteParagraph());
|
const newEntry = new StatuteParagraph(new StatuteParagraph());
|
||||||
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
||||||
for (let idx = 0; idx < headerLength; idx++) {
|
for (let idx = 0; idx < headerLength; idx++) {
|
||||||
switch (this.expectedHeader[idx]) {
|
switch (this.expectedHeader[idx]) {
|
||||||
case 'title':
|
case 'title':
|
||||||
newEntry.statuteParagraph.title = line[idx];
|
newEntry.title = line[idx];
|
||||||
break;
|
break;
|
||||||
case 'text':
|
case 'text':
|
||||||
newEntry.statuteParagraph.text = line[idx];
|
newEntry.text = line[idx];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +90,7 @@ export class StatuteImportService extends BaseImportService<ViewStatuteParagraph
|
|||||||
if (entry.status !== 'new') {
|
if (entry.status !== 'new') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await this.repo.create(entry.newEntry.statuteParagraph);
|
await this.repo.create(entry.newEntry);
|
||||||
entry.status = 'done';
|
entry.status = 'done';
|
||||||
}
|
}
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
|
@ -197,21 +197,21 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- duration column -->
|
<!-- duration column -->
|
||||||
<ng-container matColumnDef="duration">
|
<ng-container matColumnDef="agenda_duration">
|
||||||
<mat-header-cell *matHeaderCellDef translate>Duration</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef translate>Duration</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ getDuration(entry.newEntry.duration) }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ getDuration(entry.newEntry.agenda_duration) }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- comment column-->
|
<!-- comment column-->
|
||||||
<ng-container matColumnDef="comment">
|
<ng-container matColumnDef="agenda_comment">
|
||||||
<mat-header-cell *matHeaderCellDef translate>Comment</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef translate>Comment</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.comment }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.agenda_comment }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- type column -->
|
<!-- type column -->
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="agenda_type">
|
||||||
<mat-header-cell *matHeaderCellDef translate>Type</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef translate>Type</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ getTypeString(entry.newEntry.type) | translate }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ getTypeString(entry.newEntry.agenda_type) | translate }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
@ -2,21 +2,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
|
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
import { AgendaImportListComponent } from './agenda-import-list.component';
|
import { TopicImportListComponent } from './topic-import-list.component';
|
||||||
|
|
||||||
describe('AgendaImportListComponent', () => {
|
describe('TopicImportListComponent', () => {
|
||||||
let component: AgendaImportListComponent;
|
let component: TopicImportListComponent;
|
||||||
let fixture: ComponentFixture<AgendaImportListComponent>;
|
let fixture: ComponentFixture<TopicImportListComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [AgendaImportListComponent],
|
declarations: [TopicImportListComponent],
|
||||||
imports: [E2EImportsModule]
|
imports: [E2EImportsModule]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AgendaImportListComponent);
|
fixture = TestBed.createComponent(TopicImportListComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
@ -5,21 +5,21 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { AgendaImportService } from '../../services/agenda-import.service';
|
|
||||||
import { CsvExportService } from 'app/core/ui-services/csv-export.service';
|
import { CsvExportService } from 'app/core/ui-services/csv-export.service';
|
||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
||||||
import { ViewCreateTopic } from 'app/site/topics/models/view-create-topic';
|
import { CreateTopic } from 'app/site/topics/models/create-topic';
|
||||||
|
import { TopicImportService } from '../../../topics/services/topic-import.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the agenda import list view.
|
* Component for the agenda import list view.
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-agenda-import-list',
|
selector: 'os-topic-import-list',
|
||||||
templateUrl: './agenda-import-list.component.html'
|
templateUrl: './topic-import-list.component.html'
|
||||||
})
|
})
|
||||||
export class AgendaImportListComponent extends BaseImportListComponent<ViewCreateTopic> {
|
export class TopicImportListComponent extends BaseImportListComponent<CreateTopic> {
|
||||||
/**
|
/**
|
||||||
* A form for text input
|
* A form for text input
|
||||||
*/
|
*/
|
||||||
@ -40,7 +40,7 @@ export class AgendaImportListComponent extends BaseImportListComponent<ViewCreat
|
|||||||
titleService: Title,
|
titleService: Title,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
importer: AgendaImportService,
|
importer: TopicImportService,
|
||||||
formBuilder: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
private exporter: CsvExportService,
|
private exporter: CsvExportService,
|
||||||
private durationService: DurationService
|
private durationService: DurationService
|
||||||
@ -81,7 +81,7 @@ export class AgendaImportListComponent extends BaseImportListComponent<ViewCreat
|
|||||||
* Sends the data in the text field input area to the importer
|
* Sends the data in the text field input area to the importer
|
||||||
*/
|
*/
|
||||||
public parseTextArea(): void {
|
public parseTextArea(): void {
|
||||||
(this.importer as AgendaImportService).parseTextArea(this.textAreaForm.get('inputtext').value);
|
(this.importer as TopicImportService).parseTextArea(this.textAreaForm.get('inputtext').value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -14,4 +14,13 @@ export class CreateTopic extends Topic {
|
|||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super(input);
|
super(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the CreateTopic is valid. Currently only requires an existing title
|
||||||
|
*
|
||||||
|
* @returns true if it is a valid Topic
|
||||||
|
*/
|
||||||
|
public get isValid(): boolean {
|
||||||
|
return this.title ? true : false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
import { CreateTopic } from './create-topic';
|
|
||||||
import { ViewTopic } from './view-topic';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View model for Topic('Agenda item') creation.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export class ViewCreateTopic extends ViewTopic {
|
|
||||||
public get topic(): CreateTopic {
|
|
||||||
return this._model as CreateTopic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the field representing the new title
|
|
||||||
*
|
|
||||||
* @returns title string as set during import (may be different from getTitle)
|
|
||||||
*/
|
|
||||||
public get title(): string {
|
|
||||||
return this.topic.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter for the title. Sets the title of the underlying CreateTopic
|
|
||||||
*
|
|
||||||
* @param title
|
|
||||||
*/
|
|
||||||
public set title(title: string) {
|
|
||||||
this.topic.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns the duration in minutes
|
|
||||||
*/
|
|
||||||
public get duration(): number {
|
|
||||||
return this.topic.agenda_duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter for the duration. Expects values as in {@link DurationService}
|
|
||||||
*/
|
|
||||||
public set duration(duration: number) {
|
|
||||||
this.topic.agenda_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns the comment string as set during the import
|
|
||||||
*/
|
|
||||||
public get comment(): string {
|
|
||||||
return this.topic.agenda_comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the comment string of the underlying topic
|
|
||||||
* @param comment A string to set as comment
|
|
||||||
*/
|
|
||||||
public set comment(comment: string) {
|
|
||||||
this.topic.agenda_comment = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns a number representing the item type
|
|
||||||
*/
|
|
||||||
public get type(): number {
|
|
||||||
return this.topic.agenda_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets the item type for the topic's agenda entry. No validation is done here.
|
|
||||||
*
|
|
||||||
* @param A number representing the item's type. See {@link itemVisibilityChoices}
|
|
||||||
* for the interpretation of type numbers.
|
|
||||||
*/
|
|
||||||
public set type(type: number) {
|
|
||||||
this.topic.agenda_type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the text string of the underlying topic
|
|
||||||
*
|
|
||||||
* @param text A string.
|
|
||||||
*/
|
|
||||||
public set text(text: string) {
|
|
||||||
this.topic.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns the comment string of the underlying topic
|
|
||||||
*/
|
|
||||||
public get text(): string {
|
|
||||||
return this.topic.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the CreateTopic is valid. Currently only requires an existing title
|
|
||||||
*
|
|
||||||
* @returns true if it is a valid Topic
|
|
||||||
*/
|
|
||||||
public get isValid(): boolean {
|
|
||||||
return this.title ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor. Empty
|
|
||||||
*
|
|
||||||
* @param topic A CreateTopic
|
|
||||||
*/
|
|
||||||
public constructor(topic: CreateTopic) {
|
|
||||||
super(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getVerboseName = () => {
|
|
||||||
throw new Error('This should not be used');
|
|
||||||
};
|
|
||||||
}
|
|
@ -2,9 +2,9 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
|
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
import { AgendaImportService } from './agenda-import.service';
|
import { TopicImportService } from './topic-import.service';
|
||||||
|
|
||||||
describe('AgendaImportService', () => {
|
describe('TopicImportService', () => {
|
||||||
beforeEach(() =>
|
beforeEach(() =>
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [E2EImportsModule]
|
imports: [E2EImportsModule]
|
||||||
@ -12,7 +12,7 @@ describe('AgendaImportService', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
const service: AgendaImportService = TestBed.get(AgendaImportService);
|
const service: TopicImportService = TestBed.get(TopicImportService);
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -8,18 +8,17 @@ import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repos
|
|||||||
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { ViewCreateTopic } from 'app/site/topics/models/view-create-topic';
|
import { CreateTopic } from '../models/create-topic';
|
||||||
import { CreateTopic } from '../../topics/models/create-topic';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
export class TopicImportService extends BaseImportService<CreateTopic> {
|
||||||
/**
|
/**
|
||||||
* Helper for mapping the expected header in a typesafe way. Values will be passed to
|
* Helper for mapping the expected header in a typesafe way. Values will be passed to
|
||||||
* {@link expectedHeader}
|
* {@link expectedHeader}
|
||||||
*/
|
*/
|
||||||
public headerMap: (keyof ViewCreateTopic)[] = ['title', 'text', 'duration', 'comment', 'type'];
|
public headerMap: (keyof CreateTopic)[] = ['title', 'text', 'agenda_duration', 'agenda_comment', 'agenda_type'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimimal number of header entries needed to successfully create an entry
|
* The minimimal number of header entries needed to successfully create an entry
|
||||||
@ -31,7 +30,7 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
*/
|
*/
|
||||||
public errorList = {
|
public errorList = {
|
||||||
NoTitle: 'A Topic needs a title',
|
NoTitle: 'A Topic needs a title',
|
||||||
Duplicates: 'A topic tiwh this title already exists',
|
Duplicates: 'A topic with this title already exists',
|
||||||
ParsingErrors: 'Some csv values could not be read correctly.'
|
ParsingErrors: 'Some csv values could not be read correctly.'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,17 +66,17 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
* @param line a line extracted by the CSV (without the header)
|
* @param line a line extracted by the CSV (without the header)
|
||||||
* @returns a new entry for a Topic
|
* @returns a new entry for a Topic
|
||||||
*/
|
*/
|
||||||
public mapData(line: string): NewEntry<ViewCreateTopic> {
|
public mapData(line: string): NewEntry<CreateTopic> {
|
||||||
const newEntry = new ViewCreateTopic(new CreateTopic());
|
const newEntry = new CreateTopic();
|
||||||
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
||||||
let hasErrors = false;
|
let hasErrors = false;
|
||||||
for (let idx = 0; idx < headerLength; idx++) {
|
for (let idx = 0; idx < headerLength; idx++) {
|
||||||
switch (this.expectedHeader[idx]) {
|
switch (this.expectedHeader[idx]) {
|
||||||
case 'duration':
|
case 'agenda_duration':
|
||||||
try {
|
try {
|
||||||
const duration = this.parseDuration(line[idx]);
|
const duration = this.parseDuration(line[idx]);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
newEntry.duration = duration;
|
newEntry.agenda_duration = duration;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TypeError) {
|
if (e instanceof TypeError) {
|
||||||
@ -86,9 +85,9 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'type':
|
case 'agenda_type':
|
||||||
try {
|
try {
|
||||||
newEntry.type = this.parseType(line[idx]);
|
newEntry.agenda_type = this.parseType(line[idx]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TypeError) {
|
if (e instanceof TypeError) {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
@ -103,10 +102,10 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
const hasDuplicates = this.repo.getViewModelList().some(topic => topic.title === newEntry.title);
|
const hasDuplicates = this.repo.getViewModelList().some(topic => topic.title === newEntry.title);
|
||||||
|
|
||||||
// set type to 'public' if none is given in import
|
// set type to 'public' if none is given in import
|
||||||
if (!newEntry.type) {
|
if (!newEntry.agenda_type) {
|
||||||
newEntry.type = 1;
|
newEntry.agenda_type = 1;
|
||||||
}
|
}
|
||||||
const mappedEntry: NewEntry<ViewCreateTopic> = {
|
const mappedEntry: NewEntry<CreateTopic> = {
|
||||||
newEntry: newEntry,
|
newEntry: newEntry,
|
||||||
hasDuplicates: hasDuplicates,
|
hasDuplicates: hasDuplicates,
|
||||||
status: 'new',
|
status: 'new',
|
||||||
@ -133,7 +132,7 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
if (entry.status !== 'new') {
|
if (entry.status !== 'new') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await this.repo.create(entry.newEntry.topic);
|
await this.repo.create(entry.newEntry);
|
||||||
entry.status = 'done';
|
entry.status = 'done';
|
||||||
}
|
}
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
@ -183,7 +182,7 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
* @param data a string as produced by textArea input
|
* @param data a string as produced by textArea input
|
||||||
*/
|
*/
|
||||||
public parseTextArea(data: string): void {
|
public parseTextArea(data: string): void {
|
||||||
const newEntries: NewEntry<ViewCreateTopic>[] = [];
|
const newEntries: NewEntry<CreateTopic>[] = [];
|
||||||
this.clearData();
|
this.clearData();
|
||||||
this.clearPreview();
|
this.clearPreview();
|
||||||
const lines = data.split('\n');
|
const lines = data.split('\n');
|
||||||
@ -191,14 +190,14 @@ export class AgendaImportService extends BaseImportService<ViewCreateTopic> {
|
|||||||
if (!line.length) {
|
if (!line.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newTopic = new ViewCreateTopic(
|
const newTopic = new CreateTopic(
|
||||||
new CreateTopic({
|
new CreateTopic({
|
||||||
title: line,
|
title: line,
|
||||||
agenda_type: 1 // set type to 'public item' by default
|
agenda_type: 1 // set type to 'public item' by default
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const hasDuplicates = this.repo.getViewModelList().some(topic => topic.title === newTopic.title);
|
const hasDuplicates = this.repo.getViewModelList().some(topic => topic.title === newTopic.title);
|
||||||
const newEntry: NewEntry<ViewCreateTopic> = {
|
const newEntry: NewEntry<CreateTopic> = {
|
||||||
newEntry: newTopic,
|
newEntry: newTopic,
|
||||||
hasDuplicates: hasDuplicates,
|
hasDuplicates: hasDuplicates,
|
||||||
status: 'new',
|
status: 'new',
|
@ -12,6 +12,7 @@
|
|||||||
<mat-tab-group (selectedTabChange)="onTabChange()">
|
<mat-tab-group (selectedTabChange)="onTabChange()">
|
||||||
<!-- textarea import tab -->
|
<!-- textarea import tab -->
|
||||||
<mat-tab label="{{ 'Text import' | translate }}">
|
<mat-tab label="{{ 'Text import' | translate }}">
|
||||||
|
<br>
|
||||||
<div [formGroup]="textAreaForm">
|
<div [formGroup]="textAreaForm">
|
||||||
<div>
|
<div>
|
||||||
<span translate> Copy and paste your participant names in this textbox.</span>
|
<span translate> Copy and paste your participant names in this textbox.</span>
|
||||||
@ -36,19 +37,20 @@
|
|||||||
</mat-tab>
|
</mat-tab>
|
||||||
<!-- CSV import tab -->
|
<!-- CSV import tab -->
|
||||||
<mat-tab label="{{ 'CSV import' | translate }}">
|
<mat-tab label="{{ 'CSV import' | translate }}">
|
||||||
|
<br>
|
||||||
<span translate
|
<span translate
|
||||||
>Required comma or semicolon separated values with these column header names in the first row:</span
|
>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>Title</span>, <span translate>Given name</span>, <span translate>Surname</span> ,
|
<span translate>Title</span>, <span translate>Given name</span>, <span translate>Surname</span>,
|
||||||
<span translate>Structure level</span>, <span translate>Participant number</span>,
|
<span translate>Structure level</span>, <span translate>Participant number</span>,
|
||||||
<span translate>Groups</span> , <span translate>Comment</span>, <span translate>Is active</span>,
|
<span translate>Groups</span>, <span translate>Comment</span>, <span translate>Is active</span>,
|
||||||
<span translate>Is present</span> , <span translate>Is committee</span>,
|
<span translate>Is present</span>, <span translate>Is committee</span>, <span translate>Initial password</span>,
|
||||||
<span translate>Initial password</span>, <span translate>Email</span>
|
<span translate>Email</span>, <span translate>Username</span>, <span translate>Gender</span>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li translate>
|
<li translate>
|
||||||
At least given name or surname have to be filled in. All other fields are optional and may be empty.
|
One of given name, surname and username has to be filled in. All other fields are optional and may be empty.
|
||||||
</li>
|
</li>
|
||||||
<li translate>
|
<li translate>
|
||||||
Additional columns after the required ones may be present and won't affect the import.
|
Additional columns after the required ones may be present and won't affect the import.
|
||||||
@ -224,7 +226,7 @@
|
|||||||
|
|
||||||
<ng-container matColumnDef="number">
|
<ng-container matColumnDef="number">
|
||||||
<mat-header-cell *matHeaderCellDef translate>Participant number</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef translate>Participant number</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.user.number }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.number }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- groups column -->
|
<!-- groups column -->
|
||||||
@ -277,6 +279,14 @@
|
|||||||
<mat-header-cell *matHeaderCellDef translate>Email</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef translate>Email</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.email }} </mat-cell>
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.email }} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="username">
|
||||||
|
<mat-header-cell *matHeaderCellDef translate>Username</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.username }} </mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="gender">
|
||||||
|
<mat-header-cell *matHeaderCellDef translate>Gender</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let entry"> {{ entry.newEntry.gender }} </mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
||||||
<mat-row [ngClass]="getStateClass(row)" *matRowDef="let row; columns: getColumnDefinition()"> </mat-row>
|
<mat-row [ngClass]="getStateClass(row)" *matRowDef="let row; columns: getColumnDefinition()"> </mat-row>
|
||||||
|
@ -7,9 +7,9 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { NewEntry } from 'app/core/ui-services/base-import.service';
|
import { NewEntry } from 'app/core/ui-services/base-import.service';
|
||||||
import { CsvExportService } from 'app/core/ui-services/csv-export.service';
|
import { CsvExportService } from 'app/core/ui-services/csv-export.service';
|
||||||
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
import { BaseImportListComponent } from 'app/site/base/base-import-list';
|
||||||
import { UserImportService } from '../../services/user-import.service';
|
import { UserImportService } from '../../services/user-import.service';
|
||||||
import { ViewUser } from '../../models/view-user';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the user import list view.
|
* Component for the user import list view.
|
||||||
@ -18,7 +18,7 @@ import { ViewUser } from '../../models/view-user';
|
|||||||
selector: 'os-user-import-list',
|
selector: 'os-user-import-list',
|
||||||
templateUrl: './user-import-list.component.html'
|
templateUrl: './user-import-list.component.html'
|
||||||
})
|
})
|
||||||
export class UserImportListComponent extends BaseImportListComponent<ViewUser> {
|
export class UserImportListComponent extends BaseImportListComponent<User> {
|
||||||
public textAreaForm: FormGroup;
|
public textAreaForm: FormGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +59,9 @@ export class UserImportListComponent extends BaseImportListComponent<ViewUser> {
|
|||||||
'Is present',
|
'Is present',
|
||||||
'Is a committee',
|
'Is a committee',
|
||||||
'Initial password',
|
'Initial password',
|
||||||
'Email'
|
'Email',
|
||||||
|
'Username',
|
||||||
|
'Gender'
|
||||||
];
|
];
|
||||||
const rows = [
|
const rows = [
|
||||||
[
|
[
|
||||||
@ -74,7 +76,9 @@ export class UserImportListComponent extends BaseImportListComponent<ViewUser> {
|
|||||||
1,
|
1,
|
||||||
,
|
,
|
||||||
'initialPassword',
|
'initialPassword',
|
||||||
null
|
null,
|
||||||
|
'mmustermann',
|
||||||
|
'm'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
@ -88,10 +92,12 @@ export class UserImportListComponent extends BaseImportListComponent<ViewUser> {
|
|||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
'john.doe@email.com'
|
'john.doe@email.com',
|
||||||
|
'jdoe',
|
||||||
|
'diverse'
|
||||||
],
|
],
|
||||||
[null, 'Fred', 'Bloggs', 'London', null, null, null, null, null, null, null, null],
|
[null, 'Julia', 'Bloggs', 'London', null, null, null, null, null, null, null, null, 'jbloggs', 'f'],
|
||||||
[null, null, 'Executive Board', null, null, null, null, null, null, 1, null, null]
|
[null, null, 'Executive Board', null, null, null, null, null, null, 1, null, null, 'executive', null]
|
||||||
];
|
];
|
||||||
this.exporter.dummyCSVExport(headerRow, rows, `${this.translate.instant('participants-example')}.csv`);
|
this.exporter.dummyCSVExport(headerRow, rows, `${this.translate.instant('participants-example')}.csv`);
|
||||||
}
|
}
|
||||||
@ -102,7 +108,7 @@ export class UserImportListComponent extends BaseImportListComponent<ViewUser> {
|
|||||||
* @param row
|
* @param row
|
||||||
* @returns an error string similar to getVerboseError
|
* @returns an error string similar to getVerboseError
|
||||||
*/
|
*/
|
||||||
public nameErrors(row: NewEntry<ViewUser>): string {
|
public nameErrors(row: NewEntry<User>): string {
|
||||||
for (const name of ['NoName', 'Duplicates', 'DuplicateImport']) {
|
for (const name of ['NoName', 'Duplicates', 'DuplicateImport']) {
|
||||||
if (this.importer.hasError(row, name)) {
|
if (this.importer.hasError(row, name)) {
|
||||||
return this.importer.verbose(name);
|
return this.importer.verbose(name);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { User } from 'app/shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { ViewUser } from './view-user';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for correlating between strings representing BaseModels and existing
|
* Interface for correlating between strings representing BaseModels and existing
|
||||||
@ -17,7 +16,7 @@ export interface CsvMapping {
|
|||||||
*
|
*
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ViewCsvCreateUser extends ViewUser {
|
export class ImportCreateUser extends User {
|
||||||
/**
|
/**
|
||||||
* Mapping for a new/existing groups.
|
* Mapping for a new/existing groups.
|
||||||
*/
|
*/
|
||||||
@ -28,17 +27,10 @@ export class ViewCsvCreateUser extends ViewUser {
|
|||||||
/**
|
/**
|
||||||
* Getter if the minimum requrements for a user are met: A name
|
* Getter if the minimum requrements for a user are met: A name
|
||||||
*
|
*
|
||||||
* @returns false if the user has neither first nor last name
|
* @returns false if the user has neither first nor last name nor username
|
||||||
*/
|
*/
|
||||||
public get isValid(): boolean {
|
public get isValid(): boolean {
|
||||||
if (this.user && (this.first_name || this.last_name)) {
|
return !!(this.first_name || this.last_name || this.username);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public constructor(user?: User) {
|
|
||||||
super(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +59,7 @@ export class ViewCsvCreateUser extends ViewUser {
|
|||||||
open += 1;
|
open += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.user.groups_id = ids;
|
this.groups_id = ids;
|
||||||
return open;
|
return open;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,18 +9,17 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito
|
|||||||
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.service';
|
||||||
import { Group } from 'app/shared/models/users/group';
|
import { Group } from 'app/shared/models/users/group';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { CsvMapping, ViewCsvCreateUser } from '../models/view-csv-create-user';
|
import { CsvMapping, ImportCreateUser } from '../models/import-create-user';
|
||||||
import { ViewUser } from '../models/view-user';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UserImportService extends BaseImportService<ViewUser> {
|
export class UserImportService extends BaseImportService<User> {
|
||||||
/**
|
/**
|
||||||
* Helper for mapping the expected header in a typesafe way. Values and order
|
* Helper for mapping the expected header in a typesafe way. Values and order
|
||||||
* will be passed to {@link expectedHeader}
|
* will be passed to {@link expectedHeader}
|
||||||
*/
|
*/
|
||||||
public headerMap: (keyof ViewCsvCreateUser)[] = [
|
public headerMap: (keyof ImportCreateUser)[] = [
|
||||||
'title',
|
'title',
|
||||||
'first_name',
|
'first_name',
|
||||||
'last_name',
|
'last_name',
|
||||||
@ -32,7 +31,9 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
'is_present',
|
'is_present',
|
||||||
'is_committee',
|
'is_committee',
|
||||||
'default_password',
|
'default_password',
|
||||||
'email'
|
'email',
|
||||||
|
'username',
|
||||||
|
'gender'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,8 +92,8 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
* @param line
|
* @param line
|
||||||
* @returns a new entry representing an User
|
* @returns a new entry representing an User
|
||||||
*/
|
*/
|
||||||
public mapData(line: string): NewEntry<ViewUser> {
|
public mapData(line: string): NewEntry<User> {
|
||||||
const newViewUser = new ViewCsvCreateUser(new User());
|
const newViewUser = new ImportCreateUser();
|
||||||
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
const headerLength = Math.min(this.expectedHeader.length, line.length);
|
||||||
let hasErrors = false;
|
let hasErrors = false;
|
||||||
for (let idx = 0; idx < headerLength; idx++) {
|
for (let idx = 0; idx < headerLength; idx++) {
|
||||||
@ -104,7 +105,7 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
case 'is_committee':
|
case 'is_committee':
|
||||||
case 'is_present':
|
case 'is_present':
|
||||||
try {
|
try {
|
||||||
newViewUser.user[this.expectedHeader[idx]] = this.toBoolean(line[idx]);
|
newViewUser[this.expectedHeader[idx]] = this.toBoolean(line[idx]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TypeError) {
|
if (e instanceof TypeError) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@ -114,10 +115,10 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'number':
|
case 'number':
|
||||||
newViewUser.user.number = line[idx];
|
newViewUser.number = line[idx];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
newViewUser.user[this.expectedHeader[idx]] = line[idx];
|
newViewUser[this.expectedHeader[idx]] = line[idx];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,13 +137,13 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
*/
|
*/
|
||||||
public async doImport(): Promise<void> {
|
public async doImport(): Promise<void> {
|
||||||
this.newGroups = await this.createNewGroups();
|
this.newGroups = await this.createNewGroups();
|
||||||
const importUsers: NewEntry<ViewUser>[] = [];
|
const importUsers: NewEntry<User>[] = [];
|
||||||
let trackId = 1;
|
let trackId = 1;
|
||||||
for (const entry of this.entries) {
|
for (const entry of this.entries) {
|
||||||
if (entry.status !== 'new') {
|
if (entry.status !== 'new') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const openBlocks = (entry.newEntry as ViewCsvCreateUser).solveGroups(this.newGroups);
|
const openBlocks = (entry.newEntry as ImportCreateUser).solveGroups(this.newGroups);
|
||||||
if (openBlocks) {
|
if (openBlocks) {
|
||||||
this.setError(entry, 'Group');
|
this.setError(entry, 'Group');
|
||||||
this.updatePreview();
|
this.updatePreview();
|
||||||
@ -245,7 +246,7 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
* @param data a string as produced by textArea input
|
* @param data a string as produced by textArea input
|
||||||
*/
|
*/
|
||||||
public parseTextArea(data: string): void {
|
public parseTextArea(data: string): void {
|
||||||
const newEntries: NewEntry<ViewUser>[] = [];
|
const newEntries: NewEntry<User>[] = [];
|
||||||
this.clearData();
|
this.clearData();
|
||||||
this.clearPreview();
|
this.clearPreview();
|
||||||
const lines = data.split('\n');
|
const lines = data.split('\n');
|
||||||
@ -254,7 +255,7 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nameSchema = line.includes(',') ? 'lastCommaFirst' : 'firstSpaceLast';
|
const nameSchema = line.includes(',') ? 'lastCommaFirst' : 'firstSpaceLast';
|
||||||
const newUser = new ViewCsvCreateUser(this.repo.parseUserString(line, nameSchema));
|
const newUser = new ImportCreateUser(this.repo.parseUserString(line, nameSchema));
|
||||||
const newEntry = this.userToEntry(newUser);
|
const newEntry = this.userToEntry(newUser);
|
||||||
newEntries.push(newEntry);
|
newEntries.push(newEntry);
|
||||||
});
|
});
|
||||||
@ -267,15 +268,17 @@ export class UserImportService extends BaseImportService<ViewUser> {
|
|||||||
* @param newUser
|
* @param newUser
|
||||||
* @returns a NewEntry with duplicate/error information
|
* @returns a NewEntry with duplicate/error information
|
||||||
*/
|
*/
|
||||||
private userToEntry(newUser: ViewCsvCreateUser): NewEntry<ViewUser> {
|
private userToEntry(newUser: ImportCreateUser): NewEntry<User> {
|
||||||
const newEntry: NewEntry<ViewUser> = {
|
const newEntry: NewEntry<User> = {
|
||||||
newEntry: newUser,
|
newEntry: newUser,
|
||||||
hasDuplicates: false,
|
hasDuplicates: false,
|
||||||
status: 'new',
|
status: 'new',
|
||||||
errors: []
|
errors: []
|
||||||
};
|
};
|
||||||
if (newUser.isValid) {
|
if (newUser.isValid) {
|
||||||
newEntry.hasDuplicates = this.repo.getViewModelList().some(user => user.full_name === newUser.full_name);
|
newEntry.hasDuplicates = this.repo
|
||||||
|
.getViewModelList()
|
||||||
|
.some(user => user.full_name === this.repo.getFullName(newUser));
|
||||||
if (newEntry.hasDuplicates) {
|
if (newEntry.hasDuplicates) {
|
||||||
this.setError(newEntry, 'Duplicates');
|
this.setError(newEntry, 'Duplicates');
|
||||||
}
|
}
|
||||||
|
@ -352,6 +352,7 @@ class UserViewSet(ModelViewSet):
|
|||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
# Skip invalid users.
|
# Skip invalid users.
|
||||||
|
|
||||||
continue
|
continue
|
||||||
data = serializer.prepare_password(serializer.data)
|
data = serializer.prepare_password(serializer.data)
|
||||||
groups = data["groups_id"]
|
groups = data["groups_id"]
|
||||||
@ -364,7 +365,7 @@ class UserViewSet(ModelViewSet):
|
|||||||
if "importTrackId" in user:
|
if "importTrackId" in user:
|
||||||
imported_track_ids.append(user["importTrackId"])
|
imported_track_ids.append(user["importTrackId"])
|
||||||
|
|
||||||
# Now infom all clients and send a response
|
# Now inform all clients and send a response
|
||||||
inform_changed_data(created_users)
|
inform_changed_data(created_users)
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user