Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:sciencespo/isari into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
paulgirard committed Sep 8, 2017
2 parents c86ef7b + 6d4bc4e commit 92ffa72
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 101 deletions.
130 changes: 111 additions & 19 deletions client/src/app/isari-data.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { zipObject } from 'lodash/zipObject';
import { Injectable } from '@angular/core';
import { Http, URLSearchParams, RequestOptions } from '@angular/http';
import { FormGroup, FormControl, FormArray, FormBuilder, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
Expand All @@ -22,6 +21,15 @@ import uniq from 'lodash/uniq';
import startsWith from 'lodash/startsWith';
import isPlainObject from 'lodash/isPlainObject';
import flatten from 'lodash/flatten';
import zipObject from 'lodash/zipObject';
import isArray from 'lodash/isArray';

import { DatePipe } from '@angular/common';
import {saveAs} from 'file-saver';
import Papa from 'papaparse';

const XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
CSV_MIME = 'text/csv;charset=utf-8';

const mongoSchema2Api = {
'Organization': 'organizations',
Expand Down Expand Up @@ -154,6 +162,7 @@ export class IsariDataService {
}

key2label(value, base: string[], schema, lang) {
if (isArray(value)) return value.map(v => this.key2label(v, base, schema, lang));
if (!isPlainObject(value)) {
const ref = _get(schema, [...base, 'ref'].join('.'));
if (ref && !startsWith(value, 'N/A')) return { ref, value };
Expand All @@ -171,29 +180,112 @@ export class IsariDataService {
if (typeof obj === 'string') return Observable.of(obj);
if (obj.value && obj.ref) return this.getForeignLabel(obj.ref, obj.value).map(x => x[0].value);

const format = (o, refs, level = 0) => Object.keys(o)
.reduce((s, k) => {
s += `${' '.repeat(level)}${k} : `;
if (typeof o[k] === 'string') s += o[k];
else if (o[k].ref && o[k].value) s += o[k].value.length === 0 ? '[]' : (refs[o[k].value] || '????');
else s += "\r\n" + format(o[k], refs, level + 1);
return s + "\r\n";
}, "");

const getRefs = (o) => Object.keys(o)
.reduce((r, k) => {
if (typeof o[k] === 'string') return r;
if (!o[k].ref || !o[k].value) return [...r, ...getRefs(o[k])];
return [...r, o[k]];
}, []);

return Observable.combineLatest(getRefs(obj)
.map(({ ref, value }) => this.getForeignLabel(ref, value)))
const format = (o, refs, level = 0) => {
if (isArray(o)) return o.map(oo => format(oo, refs, level)).join("\r\n---\r\n");

return Object.keys(o)
.reduce((s, k) => {
s += `${' '.repeat(level)}${k} : `;
if (typeof o[k] === 'string') s += o[k];
else if (o[k].ref && o[k].value) s += o[k].value.length === 0 ? '[]' : (refs[o[k].value] || '????');
else s += "\r\n" + format(o[k], refs, level + 1);
return s + "\r\n";
}, "");
}

const getRefs = (o) => {
if (isArray(o)) flatten(o.map(getRefs));
return Object.keys(o)
.reduce((r, k) => {
if (typeof o[k] === 'string') return r;
if (!o[k].ref || !o[k].value) return [...r, ...getRefs(o[k])];
return [...r, o[k]];
}, []);
};

const refs = getRefs(obj);

return Observable.combineLatest(
refs.length
? getRefs(obj).map(({ ref, value }) => this.getForeignLabel(ref, value))
: Observable.of([])
)
.map(labels => flatten(labels).reduce((l, v) => Object.assign(l, { [v.id]: v.value }), {}))
.map(labels => format(obj, labels));

}

exportLogs(logs, feature, labs$, translate, details) {

function csv(data) {
const csvString = Papa.unparse(data);
const blob = new Blob([csvString], {type: CSV_MIME});
saveAs(blob, `editlogs.csv`);
}

function getRow(log, feature, translations, labs, diff = null, pos = 0, values = []) {
const res = {
[translations['editLogs.date']]: (new DatePipe('fr-FR')).transform(log.date, 'yyyy-MM-dd HH:mm'),
[translations['editLogs.object.' + feature]]: log.item.name,
[translations['editLogs.action']]: log.action,
[translations['editLogs.fields']]: log._labels.join('\r\n'),
[translations['editLogs.who']]: log.who.name,
[translations['editLogs.lab']]: log.who.roles.map(role => role.lab ? labs[role.lab].value : '').join('\r\n'),
[translations['editLogs.role']]: log.who.roles.map(role => role._label).join('\r\n'),
};
if (!diff) return res;

return Object.assign(res, {
[translations['editLogs.action']]: diff.editType,
[translations['editLogs.fields']]: diff._label,
[translations['editLogs.before']]: values[pos * 2],
[translations['editLogs.after']]: values[pos * 2 + 1],

})
}

const translations$ = translate.get([
'editLogs.date', 'editLogs.who', 'editLogs.action',
'editLogs.fields', 'editLogs.role', 'editLogs.lab',
'editLogs.object.' + feature, 'editLogs.before', 'editLogs.after'
]);

if (details) {
// Je suis navré pour ce qui va suivre

const logs$ = logs.reduce((acc1, log) => [
...acc1,
...log.diff.reduce((acc2, diff) => [...acc2, (diff._beforeLabelled$ || Observable.of('')), (diff._afterLabelled$ || Observable.of(''))], [])
], []);

// RxJS FTW ?!
Observable.combineLatest([
(<Observable<any>>Observable.merge(logs$)
.mergeAll())
.scan((acc, value, i) => [...acc, value], [])
.take(logs$.length)
.last(),
translations$,
labs$
])
.subscribe(([values, translations, labs]) => {
csv(logs.reduce((d, log) => [
...d,
...log.diff.map((diff, j) => getRow(log, feature, translations, labs, diff, d.length + j, values))
], []));
});

} else {
Observable.combineLatest([
translations$,
labs$
])
.subscribe(([translations, labs]) => {
csv(logs.map(log => getRow(log, feature, translations, labs)));
})
}
}

getRelations(feature: string, id: string) {
if (!id) return Promise.resolve({}); // no id === creation === no relations
const url = `${this.dataUrl}/${feature}/${id}/relations`;
Expand Down
1 change: 1 addition & 0 deletions client/src/app/isari-editor/isari-editor.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
[labs]="labs$ | async"
[options]="options"
[hideItemCol]="true"
(onDownloadCSV)="downloadCSV($event)"
(onOptionsChange)="changeOpt($event)"
(onDetailsToggle)="toggleDetails()"></isari-log-table>

Expand Down
3 changes: 3 additions & 0 deletions client/src/app/isari-editor/isari-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,5 +319,8 @@ export class IsariEditorComponent implements OnInit {
this.details$.next(!this.details$.value);
}

downloadCSV(logs) {
this.isariDataService.exportLogs(logs, this.feature, this.labs$, this.translate, this.details$.value);
}

}
80 changes: 1 addition & 79 deletions client/src/app/isari-logs/isari-logs.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ import uniq from 'lodash/uniq';
import { BehaviorSubject } from 'rxjs';
import { EditLogApiOptions } from './EditLogApiOptions.class';

import zipObject from 'lodash/zipObject';
import { DatePipe } from '@angular/common';
import {saveAs} from 'file-saver';
import Papa from 'papaparse';

const XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
CSV_MIME = 'text/csv;charset=utf-8';

@Component({
selector: 'isari-logs',
templateUrl: './isari-logs.component.html',
Expand Down Expand Up @@ -84,77 +76,7 @@ export class IsariLogsComponent implements OnInit {
}

downloadCSV(logs) {

function csv(data) {
const csvString = Papa.unparse(data);
const blob = new Blob([csvString], {type: CSV_MIME});
saveAs(blob, `editlogs.csv`);
}

function getRow(log, feature, translations, labs, diff = null, pos = 0, values = []) {
const res = {
[translations['editLogs.date']]: (new DatePipe('fr-FR')).transform(log.date, 'yyyy-MM-dd HH:mm'),
[translations['editLogs.object.' + feature]]: log.item.name,
[translations['editLogs.action']]: log.action,
[translations['editLogs.fields']]: log._labels.join('\r\n'),
[translations['editLogs.who']]: log.who.name,
[translations['editLogs.lab']]: log.who.roles.map(role => role.lab ? labs[role.lab].value : '').join('\r\n'),
[translations['editLogs.role']]: log.who.roles.map(role => role._label).join('\r\n'),
};
if (!diff) return res;

return Object.assign(res, {
[translations['editLogs.action']]: diff.editType,
[translations['editLogs.fields']]: diff._label,
[translations['editLogs.before']]: values[pos * 2],
[translations['editLogs.after']]: values[pos * 2 + 1],

})
}

const translations$ = this.translate.get([
'editLogs.date', 'editLogs.who', 'editLogs.action',
'editLogs.fields', 'editLogs.role', 'editLogs.lab',
'editLogs.object.' + this.feature, 'editLogs.before', 'editLogs.after'
]);

if (this.details$.value) {
// Je suis navré pour ce qui va suivre

const logs$ = logs.reduce((acc1, log) => [
...acc1,
...log.diff.reduce((acc2, diff) => [...acc2, (diff._beforeLabelled$ || Observable.of('')), (diff._afterLabelled$ || Observable.of(''))], [])
], []);

// RxJS FTW ?!
Observable.combineLatest([
(<Observable<any>>Observable.merge(logs$)
.mergeAll())
.scan((acc, value, i) => [...acc, value], [])
.take(logs$.length)
.last(),
translations$,
this.labs$
])
.subscribe(([values, translations, labs]) => {
csv(logs.reduce((d, log) => [
...d,
...log.diff.map((diff, j) => getRow(log, this.feature, translations, labs, diff, d.length + j, values))
], []));
});

} else {
Observable.combineLatest([
translations$,
this.labs$
])
.subscribe(([translations, labs]) => {
csv(logs.map(log => getRow(log, this.feature, translations, labs)));
})
}


this.isariDataService.exportLogs(logs, this.feature, this.labs$, this.translate, this.details$.value);
}


}
11 changes: 8 additions & 3 deletions server/routes/editLog.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function getEditLog(req, res){
// User has to have write access on an object to access its editlog
.then(() => {
if (itemID) {
return req['userCanEdit' + model](itemID).then(ok => {
return models[model].findById(itemID).then(req['userCanEdit' + model]).then(ok => {
if (!ok) {
throw new UnauthorizedError({ title: 'Write access is mandatory to access EditLog'})
}
Expand Down Expand Up @@ -399,11 +399,16 @@ function formatEdits(data, model, removeConfidential){
})
}


// Handle confidential changes
const test = isNotConfidentialChange(model)
if (removeConfidential) {
edit.diff = edit.diff.filter(isNotConfidentialChange(model))
edit.diff = edit.diff.filter(test)
}
edit.diff = edit.diff.map(change => test(change)
? change
: Object.assign({}, change, { confidential: true }))

// Handle accessMonitoring on changes
edit.diff = getAccessMonitorings(model, edit.diff)

// TEMPFIX FOR CLIENT
Expand Down

0 comments on commit 92ffa72

Please sign in to comment.