add CSV for motions
This does the csv export for the user list emanuelschuetze: Uses right order and translatable labels jsaalfeld: fixing some documentation, rebasing to master and fixing strings and coding style. fixing #4035
This commit is contained in:
parent
14b2d33d00
commit
021cc0a393
@ -1,16 +1,26 @@
|
||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FileExportService } from './file-export.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||
import { FileExportService } from './file-export.service';
|
||||
import { ConfigService } from './config.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CsvExportService {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param exporter helper to export something as file
|
||||
* @param translate translation serivice
|
||||
* @param config Configuration Service
|
||||
*/
|
||||
public constructor(protected exporter: FileExportService, private translate: TranslateService) {}
|
||||
public constructor(
|
||||
protected exporter: FileExportService,
|
||||
private translate: TranslateService,
|
||||
private config: ConfigService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Saves an array of model data to a CSV.
|
||||
@ -29,11 +39,16 @@ export class CsvExportService {
|
||||
assemble?: string; // (if property is further child object, the property of these to be used)
|
||||
}[],
|
||||
filename: string,
|
||||
{ lineSeparator = '\r\n', columnSeparator = ';' }: { lineSeparator?: string; columnSeparator?: string } = {}
|
||||
{
|
||||
lineSeparator = '\r\n',
|
||||
columnSeparator = this.config.instant('general_csv_separator')
|
||||
}: {
|
||||
lineSeparator?: string;
|
||||
columnSeparator?: string;
|
||||
} = {}
|
||||
): void {
|
||||
const allLines = []; // Array of arrays of entries
|
||||
const usedColumns = []; // mapped properties to be included
|
||||
|
||||
// initial array of usable text separators. The first character not used
|
||||
// in any text data or as column separator will be used as text separator
|
||||
let tsList = ['"', "'", '`', '/', '\\', ';', '.'];
|
||||
@ -58,15 +73,17 @@ export class CsvExportService {
|
||||
// create lines
|
||||
data.forEach(item => {
|
||||
const line = [];
|
||||
for (let i = 0; i < usedColumns.length; i++ ){
|
||||
for (let i = 0; i < usedColumns.length; i++) {
|
||||
const property = usedColumns[i];
|
||||
let prop: any = item[property];
|
||||
if (columns[i].assemble){
|
||||
prop = item[property].map(subitem => this.translate.instant(subitem[columns[i].assemble])).join(',');
|
||||
if (columns[i].assemble) {
|
||||
prop = item[property]
|
||||
.map(subitem => this.translate.instant(subitem[columns[i].assemble]))
|
||||
.join(',');
|
||||
}
|
||||
tsList = this.checkCsvTextSafety(prop, tsList);
|
||||
line.push(prop);
|
||||
};
|
||||
}
|
||||
allLines.push(line);
|
||||
});
|
||||
|
||||
@ -100,11 +117,13 @@ export class CsvExportService {
|
||||
* Checks if a given input contains any of the characters defined in a list
|
||||
* used for textseparators. The list is then returned without the 'special'
|
||||
* characters, as they may not be used as text separator in this csv.
|
||||
*
|
||||
* @param input any input to be sent to CSV
|
||||
* @param tsList The list of special characters to check.
|
||||
* @returns the cleand CSV String list
|
||||
*/
|
||||
public checkCsvTextSafety(input: any, tsList: string[]): string[] {
|
||||
if (input === null || input === undefined ) {
|
||||
if (input === null || input === undefined) {
|
||||
return tsList;
|
||||
}
|
||||
|
||||
@ -112,6 +131,12 @@ export class CsvExportService {
|
||||
return tsList.filter(char => inputAsString.indexOf(char) < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first letter of a string
|
||||
*
|
||||
* @param input String that should be capitalized
|
||||
* @returns capitalized string
|
||||
*/
|
||||
private capitalizeTranslate(input: string): string {
|
||||
const temp = input.charAt(0).toUpperCase() + input.slice(1);
|
||||
return this.translate.instant(temp);
|
||||
|
@ -81,11 +81,6 @@
|
||||
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>
|
||||
|
||||
<mat-menu #motionListMenu="matMenu">
|
||||
<button mat-menu-item (click)="downloadMotions()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export ...</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item routerLink="category">
|
||||
<mat-icon>device_hub</mat-icon>
|
||||
<span translate>Categories</span>
|
||||
@ -105,4 +100,8 @@
|
||||
<mat-icon>sort</mat-icon>
|
||||
<span translate>Call list</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="csvExportMotionList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export as CSV</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
@ -9,7 +9,8 @@ import { ViewMotion } from '../../models/view-motion';
|
||||
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
||||
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { ConfigService } from "../../../../core/services/config.service";
|
||||
import { ConfigService } from '../../../../core/services/config.service';
|
||||
import { CsvExportService } from 'app/core/services/csv-export.service';
|
||||
|
||||
/**
|
||||
* Component that displays all the motions in a Table using DataSource.
|
||||
@ -48,6 +49,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
* @param route Current route
|
||||
* @param configService The configuration provider
|
||||
* @param repo Motion Repository
|
||||
* @param csvExport CSV Export Service
|
||||
*/
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
@ -56,7 +58,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private configService: ConfigService,
|
||||
private repo: MotionRepositoryService
|
||||
private repo: MotionRepositoryService,
|
||||
private csvExport: CsvExportService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
}
|
||||
@ -79,9 +82,11 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
}
|
||||
});
|
||||
});
|
||||
this.configService.get('motions_statutes_enabled').subscribe((enabled: boolean): void => {
|
||||
this.statutesEnabled = enabled;
|
||||
});
|
||||
this.configService.get('motions_statutes_enabled').subscribe(
|
||||
(enabled: boolean): void => {
|
||||
this.statutesEnabled = enabled;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +101,9 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
/**
|
||||
* Get the icon to the corresponding Motion Status
|
||||
* TODO Needs to be more accessible (Motion workflow needs adjustment on the server)
|
||||
*
|
||||
* @param state the name of the state
|
||||
* @returns the icon string
|
||||
*/
|
||||
public getStateIcon(state: WorkflowState): string {
|
||||
const stateName = state.name;
|
||||
@ -113,7 +120,9 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
|
||||
/**
|
||||
* Determines if an icon should be shown in the list view
|
||||
* @param state
|
||||
*
|
||||
* @param state the workflowstate
|
||||
* @returns a boolean if the icon should be shown
|
||||
*/
|
||||
public isDisplayIcon(state: WorkflowState): boolean {
|
||||
if (state) {
|
||||
@ -125,6 +134,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
|
||||
/**
|
||||
* Handler for the speakers button
|
||||
*
|
||||
* @param motion indicates the row that was clicked on
|
||||
*/
|
||||
public onSpeakerIcon(motion: ViewMotion): void {
|
||||
@ -139,11 +149,21 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
}
|
||||
|
||||
/**
|
||||
* Download all motions As PDF and DocX
|
||||
*
|
||||
* TODO: Currently does nothing
|
||||
* Export all motions as CSV
|
||||
*/
|
||||
public downloadMotions(): void {
|
||||
console.log('Download Motions Button');
|
||||
public csvExportMotionList(): void {
|
||||
this.csvExport.export(
|
||||
this.dataSource.data,
|
||||
[
|
||||
{ property: 'identifier' },
|
||||
{ property: 'title' },
|
||||
{ property: 'text' },
|
||||
{ property: 'reason' },
|
||||
{ property: 'submitters' },
|
||||
{ property: 'category' },
|
||||
{ property: 'origin' }
|
||||
],
|
||||
this.translate.instant('Motions') + '.csv'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,6 @@
|
||||
|
||||
<button mat-menu-item (click)="csvExportUserList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export as csv</span>
|
||||
<span translate>Export as CSV</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
@ -19,12 +19,16 @@ import { MatSnackBar } from '@angular/material';
|
||||
styleUrls: ['./user-list.component.scss']
|
||||
})
|
||||
export class UserListComponent extends ListViewBaseComponent<ViewUser> implements OnInit {
|
||||
|
||||
/**
|
||||
* The usual constructor for components
|
||||
*
|
||||
* @param titleService Serivce for setting the title
|
||||
* @param translate Service for translation handling
|
||||
* @param matSnackBar Helper to diplay errors
|
||||
* @param repo the user repository
|
||||
* @param titleService
|
||||
* @param translate
|
||||
* @param router the router service
|
||||
* @param route the local route
|
||||
* @param csvExport CSV export Service
|
||||
*/
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
@ -62,6 +66,7 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
||||
|
||||
/**
|
||||
* Handles the click on a user row
|
||||
*
|
||||
* @param row selected row
|
||||
*/
|
||||
public selectUser(row: ViewUser): void {
|
||||
@ -75,25 +80,27 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
||||
this.router.navigate(['./new'], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
// TODO save all data from the dataSource
|
||||
/**
|
||||
* Export all users as CSV
|
||||
*/
|
||||
public csvExportUserList(): void {
|
||||
this.csvExport.export(
|
||||
this.dataSource.data,
|
||||
[
|
||||
{ property: 'title' },
|
||||
{ property: 'first_name', label: 'First Name' },
|
||||
{ property: 'last_name', label: 'Last Name' },
|
||||
{ property: 'structure_level', label: 'Structure Level' },
|
||||
{ property: 'participant_number', label: 'Participant Number' },
|
||||
{ property: 'groups', assemble: 'name'},
|
||||
{ property: 'first_name', label: 'Given name' },
|
||||
{ property: 'last_name', label: 'Surname' },
|
||||
{ property: 'structure_level', label: 'Structure level' },
|
||||
{ property: 'participant_number', label: 'Participant number' },
|
||||
{ property: 'groups', assemble: 'name' },
|
||||
{ property: 'comment' },
|
||||
{ property: 'is_active', label: 'Active' },
|
||||
{ property: 'is_present', label: 'Presence' },
|
||||
{ property: 'is_committee', label: 'Committee' },
|
||||
{ property: 'default_password', label: 'Default password' },
|
||||
{ property: 'email', label: 'E-Mail' }
|
||||
{ property: 'is_active', label: 'Is active' },
|
||||
{ property: 'is_present', label: 'Is present' },
|
||||
{ property: 'is_committee', label: 'Is a committee' },
|
||||
{ property: 'default_password', label: 'Initial password' },
|
||||
{ property: 'email' }
|
||||
],
|
||||
'export.csv'
|
||||
this.translate.instant('Participants') + '.csv'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"About me":"O mě","Accept":"Přijmout","Accepted":"Přijato","Agenda":"Pořad jednání","All your changes are saved immediately.":"Všechny vaše změny jsou okamžitě uloženy.","Cancel":"Zrušit","Categories":"Skupiny","Category":"Obor činnosti","Change password":"Změnit heslo","Change recommendation":"Změnit doporučení","Changed version":"Změněná verze","Comment":"Poznámka","Comments":"Poznámky","Content":"Obsah","Create new category":"Vytvořit nový obor činnosti","Create new comment field":"Vytvořit nové poznámkové pole","Create new statute paragraph":"Vytvořit nový odstavec předpisu","Delete":"Smazat","Delete all files":"Smazat všechny soubory","Designates whether this user is in the room.":"Určuje, zda je tento uživatel v místnosti.","Designates whether this user should be treated as a committee.":"Určuje, zda se s tímto uživatelem má zacházet jako s výborem.","Designates whether this user should be treated as active. Unselect this instead of deleting the account.":"Určuje, zda se má s tímto uživatelem zacházet jako s činným. Zrušte vybrání namísto smazání účtu.","Diff version":"Znázornění změn","Edit":"Upravit","Edit category:":"Upravit obor činnosti:","Edit comment field:":"Upravit poznámkové pole:","Edit profile":"Upravit profil","Edit statute paragraph:":"Upravit odstavec předpisu:","Elections":"Elections","Email":"E-mail","English":"Angličtina","Export ...":"Vyvést...","Export as csv":"Vyvést jako CSV","FILTER":"FILTR","Files":"Soubory","Final version":"Konečná verze","French":"Francouzština","German":"Němčina","Given name":"Křestní jméno","Groups":"Skupiny","Groups with read permissions":"Skupiny s oprávněním pro čtení","Groups with write permissions":"Skupiny s oprávněním pro zápis","Home":"Domovská stránka","Identifier":"Identifikátor","Import ...":"Zavést...","Initial password":"Počáteční heslo","Installed plugins":"Nainstalované přídavné moduly","Is a committee":"Je výbor","Is active":"Je činná","Is present":"Je přítomná","Legal notice":"Právní upozornění","License":"Povolení","Line":"Řádek","Login":"Přihlášení","Login as Guest":"Přihlásit se jako host","Logout":"Odhlásit se","Meta information":"Popisné informace","Motion":"Návrh","Motions":"Návrhy","Name":"Název","New group name":"Název nové skupiny","New motion":"Nový návrh","New tag name":"Název nové značky","No change recommendations yet":"Dosud žádná doporučení změn","No comment":"Bez poznámky","No groups selected":"Nevybrány žádné skupiny","No personal note":"Bez osobní poznámky","No statute paragraphs":"Žádné odstavce předpisu","None":"Žádné","Note: You have to reject all change recommendations if the plenum does not follow the recommendation. This does not affect amendments.":"Poznámka: Musíte odmítnout všechna doporučení změn, pokud plenární zasedání nesleduje doporučení. Toto neovlivní dodatky.","OK":"OK","Offline mode: You can use OpenSlides but changes are not saved.":"Režim nepřipojen k internetu: Můžete OpenSlides používat, ale změny nejsou ukládány.","Only for internal notes.":"Jen pro vnitřní poznámky.","Origin":"Původ","Original version":"Původní verze","PDF":"PDF","Participant number":"Číslo účastníka","Participants":"Účastníci","Permissions":"Oprávnění","Personal note":"Osobní poznámka","Prefix":"Předpona","Present":"Přítomen","Privacy Policy":"Politika pro soukromí","Privacy policy":"Politika pro soukromí","Project":"Promítat","Projector":"Promítací přístroj","Public":"Veřejný","Reason":"Zdůvodnění","Reject":"Odmítnout","Rejected":"Odmítnuto","Required":"Požadováno","Reset recommendation":"Vynulovat doporučení","Reset state":"Obnovit stav","SORT":"TŘÍDIT","Save":"Uložit","Selected values":"Vybrané hodnoty","Settings":"Nastavení","Sort ...":"Třídit...","State":"Stav","Statute paragraph":"Odstavec předpisu","Statute paragraphs":"Odstavce předpisu","Structure level":"Úroveň rozčlenění","Submitters":"Navrhovatelé","Summary of changes":"Přehled změn","Supporters":"Podporovatel","Surname":"Příjmení","Tags":"Klíčová slova","The assembly may decide:":"Shromáždění se může usnést:","The event manager hasn't set up a privacy policy yet.":"Správce událostí ještě nenastavil politiku soukromí.","This change collides with another one.":"Tato změna se střetává s jinou.","Title":"Název","Username":"Uživatelské jméno","Welcome to OpenSlides":"Vítejte v OpenSlides","Yes":"Ano","by":"od","inline":"uvnitř","none":"žádné","outside":"vně"}
|
||||
{"About me":"O mě","Accept":"Přijmout","Accepted":"Přijato","Agenda":"Pořad jednání","All your changes are saved immediately.":"Všechny vaše změny jsou okamžitě uloženy.","Cancel":"Zrušit","Categories":"Skupiny","Category":"Obor činnosti","Change password":"Změnit heslo","Change recommendation":"Změnit doporučení","Changed version":"Změněná verze","Comment":"Poznámka","Comments":"Poznámky","Content":"Obsah","Create new category":"Vytvořit nový obor činnosti","Create new comment field":"Vytvořit nové poznámkové pole","Create new statute paragraph":"Vytvořit nový odstavec předpisu","Delete":"Smazat","Delete all files":"Smazat všechny soubory","Designates whether this user is in the room.":"Určuje, zda je tento uživatel v místnosti.","Designates whether this user should be treated as a committee.":"Určuje, zda se s tímto uživatelem má zacházet jako s výborem.","Designates whether this user should be treated as active. Unselect this instead of deleting the account.":"Určuje, zda se má s tímto uživatelem zacházet jako s činným. Zrušte vybrání namísto smazání účtu.","Diff version":"Znázornění změn","Edit":"Upravit","Edit category:":"Upravit obor činnosti:","Edit comment field:":"Upravit poznámkové pole:","Edit profile":"Upravit profil","Edit statute paragraph:":"Upravit odstavec předpisu:","Elections":"Elections","Email":"E-mail","English":"Angličtina","Export ...":"Vyvést...","Export as CSV":"Vyvést jako CSV","FILTER":"FILTR","Files":"Soubory","Final version":"Konečná verze","French":"Francouzština","German":"Němčina","Given name":"Křestní jméno","Groups":"Skupiny","Groups with read permissions":"Skupiny s oprávněním pro čtení","Groups with write permissions":"Skupiny s oprávněním pro zápis","Home":"Domovská stránka","Identifier":"Identifikátor","Import ...":"Zavést...","Initial password":"Počáteční heslo","Installed plugins":"Nainstalované přídavné moduly","Is a committee":"Je výbor","Is active":"Je činná","Is present":"Je přítomná","Legal notice":"Právní upozornění","License":"Povolení","Line":"Řádek","Login":"Přihlášení","Login as Guest":"Přihlásit se jako host","Logout":"Odhlásit se","Meta information":"Popisné informace","Motion":"Návrh","Motions":"Návrhy","Name":"Název","New group name":"Název nové skupiny","New motion":"Nový návrh","New tag name":"Název nové značky","No change recommendations yet":"Dosud žádná doporučení změn","No comment":"Bez poznámky","No groups selected":"Nevybrány žádné skupiny","No personal note":"Bez osobní poznámky","No statute paragraphs":"Žádné odstavce předpisu","None":"Žádné","Note: You have to reject all change recommendations if the plenum does not follow the recommendation. This does not affect amendments.":"Poznámka: Musíte odmítnout všechna doporučení změn, pokud plenární zasedání nesleduje doporučení. Toto neovlivní dodatky.","OK":"OK","Offline mode: You can use OpenSlides but changes are not saved.":"Režim nepřipojen k internetu: Můžete OpenSlides používat, ale změny nejsou ukládány.","Only for internal notes.":"Jen pro vnitřní poznámky.","Origin":"Původ","Original version":"Původní verze","PDF":"PDF","Participant number":"Číslo účastníka","Participants":"Účastníci","Permissions":"Oprávnění","Personal note":"Osobní poznámka","Prefix":"Předpona","Present":"Přítomen","Privacy Policy":"Politika pro soukromí","Privacy policy":"Politika pro soukromí","Project":"Promítat","Projector":"Promítací přístroj","Public":"Veřejný","Reason":"Zdůvodnění","Reject":"Odmítnout","Rejected":"Odmítnuto","Required":"Požadováno","Reset recommendation":"Vynulovat doporučení","Reset state":"Obnovit stav","SORT":"TŘÍDIT","Save":"Uložit","Selected values":"Vybrané hodnoty","Settings":"Nastavení","Sort ...":"Třídit...","State":"Stav","Statute paragraph":"Odstavec předpisu","Statute paragraphs":"Odstavce předpisu","Structure level":"Úroveň rozčlenění","Submitters":"Navrhovatelé","Summary of changes":"Přehled změn","Supporters":"Podporovatel","Surname":"Příjmení","Tags":"Klíčová slova","The assembly may decide:":"Shromáždění se může usnést:","The event manager hasn't set up a privacy policy yet.":"Správce událostí ještě nenastavil politiku soukromí.","This change collides with another one.":"Tato změna se střetává s jinou.","Title":"Název","Username":"Uživatelské jméno","Welcome to OpenSlides":"Vítejte v OpenSlides","Yes":"Ano","by":"od","inline":"uvnitř","none":"žádné","outside":"vně"}
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Translators:
|
||||
# fri, 2018
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: fri, 2018\n"
|
||||
@ -111,7 +111,7 @@ msgstr "Angličtina"
|
||||
msgid "Export ..."
|
||||
msgstr "Vyvést..."
|
||||
|
||||
msgid "Export as csv"
|
||||
msgid "Export as CSV"
|
||||
msgstr "Vyvést jako CSV"
|
||||
|
||||
msgid "FILTER"
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
# Translators:
|
||||
# Emanuel Schütze <emanuel.schuetze@intevation.de>, 2018
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: Emanuel Schütze <emanuel.schuetze@intevation.de>, 2018\n"
|
||||
@ -417,7 +417,7 @@ msgstr "Export"
|
||||
msgid "Export ..."
|
||||
msgstr "Exportieren ..."
|
||||
|
||||
msgid "Export as csv"
|
||||
msgid "Export as CSV"
|
||||
msgstr "Exportieren als CSV"
|
||||
|
||||
msgid "FILTER"
|
||||
|
@ -386,7 +386,7 @@ msgstr ""
|
||||
msgid "Export ..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Export as csv"
|
||||
msgid "Export as CSV"
|
||||
msgstr ""
|
||||
|
||||
msgid "FILTER"
|
||||
@ -1059,4 +1059,4 @@ msgid "submitted"
|
||||
msgstr ""
|
||||
|
||||
msgid "withdrawed"
|
||||
msgstr ""
|
||||
msgstr ""
|
||||
|
Loading…
Reference in New Issue
Block a user