From 7b0d8e903a94b7fdb044457333d4e9a280a66987 Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:16:32 +0100 Subject: [PATCH 01/12] Working on it --- src/app/core/config/config-fix.ts | 10 +++- .../download-service/download.service.ts | 53 +++++++++++++++++-- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index 84b2a62b3a..7ae19c81fa 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -299,7 +299,8 @@ export const defaultJsonConfig = { "name", { id: "DisplayParticipantsCount", viewComponent: "DisplayParticipantsCount", label: $localize`Children` }, "privateSchool", - "language" + "language", + "linkedSchool" ], "filters": [ { @@ -325,7 +326,7 @@ export const defaultJsonConfig = { { "fields": ["name", "privateSchool"] }, { "fields": ["address", "phone"] }, { "fields": ["language", "timing"] }, - { "fields": ["remarks"] } + { "fields": ["remarks", "linkedSchool"] } ] } } @@ -831,6 +832,11 @@ export const defaultJsonConfig = { "remarks": { "dataType": "string", "label": $localize`:Label for the remarks for a school:Remarks` + }, + "linkedSchool": { + "dataType": "entity", + "additional": "School", + "label": $localize`:Label for the remarks for a school:Linked School` } }, }, diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 85012822c6..69c05cc026 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -6,6 +6,11 @@ import { DataTransformationService } from "../data-transformation-service/data-t import { transformToReadableFormat } from "../../common-components/entities-table/value-accessor/value-accessor"; import { Papa } from "ngx-papaparse"; import { EntitySchemaField } from "app/core/entity/schema/entity-schema-field"; +import { Entity } from "app/core/entity/model/entity"; +import { EntityDatatype } from "app/core/basic-datatypes/entity/entity.datatype"; +import { DefaultDatatype } from "app/core/entity/default-datatype/default.datatype"; +import { EntityArrayDatatype } from "app/core/basic-datatypes/entity-array/entity-array.datatype"; +import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; /** * This service allows to start a download process from the browser. @@ -22,6 +27,7 @@ export class DownloadService { private dataTransformationService: DataTransformationService, private papa: Papa, private loggingService: LoggingService, + private entityMapperService: EntityMapperService, ) {} /** @@ -37,6 +43,16 @@ export class DownloadService { filename: string, exportConfig?: ExportColumnConfig[], ) { + console.log( + "trigger download with data: ", + data, + "; format: ", + format, + "; filename: ", + filename, + "; exportConfig: ", + exportConfig, + ); const blobData = await this.getFormattedBlobData( data, format, @@ -96,11 +112,15 @@ export class DownloadService { if (data.length > 0 && typeof data[0]?.getConstructor === "function") { entityConstructor = data[0].getConstructor(); } + console.log("entity constructor: ", entityConstructor); const keys = new Set(); + console.log("data: ", data); data.forEach((row) => Object.keys(row).forEach((key) => keys.add(key))); data = data.map(transformToReadableFormat); + console.log("data after map: ", data); + if (!entityConstructor) { return this.papa.unparse(data, { quotes: true, @@ -111,33 +131,58 @@ export class DownloadService { } const result = this.exportFile(data, entityConstructor); + console.log("result: ", result); return result; } exportFile(data: any[], entityConstructor: { schema: any }) { const entitySchema = entityConstructor.schema; - const columnLabels = new Map(); + const columnLabels = new Map(); + + console.log("entitySchema: ", entitySchema); - entitySchema.forEach((value: { label: EntitySchemaField }, key: string) => { - if (value.label) columnLabels.set(key, value.label); + entitySchema.forEach((value: EntitySchemaField, key: string) => { + if (value.label) { + columnLabels.set(key, value.label); + if (value.dataType === EntityDatatype.dataType) { + console.log("EntityDataType bei", value.label); + columnLabels.set(key + "_readable", value.label + "_readable"); + } + if (value.dataType === EntityArrayDatatype.dataType) + console.log("EntityArrayDataType bei", value.label); + } }); - const exportEntities = data.map((item) => { + console.log("columnLabels: ", columnLabels); + + // XXX TODO XXX: await doen't work in here! + const exportEntities = data.map(async (item) => { let newItem = {}; for (const key in item) { + console.log("Peter prüft key:", key); if (columnLabels.has(key)) { newItem[key] = item[key]; } + if (columnLabels.has(key + "_readable")) { + console.log(" Peter ist hier"); + const type = Entity.extractTypeFromId(key); + const entity: Entity = await this.entityMapperService.load(type, key); + newItem[key + "_readable"] = entity.toString(); + } } return newItem; }); + console.log("exportEntities; ", exportEntities); + const columnKeys: string[] = Array.from(columnLabels.keys()); const labels: any[] = Array.from(columnLabels.values()); const orderedData: any[] = exportEntities.map((item) => columnKeys.map((key) => item[key]), ); + console.log("orderedData:", orderedData); + return this.papa.unparse( { fields: labels, From e0a05c80337e1af16ba68e2b21676e89a3c58c8f Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:01:13 +0100 Subject: [PATCH 02/12] Got first test to work --- .../download-service/download.service.spec.ts | 37 ++++++++++++- .../download-service/download.service.ts | 52 ++++++++++++------- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 8c72ee576f..9e41e30428 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -8,15 +8,21 @@ import { Entity } from "../../entity/model/entity"; import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.interface"; import { DatabaseField } from "../../entity/database-field.decorator"; import moment from "moment"; +import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; +import { School } from "app/child-dev-project/schools/model/school"; -describe("DownloadService", () => { +fdescribe("DownloadService", () => { let service: DownloadService; let mockDataTransformationService: jasmine.SpyObj; + let mockEntityMapper: jasmine.SpyObj; + const testSchool = School.create({ name: "Test School" }); beforeEach(() => { mockDataTransformationService = jasmine.createSpyObj([ "queryAndTransformData", ]); + mockEntityMapper = jasmine.createSpyObj(["load"]); + mockEntityMapper.load.and.resolveTo(testSchool); TestBed.configureTestingModule({ providers: [ DownloadService, @@ -24,6 +30,10 @@ describe("DownloadService", () => { provide: DataTransformationService, useValue: mockDataTransformationService, }, + { + provide: EntityMapperService, + useValue: mockEntityMapper, + }, LoggingService, ], }); @@ -78,7 +88,7 @@ describe("DownloadService", () => { '"_id","_rev","propOne","propTwo"' + DownloadService.SEPARATOR_ROW + '"TestForCsvEntity:1","2","first","second"'; - spyOn(service, "exportFile").and.returnValue(expected); + spyOn(service, "exportFile").and.resolveTo(expected); const result = await service.createCsv([test]); expect(result).toEqual(expected); }); @@ -114,6 +124,29 @@ describe("DownloadService", () => { expect(columnValues).toContain('"true"'); }); + fit("should add column with entity toString for referenced entities in export", async () => { + @DatabaseEntity("EntityRefDownloadTestEntity") + class EntityRefDownloadTestEntity extends Entity { + @DatabaseField({ dataType: "entity", label: "referenced entity" }) + relatedEntity: string; + //@DatabaseField({ dataType: "entity-array" }) relatedEntitiesArray: string[]; + } + const relatedEntity = testSchool; + + const testEntity = new EntityRefDownloadTestEntity(); + testEntity.relatedEntity = relatedEntity.getId(); + + const csvExport = await service.createCsv([testEntity]); + console.log("csvExport:", csvExport); + + const rows = csvExport.split(DownloadService.SEPARATOR_ROW); + expect(rows).toHaveSize(1 + 1); // includes 1 header line + const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); + expect(columnValues).toHaveSize(2); + expect(columnValues).toContain('"' + relatedEntity.getId() + '"'); + expect(columnValues).toContain('"' + relatedEntity.toString() + '"'); + }); + it("should export all properties using object keys as headers, if no schema is available", async () => { const docs = [ { _id: "Test:1", name: "Child 1" }, diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 69c05cc026..5d0014b937 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -130,12 +130,12 @@ export class DownloadService { }); } - const result = this.exportFile(data, entityConstructor); + const result = await this.exportFile(data, entityConstructor); console.log("result: ", result); return result; } - exportFile(data: any[], entityConstructor: { schema: any }) { + async exportFile(data: any[], entityConstructor: { schema: any }) { const entitySchema = entityConstructor.schema; const columnLabels = new Map(); @@ -150,28 +150,15 @@ export class DownloadService { } if (value.dataType === EntityArrayDatatype.dataType) console.log("EntityArrayDataType bei", value.label); + /// ToDo: Add code here } }); console.log("columnLabels: ", columnLabels); - // XXX TODO XXX: await doen't work in here! - const exportEntities = data.map(async (item) => { - let newItem = {}; - for (const key in item) { - console.log("Peter prüft key:", key); - if (columnLabels.has(key)) { - newItem[key] = item[key]; - } - if (columnLabels.has(key + "_readable")) { - console.log(" Peter ist hier"); - const type = Entity.extractTypeFromId(key); - const entity: Entity = await this.entityMapperService.load(type, key); - newItem[key + "_readable"] = entity.toString(); - } - } - return newItem; - }); + const exportEntities = await Promise.all( + data.map(async (item) => this.mapEntity(item, columnLabels)), + ); console.log("exportEntities; ", exportEntities); @@ -194,4 +181,31 @@ export class DownloadService { }, ); } + + private async mapEntity(item: Entity, columnLabels): Promise { + let newItem = {}; + for (const key in item) { + console.log("Peter prüft key:", key); + if (columnLabels.has(key)) { + newItem[key] = item[key]; + } + if (columnLabels.has(key + "_readable")) { + const relatedEntityId = item[key]; + console.log(" Peter ist hier", key); + const type = Entity.extractTypeFromId(relatedEntityId); + console.log( + " Peter type:", + type, + "; relatedEntityId: ", + relatedEntityId, + ); + const entity: Entity = await this.entityMapperService.load( + type, + relatedEntityId, + ); + newItem[key + "_readable"] = entity.toString(); + } + } + return newItem; + } } From 277e3996d419e4b35912eacc2212190e42f78c9e Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:51:36 +0100 Subject: [PATCH 03/12] Advancing, but WIP and currently not working --- .../download-service/download.service.spec.ts | 45 ++++++++++++++++--- .../download-service/download.service.ts | 44 +++++++++++------- 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 9e41e30428..0adcf0a4d6 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -10,19 +10,21 @@ import { DatabaseField } from "../../entity/database-field.decorator"; import moment from "moment"; import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; import { School } from "app/child-dev-project/schools/model/school"; +import { Child } from "app/child-dev-project/children/model/child"; +import { mockEntityMapper } from "app/core/entity/entity-mapper/mock-entity-mapper-service"; fdescribe("DownloadService", () => { let service: DownloadService; let mockDataTransformationService: jasmine.SpyObj; - let mockEntityMapper: jasmine.SpyObj; + let mockedEntityMapper; const testSchool = School.create({ name: "Test School" }); + const testChild = Child.create("Test Child"); beforeEach(() => { mockDataTransformationService = jasmine.createSpyObj([ "queryAndTransformData", ]); - mockEntityMapper = jasmine.createSpyObj(["load"]); - mockEntityMapper.load.and.resolveTo(testSchool); + mockedEntityMapper = mockEntityMapper([testSchool, testChild]); TestBed.configureTestingModule({ providers: [ DownloadService, @@ -32,7 +34,7 @@ fdescribe("DownloadService", () => { }, { provide: EntityMapperService, - useValue: mockEntityMapper, + useValue: mockedEntityMapper, }, LoggingService, ], @@ -124,17 +126,20 @@ fdescribe("DownloadService", () => { expect(columnValues).toContain('"true"'); }); - fit("should add column with entity toString for referenced entities in export", async () => { + it("should add columns with entity toString for referenced entities in export", async () => { @DatabaseEntity("EntityRefDownloadTestEntity") class EntityRefDownloadTestEntity extends Entity { @DatabaseField({ dataType: "entity", label: "referenced entity" }) relatedEntity: string; - //@DatabaseField({ dataType: "entity-array" }) relatedEntitiesArray: string[]; + @DatabaseField({ dataType: "entity", label: "referenced entity 2" }) + relatedEntity2: string; } const relatedEntity = testSchool; + const relatedEntity2 = testChild; const testEntity = new EntityRefDownloadTestEntity(); testEntity.relatedEntity = relatedEntity.getId(); + testEntity.relatedEntity2 = relatedEntity2.getId(); const csvExport = await service.createCsv([testEntity]); console.log("csvExport:", csvExport); @@ -142,9 +147,35 @@ fdescribe("DownloadService", () => { const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); - expect(columnValues).toHaveSize(2); + expect(columnValues).toHaveSize(4); expect(columnValues).toContain('"' + relatedEntity.getId() + '"'); expect(columnValues).toContain('"' + relatedEntity.toString() + '"'); + expect(columnValues).toContain('"' + relatedEntity2.getId() + '"'); + expect(columnValues).toContain('"' + relatedEntity2.toString() + '"'); + }); + + fit("should add column with entity toString for referenced array of entities in export", async () => { + @DatabaseEntity("EntityRefDownloadTestEntity") + class EntityRefDownloadTestEntity extends Entity { + // @DatabaseField({ dataType: "entity", label: "referenced entity" }) + // relatedEntity: string; + @DatabaseField({ dataType: "entity-array", label: "referenced entities" }) + relatedEntitiesArray: string[]; + } + const testEntity = new EntityRefDownloadTestEntity(); + testEntity.relatedEntitiesArray = [testSchool.getId(), testChild.getId()]; + + const csvExport = await service.createCsv([testEntity]); + console.log("csvExport:", csvExport); + + const rows = csvExport.split(DownloadService.SEPARATOR_ROW); + expect(rows).toHaveSize(1 + 1); // includes 1 header line + const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); + expect(columnValues).toHaveSize(4); + expect(columnValues).toContain('"' + testSchool.getId() + '"'); + expect(columnValues).toContain('"' + testSchool.toString() + '"'); + expect(columnValues).toContain('"' + testChild.getId() + '"'); + expect(columnValues).toContain('"' + testChild.toString() + '"'); }); it("should export all properties using object keys as headers, if no schema is available", async () => { diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 5d0014b937..89adfbac57 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -148,9 +148,10 @@ export class DownloadService { console.log("EntityDataType bei", value.label); columnLabels.set(key + "_readable", value.label + "_readable"); } - if (value.dataType === EntityArrayDatatype.dataType) + if (value.dataType === EntityArrayDatatype.dataType) { console.log("EntityArrayDataType bei", value.label); - /// ToDo: Add code here + columnLabels.set(key + "_readable", value.label + "_readable"); + } } }); @@ -190,20 +191,31 @@ export class DownloadService { newItem[key] = item[key]; } if (columnLabels.has(key + "_readable")) { - const relatedEntityId = item[key]; - console.log(" Peter ist hier", key); - const type = Entity.extractTypeFromId(relatedEntityId); - console.log( - " Peter type:", - type, - "; relatedEntityId: ", - relatedEntityId, - ); - const entity: Entity = await this.entityMapperService.load( - type, - relatedEntityId, - ); - newItem[key + "_readable"] = entity.toString(); + let relatedEntitiesIdArray: string[] = []; + let relatedEntitiesToStringArray: string[] = []; + if (Array.isArray(item[key])) { + relatedEntitiesIdArray = item[key]; + } else { + relatedEntitiesIdArray = [...item[key]]; + } + relatedEntitiesIdArray.forEach(async (relatedEntityId) => { + console.log(" Peter ist hier", key); + const type = Entity.extractTypeFromId(relatedEntityId); + console.log( + " Peter type:", + type, + "; relatedEntityId: ", + relatedEntityId, + ); + let relatedEntity: Entity = await this.entityMapperService.load( + type, + relatedEntityId, + ); + console.log("Peter entity", relatedEntity); + console.log("Peter entity.toString()", relatedEntity.toString()); + relatedEntitiesToStringArray.push(relatedEntity.toString()); + }); + newItem[key + "_readable"] = relatedEntitiesToStringArray; } } return newItem; From cb865bbab4e0d7cd4b4bce65e98d85370a0083d8 Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:34:09 +0100 Subject: [PATCH 04/12] Still WIP --- .../export/download-service/download.service.spec.ts | 10 +++++----- .../core/export/download-service/download.service.ts | 7 ++++--- src/environments/environment.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 0adcf0a4d6..532cd23f9b 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -171,11 +171,11 @@ fdescribe("DownloadService", () => { const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); - expect(columnValues).toHaveSize(4); - expect(columnValues).toContain('"' + testSchool.getId() + '"'); - expect(columnValues).toContain('"' + testSchool.toString() + '"'); - expect(columnValues).toContain('"' + testChild.getId() + '"'); - expect(columnValues).toContain('"' + testChild.toString() + '"'); + expect(columnValues).toHaveSize(2); + // expect(columnValues).toContain('"' + testSchool.getId() + '"'); + // expect(columnValues).toContain('"' + testSchool.toString() + '"'); + // expect(columnValues).toContain('"' + testChild.getId() + '"'); + // expect(columnValues).toContain('"' + testChild.toString() + '"'); }); it("should export all properties using object keys as headers, if no schema is available", async () => { diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 89adfbac57..7cca76cbe8 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -169,7 +169,8 @@ export class DownloadService { columnKeys.map((key) => item[key]), ); - console.log("orderedData:", orderedData); + console.log("Peter labels", labels); + console.log("orderedData:", JSON.stringify(orderedData)); return this.papa.unparse( { @@ -198,7 +199,7 @@ export class DownloadService { } else { relatedEntitiesIdArray = [...item[key]]; } - relatedEntitiesIdArray.forEach(async (relatedEntityId) => { + for (let relatedEntityId of relatedEntitiesIdArray) { console.log(" Peter ist hier", key); const type = Entity.extractTypeFromId(relatedEntityId); console.log( @@ -214,7 +215,7 @@ export class DownloadService { console.log("Peter entity", relatedEntity); console.log("Peter entity.toString()", relatedEntity.toString()); relatedEntitiesToStringArray.push(relatedEntity.toString()); - }); + } newItem[key + "_readable"] = relatedEntitiesToStringArray; } } diff --git a/src/environments/environment.ts b/src/environments/environment.ts index be96f460e4..7c78ab596e 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -32,7 +32,7 @@ export const environment = { remoteLoggingDsn: undefined, // only set for production mode in environment.prod.ts /** The following settings can be overridden by the `config.json` if present, see {@link AppSettings} */ demo_mode: true, - session_type: SessionType.mock, + session_type: SessionType.local, account_url: "https://accounts.aam-digital.net", email: undefined, }; From c00d1c4429071768856e029a5c556cb3dece884d Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:46:39 +0100 Subject: [PATCH 05/12] Bugfix and test now Working --- src/app/core/config/config-fix.ts | 9 +-------- .../download-service/download.service.spec.ts | 10 +++++----- .../export/download-service/download.service.ts | 13 +------------ 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index 7ae19c81fa..efa4a5cab0 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -300,7 +300,6 @@ export const defaultJsonConfig = { { id: "DisplayParticipantsCount", viewComponent: "DisplayParticipantsCount", label: $localize`Children` }, "privateSchool", "language", - "linkedSchool" ], "filters": [ { @@ -325,8 +324,7 @@ export const defaultJsonConfig = { "fieldGroups": [ { "fields": ["name", "privateSchool"] }, { "fields": ["address", "phone"] }, - { "fields": ["language", "timing"] }, - { "fields": ["remarks", "linkedSchool"] } + { "fields": ["language", "timing"] } ] } } @@ -832,11 +830,6 @@ export const defaultJsonConfig = { "remarks": { "dataType": "string", "label": $localize`:Label for the remarks for a school:Remarks` - }, - "linkedSchool": { - "dataType": "entity", - "additional": "School", - "label": $localize`:Label for the remarks for a school:Linked School` } }, }, diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 532cd23f9b..88db2c3983 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -171,11 +171,11 @@ fdescribe("DownloadService", () => { const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); - expect(columnValues).toHaveSize(2); - // expect(columnValues).toContain('"' + testSchool.getId() + '"'); - // expect(columnValues).toContain('"' + testSchool.toString() + '"'); - // expect(columnValues).toContain('"' + testChild.getId() + '"'); - // expect(columnValues).toContain('"' + testChild.toString() + '"'); + expect(columnValues).toHaveSize(4); + expect(columnValues).toContain('"' + testSchool.getId() + ""); + expect(columnValues).toContain('"' + testSchool.toString() + ""); + expect(columnValues).toContain("" + testChild.getId() + '"'); + expect(columnValues).toContain("" + testChild.toString() + '"'); }); it("should export all properties using object keys as headers, if no schema is available", async () => { diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 7cca76cbe8..a53d496118 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -169,7 +169,6 @@ export class DownloadService { columnKeys.map((key) => item[key]), ); - console.log("Peter labels", labels); console.log("orderedData:", JSON.stringify(orderedData)); return this.papa.unparse( @@ -187,7 +186,6 @@ export class DownloadService { private async mapEntity(item: Entity, columnLabels): Promise { let newItem = {}; for (const key in item) { - console.log("Peter prüft key:", key); if (columnLabels.has(key)) { newItem[key] = item[key]; } @@ -197,23 +195,14 @@ export class DownloadService { if (Array.isArray(item[key])) { relatedEntitiesIdArray = item[key]; } else { - relatedEntitiesIdArray = [...item[key]]; + relatedEntitiesIdArray = item[key].split(); } for (let relatedEntityId of relatedEntitiesIdArray) { - console.log(" Peter ist hier", key); const type = Entity.extractTypeFromId(relatedEntityId); - console.log( - " Peter type:", - type, - "; relatedEntityId: ", - relatedEntityId, - ); let relatedEntity: Entity = await this.entityMapperService.load( type, relatedEntityId, ); - console.log("Peter entity", relatedEntity); - console.log("Peter entity.toString()", relatedEntity.toString()); relatedEntitiesToStringArray.push(relatedEntity.toString()); } newItem[key + "_readable"] = relatedEntitiesToStringArray; From 138728aa9c95ef0bf070e417c59ffc6a1924e176 Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:14:32 +0200 Subject: [PATCH 06/12] Cleaning up --- .../download-service/download.service.spec.ts | 8 +----- .../download-service/download.service.ts | 26 ------------------- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 88db2c3983..33aa13430d 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -127,7 +127,6 @@ fdescribe("DownloadService", () => { }); it("should add columns with entity toString for referenced entities in export", async () => { - @DatabaseEntity("EntityRefDownloadTestEntity") class EntityRefDownloadTestEntity extends Entity { @DatabaseField({ dataType: "entity", label: "referenced entity" }) relatedEntity: string; @@ -142,7 +141,6 @@ fdescribe("DownloadService", () => { testEntity.relatedEntity2 = relatedEntity2.getId(); const csvExport = await service.createCsv([testEntity]); - console.log("csvExport:", csvExport); const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line @@ -154,11 +152,8 @@ fdescribe("DownloadService", () => { expect(columnValues).toContain('"' + relatedEntity2.toString() + '"'); }); - fit("should add column with entity toString for referenced array of entities in export", async () => { - @DatabaseEntity("EntityRefDownloadTestEntity") + it("should add column with entity toString for referenced array of entities in export", async () => { class EntityRefDownloadTestEntity extends Entity { - // @DatabaseField({ dataType: "entity", label: "referenced entity" }) - // relatedEntity: string; @DatabaseField({ dataType: "entity-array", label: "referenced entities" }) relatedEntitiesArray: string[]; } @@ -166,7 +161,6 @@ fdescribe("DownloadService", () => { testEntity.relatedEntitiesArray = [testSchool.getId(), testChild.getId()]; const csvExport = await service.createCsv([testEntity]); - console.log("csvExport:", csvExport); const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index a53d496118..a1bd33d30f 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -8,7 +8,6 @@ import { Papa } from "ngx-papaparse"; import { EntitySchemaField } from "app/core/entity/schema/entity-schema-field"; import { Entity } from "app/core/entity/model/entity"; import { EntityDatatype } from "app/core/basic-datatypes/entity/entity.datatype"; -import { DefaultDatatype } from "app/core/entity/default-datatype/default.datatype"; import { EntityArrayDatatype } from "app/core/basic-datatypes/entity-array/entity-array.datatype"; import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; @@ -43,16 +42,6 @@ export class DownloadService { filename: string, exportConfig?: ExportColumnConfig[], ) { - console.log( - "trigger download with data: ", - data, - "; format: ", - format, - "; filename: ", - filename, - "; exportConfig: ", - exportConfig, - ); const blobData = await this.getFormattedBlobData( data, format, @@ -112,15 +101,11 @@ export class DownloadService { if (data.length > 0 && typeof data[0]?.getConstructor === "function") { entityConstructor = data[0].getConstructor(); } - console.log("entity constructor: ", entityConstructor); const keys = new Set(); - console.log("data: ", data); data.forEach((row) => Object.keys(row).forEach((key) => keys.add(key))); data = data.map(transformToReadableFormat); - console.log("data after map: ", data); - if (!entityConstructor) { return this.papa.unparse(data, { quotes: true, @@ -131,7 +116,6 @@ export class DownloadService { } const result = await this.exportFile(data, entityConstructor); - console.log("result: ", result); return result; } @@ -139,38 +123,28 @@ export class DownloadService { const entitySchema = entityConstructor.schema; const columnLabels = new Map(); - console.log("entitySchema: ", entitySchema); - entitySchema.forEach((value: EntitySchemaField, key: string) => { if (value.label) { columnLabels.set(key, value.label); if (value.dataType === EntityDatatype.dataType) { - console.log("EntityDataType bei", value.label); columnLabels.set(key + "_readable", value.label + "_readable"); } if (value.dataType === EntityArrayDatatype.dataType) { - console.log("EntityArrayDataType bei", value.label); columnLabels.set(key + "_readable", value.label + "_readable"); } } }); - console.log("columnLabels: ", columnLabels); - const exportEntities = await Promise.all( data.map(async (item) => this.mapEntity(item, columnLabels)), ); - console.log("exportEntities; ", exportEntities); - const columnKeys: string[] = Array.from(columnLabels.keys()); const labels: any[] = Array.from(columnLabels.values()); const orderedData: any[] = exportEntities.map((item) => columnKeys.map((key) => item[key]), ); - console.log("orderedData:", JSON.stringify(orderedData)); - return this.papa.unparse( { fields: labels, From 551626508344111dac35328245ebdf739d855bf3 Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:29:41 +0200 Subject: [PATCH 07/12] Simplifying code --- .../download-service/download.service.spec.ts | 2 +- .../download-service/download.service.ts | 35 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 33aa13430d..2ccfb2833a 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -13,7 +13,7 @@ import { School } from "app/child-dev-project/schools/model/school"; import { Child } from "app/child-dev-project/children/model/child"; import { mockEntityMapper } from "app/core/entity/entity-mapper/mock-entity-mapper-service"; -fdescribe("DownloadService", () => { +describe("DownloadService", () => { let service: DownloadService; let mockDataTransformationService: jasmine.SpyObj; let mockedEntityMapper; diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index a1bd33d30f..10df63c1f2 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -126,10 +126,10 @@ export class DownloadService { entitySchema.forEach((value: EntitySchemaField, key: string) => { if (value.label) { columnLabels.set(key, value.label); - if (value.dataType === EntityDatatype.dataType) { - columnLabels.set(key + "_readable", value.label + "_readable"); - } - if (value.dataType === EntityArrayDatatype.dataType) { + if ( + value.dataType === EntityDatatype.dataType || + value.dataType === EntityArrayDatatype.dataType + ) { columnLabels.set(key + "_readable", value.label + "_readable"); } } @@ -164,22 +164,21 @@ export class DownloadService { newItem[key] = item[key]; } if (columnLabels.has(key + "_readable")) { - let relatedEntitiesIdArray: string[] = []; - let relatedEntitiesToStringArray: string[] = []; - if (Array.isArray(item[key])) { - relatedEntitiesIdArray = item[key]; - } else { - relatedEntitiesIdArray = item[key].split(); - } - for (let relatedEntityId of relatedEntitiesIdArray) { - const type = Entity.extractTypeFromId(relatedEntityId); - let relatedEntity: Entity = await this.entityMapperService.load( - type, - relatedEntityId, + const relatedEntitiesIds: string[] = Array.isArray(item[key]) + ? item[key] + : item[key].split(); + let relatedEntitiesToStrings: string[] = []; + for (let relatedEntityId of relatedEntitiesIds) { + relatedEntitiesToStrings.push( + ( + await this.entityMapperService.load( + Entity.extractTypeFromId(relatedEntityId), + relatedEntityId, + ) + ).toString(), ); - relatedEntitiesToStringArray.push(relatedEntity.toString()); } - newItem[key + "_readable"] = relatedEntitiesToStringArray; + newItem[key + "_readable"] = relatedEntitiesToStrings; } } return newItem; From 19da4f77655efba2c526ee1064775964f206b342 Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:45:59 +0200 Subject: [PATCH 08/12] Small adoptions --- src/app/core/config/config-fix.ts | 2 +- src/app/core/export/download-service/download.service.ts | 4 ++-- src/environments/environment.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index efa4a5cab0..af025f3773 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -299,7 +299,7 @@ export const defaultJsonConfig = { "name", { id: "DisplayParticipantsCount", viewComponent: "DisplayParticipantsCount", label: $localize`Children` }, "privateSchool", - "language", + "language" ], "filters": [ { diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 10df63c1f2..51e1807091 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -158,7 +158,7 @@ export class DownloadService { } private async mapEntity(item: Entity, columnLabels): Promise { - let newItem = {}; + const newItem = {}; for (const key in item) { if (columnLabels.has(key)) { newItem[key] = item[key]; @@ -168,7 +168,7 @@ export class DownloadService { ? item[key] : item[key].split(); let relatedEntitiesToStrings: string[] = []; - for (let relatedEntityId of relatedEntitiesIds) { + for (const relatedEntityId of relatedEntitiesIds) { relatedEntitiesToStrings.push( ( await this.entityMapperService.load( diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 7c78ab596e..be96f460e4 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -32,7 +32,7 @@ export const environment = { remoteLoggingDsn: undefined, // only set for production mode in environment.prod.ts /** The following settings can be overridden by the `config.json` if present, see {@link AppSettings} */ demo_mode: true, - session_type: SessionType.local, + session_type: SessionType.mock, account_url: "https://accounts.aam-digital.net", email: undefined, }; From 7c489044fc1f38f3a3b3002ebb54ea46797b7bee Mon Sep 17 00:00:00 2001 From: Christoph Scheuing <47225324+christophscheuing@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:53:25 +0200 Subject: [PATCH 09/12] Re-changed config-fix.ts --- src/app/core/config/config-fix.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index af025f3773..84b2a62b3a 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -324,7 +324,8 @@ export const defaultJsonConfig = { "fieldGroups": [ { "fields": ["name", "privateSchool"] }, { "fields": ["address", "phone"] }, - { "fields": ["language", "timing"] } + { "fields": ["language", "timing"] }, + { "fields": ["remarks"] } ] } } From 77b0769af443e83e0a9383e2ff8d4c8b4e819221 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Fri, 12 Apr 2024 08:55:04 +0200 Subject: [PATCH 10/12] clarify test --- .../export/download-service/download.service.spec.ts | 9 +++------ src/app/core/export/download-service/download.service.ts | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 2ccfb2833a..4ea11b69b2 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -164,12 +164,9 @@ describe("DownloadService", () => { const rows = csvExport.split(DownloadService.SEPARATOR_ROW); expect(rows).toHaveSize(1 + 1); // includes 1 header line - const columnValues = rows[1].split(DownloadService.SEPARATOR_COL); - expect(columnValues).toHaveSize(4); - expect(columnValues).toContain('"' + testSchool.getId() + ""); - expect(columnValues).toContain('"' + testSchool.toString() + ""); - expect(columnValues).toContain("" + testChild.getId() + '"'); - expect(columnValues).toContain("" + testChild.toString() + '"'); + expect(rows[1]).toBe( + `"${testSchool.getId()},${testChild.getId()}","${testSchool.toString()},${testChild.toString()}"`, + ); }); it("should export all properties using object keys as headers, if no schema is available", async () => { diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 51e1807091..e67ea61380 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -136,7 +136,7 @@ export class DownloadService { }); const exportEntities = await Promise.all( - data.map(async (item) => this.mapEntity(item, columnLabels)), + data.map((item) => this.mapEntity(item, columnLabels)), ); const columnKeys: string[] = Array.from(columnLabels.keys()); From caf5fed645481545cc409d0f216bbb6508628aaa Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Fri, 12 Apr 2024 08:58:02 +0200 Subject: [PATCH 11/12] handle invalid ids --- .../download-service/download.service.spec.ts | 17 +++++++++++++++++ .../export/download-service/download.service.ts | 10 ++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 4ea11b69b2..b2b34c4727 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -169,6 +169,23 @@ describe("DownloadService", () => { ); }); + it("should handle undefined entity ids without errors", async () => { + class EntityRefDownloadTestEntity extends Entity { + @DatabaseField({ dataType: "entity-array", label: "referenced entities" }) + relatedEntitiesArray: string[]; + } + const testEntity = new EntityRefDownloadTestEntity(); + testEntity.relatedEntitiesArray = ["undefined-id", testChild.getId()]; + + const csvExport = await service.createCsv([testEntity]); + + const rows = csvExport.split(DownloadService.SEPARATOR_ROW); + expect(rows).toHaveSize(1 + 1); // includes 1 header line + expect(rows[1]).toBe( + `"undefined-id,${testChild.getId()}",",${testChild.toString()}"`, + ); + }); + it("should export all properties using object keys as headers, if no schema is available", async () => { const docs = [ { _id: "Test:1", name: "Child 1" }, diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index e67ea61380..9e204964b2 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -171,10 +171,12 @@ export class DownloadService { for (const relatedEntityId of relatedEntitiesIds) { relatedEntitiesToStrings.push( ( - await this.entityMapperService.load( - Entity.extractTypeFromId(relatedEntityId), - relatedEntityId, - ) + await this.entityMapperService + .load( + Entity.extractTypeFromId(relatedEntityId), + relatedEntityId, + ) + .catch((e) => "") ).toString(), ); } From 3fa756a605b229b205b4a1b813174ac39bab5b3a Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Fri, 12 Apr 2024 09:25:18 +0200 Subject: [PATCH 12/12] small code cleanup --- src/app/core/config/config-fix.ts | 5 -- .../download-service/download.service.ts | 74 +++++++++++-------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index 84b2a62b3a..e1c478ec17 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -714,11 +714,6 @@ export const defaultJsonConfig = { "title", "type", "assignedTo" - ], - "exportConfig": [ - { "label": "Title", "query": "title" }, - { "label": "Type", "query": "type" }, - { "label": "Assigned users", "query": "assignedTo" } ] } }, diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index 9e204964b2..f69d8db225 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -5,8 +5,7 @@ import { LoggingService } from "../../logging/logging.service"; import { DataTransformationService } from "../data-transformation-service/data-transformation.service"; import { transformToReadableFormat } from "../../common-components/entities-table/value-accessor/value-accessor"; import { Papa } from "ngx-papaparse"; -import { EntitySchemaField } from "app/core/entity/schema/entity-schema-field"; -import { Entity } from "app/core/entity/model/entity"; +import { Entity, EntityConstructor } from "app/core/entity/model/entity"; import { EntityDatatype } from "app/core/basic-datatypes/entity/entity.datatype"; import { EntityArrayDatatype } from "app/core/basic-datatypes/entity-array/entity-array.datatype"; import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; @@ -119,24 +118,28 @@ export class DownloadService { return result; } - async exportFile(data: any[], entityConstructor: { schema: any }) { + async exportFile(data: any[], entityConstructor: EntityConstructor) { const entitySchema = entityConstructor.schema; const columnLabels = new Map(); - entitySchema.forEach((value: EntitySchemaField, key: string) => { - if (value.label) { - columnLabels.set(key, value.label); - if ( - value.dataType === EntityDatatype.dataType || - value.dataType === EntityArrayDatatype.dataType - ) { - columnLabels.set(key + "_readable", value.label + "_readable"); - } + for (const [id, field] of entitySchema.entries()) { + if (!field.label) { + // skip "technical" fields without an explicit label + continue; } - }); + + columnLabels.set(id, field.label); + + if ( + field.dataType === EntityDatatype.dataType || + field.dataType === EntityArrayDatatype.dataType + ) { + columnLabels.set(id + "_readable", field.label + " (readable)"); + } + } const exportEntities = await Promise.all( - data.map((item) => this.mapEntity(item, columnLabels)), + data.map((item) => this.mapEntityToExportRow(item, columnLabels)), ); const columnKeys: string[] = Array.from(columnLabels.keys()); @@ -157,32 +160,39 @@ export class DownloadService { ); } - private async mapEntity(item: Entity, columnLabels): Promise { + private async mapEntityToExportRow( + item: Entity, + columnLabels: Map, + ): Promise { const newItem = {}; for (const key in item) { if (columnLabels.has(key)) { newItem[key] = item[key]; } + if (columnLabels.has(key + "_readable")) { - const relatedEntitiesIds: string[] = Array.isArray(item[key]) - ? item[key] - : item[key].split(); - let relatedEntitiesToStrings: string[] = []; - for (const relatedEntityId of relatedEntitiesIds) { - relatedEntitiesToStrings.push( - ( - await this.entityMapperService - .load( - Entity.extractTypeFromId(relatedEntityId), - relatedEntityId, - ) - .catch((e) => "") - ).toString(), - ); - } - newItem[key + "_readable"] = relatedEntitiesToStrings; + newItem[key + "_readable"] = await this.loadRelatedEntitiesToString( + item[key], + ); } } return newItem; } + + private async loadRelatedEntitiesToString(value: string | string[]) { + const relatedEntitiesToStrings: string[] = []; + + const relatedEntitiesIds: string[] = Array.isArray(value) ? value : [value]; + for (const relatedEntityId of relatedEntitiesIds) { + relatedEntitiesToStrings.push( + ( + await this.entityMapperService + .load(Entity.extractTypeFromId(relatedEntityId), relatedEntityId) + .catch((e) => "") + ).toString(), + ); + } + + return relatedEntitiesToStrings; + } }