diff --git a/package-lock.json b/package-lock.json
index 04fcb069ad..6f62973c84 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
+ "name": "ndb-core",
"version": "0.0.0",
"hasInstallScript": true,
"license": "GPL-3.0",
@@ -33,20 +34,16 @@
"deep-object-diff": "^1.1.0",
"faker": "^5.5.3",
"flag-icon-css": "^3.5.0",
- "idb": "^6.1.5",
"json-query": "^2.2.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"moment": "^2.29.1",
- "ngx-filter-pipe": "^2.1.2",
"ngx-markdown": "^11.1.3",
"ngx-papaparse": "^5.0.0",
"pouchdb-adapter-memory": "^7.2.2",
"pouchdb-browser": "^7.2.2",
"reflect-metadata": "^0.1.13",
- "reveal": "0.0.4",
"rxjs": "^6.6.7",
- "stream": "0.0.2",
"tslib": "^2.3.1",
"uuid": "^8.3.2",
"webdav": "^4.7.0",
@@ -13410,11 +13407,6 @@
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
},
- "node_modules/emitter-component": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
- "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY="
- },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -16657,11 +16649,6 @@
"postcss": "^8.1.0"
}
},
- "node_modules/idb": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz",
- "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw=="
- },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -20574,22 +20561,6 @@
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
- "node_modules/ngx-filter-pipe": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ngx-filter-pipe/-/ngx-filter-pipe-2.1.2.tgz",
- "integrity": "sha512-YEXvjEw+Mpg5jL+yqSnFWKiY0P9XtRAJ2Dk3n9sC4stnsuhPzPRwIkF58aBvqYfoi3vrb7KQFImgbmfFAQqnFw==",
- "dependencies": {
- "tslib": "^1.7.1"
- },
- "peerDependencies": {
- "@angular/core": ">=5.0.0"
- }
- },
- "node_modules/ngx-filter-pipe/node_modules/tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
- },
"node_modules/ngx-i18nsupport": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/ngx-i18nsupport/-/ngx-i18nsupport-0.17.1.tgz",
@@ -24805,11 +24776,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/reveal": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/reveal/-/reveal-0.0.4.tgz",
- "integrity": "sha1-HBQoGTNZ+wO8ApE5Bi6in49pHYU="
- },
"node_modules/rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -26818,14 +26784,6 @@
"react-dom": "^16.8.0 || ^17.0.0"
}
},
- "node_modules/stream": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
- "integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=",
- "dependencies": {
- "emitter-component": "^1.1.1"
- }
- },
"node_modules/stream-browserify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
@@ -42115,11 +42073,6 @@
}
}
},
- "emitter-component": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
- "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY="
- },
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -44718,11 +44671,6 @@
"dev": true,
"requires": {}
},
- "idb": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz",
- "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw=="
- },
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -47733,21 +47681,6 @@
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
- "ngx-filter-pipe": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ngx-filter-pipe/-/ngx-filter-pipe-2.1.2.tgz",
- "integrity": "sha512-YEXvjEw+Mpg5jL+yqSnFWKiY0P9XtRAJ2Dk3n9sC4stnsuhPzPRwIkF58aBvqYfoi3vrb7KQFImgbmfFAQqnFw==",
- "requires": {
- "tslib": "^1.7.1"
- },
- "dependencies": {
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
- }
- }
- },
"ngx-i18nsupport": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/ngx-i18nsupport/-/ngx-i18nsupport-0.17.1.tgz",
@@ -51015,11 +50948,6 @@
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true
},
- "reveal": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/reveal/-/reveal-0.0.4.tgz",
- "integrity": "sha1-HBQoGTNZ+wO8ApE5Bi6in49pHYU="
- },
"rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -52648,14 +52576,6 @@
"ts-dedent": "^2.1.1"
}
},
- "stream": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
- "integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=",
- "requires": {
- "emitter-component": "^1.1.1"
- }
- },
"stream-browserify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
diff --git a/package.json b/package.json
index 1758cd39eb..39fbcd1e0d 100644
--- a/package.json
+++ b/package.json
@@ -47,20 +47,16 @@
"deep-object-diff": "^1.1.0",
"faker": "^5.5.3",
"flag-icon-css": "^3.5.0",
- "idb": "^6.1.5",
"json-query": "^2.2.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"moment": "^2.29.1",
- "ngx-filter-pipe": "^2.1.2",
"ngx-markdown": "^11.1.3",
"ngx-papaparse": "^5.0.0",
"pouchdb-adapter-memory": "^7.2.2",
"pouchdb-browser": "^7.2.2",
"reflect-metadata": "^0.1.13",
- "reveal": "0.0.4",
"rxjs": "^6.6.7",
- "stream": "0.0.2",
"tslib": "^2.3.1",
"uuid": "^8.3.2",
"webdav": "^4.7.0",
diff --git a/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.spec.ts b/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.spec.ts
deleted file mode 100644
index 7d27e58fea..0000000000
--- a/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.spec.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import { TestBed } from "@angular/core/testing";
-import { AttendanceMigrationService } from "./attendance-migration.service";
-import { AttendanceMonth } from "../model/attendance-month";
-import { AttendanceStatus } from "../model/attendance-status";
-import { Database } from "../../../core/database/database";
-import { EntitySchemaService } from "../../../core/entity/schema/entity-schema.service";
-import { defaultAttendanceStatusTypes } from "../../../core/config/default-config/default-attendance-status-types";
-import { EntityModule } from "../../../core/entity/entity.module";
-import { expectEntitiesToMatch } from "../../../utils/expect-entity-data.spec";
-import { EventNote } from "../model/event-note";
-import { ChildrenService } from "../../children/children.service";
-import { PouchDatabase } from "../../../core/database/pouch-database";
-
-describe("AttendanceMigrationService", () => {
- let service: AttendanceMigrationService;
- let entitySchemaService: EntitySchemaService;
- let testDatabase: PouchDatabase;
-
- beforeEach(async () => {
- testDatabase = PouchDatabase.createWithInMemoryDB();
-
- TestBed.configureTestingModule({
- imports: [EntityModule],
- providers: [
- AttendanceMigrationService,
- {
- provide: ChildrenService,
- useValue: jasmine.createSpyObj(["queryRelationsOf"]),
- },
- { provide: Database, useValue: testDatabase },
- ],
- });
- service = TestBed.inject(AttendanceMigrationService);
- entitySchemaService = TestBed.inject(EntitySchemaService);
- });
-
- afterEach(async () => {
- await testDatabase.destroy();
- });
-
- it("should create events for each existing attendance-day in attendance-month", async () => {
- const testChild = "1";
- const testInstitution = "school";
-
- const old = AttendanceMonth.createAttendanceMonth(
- testChild,
- testInstitution
- );
- old.month = new Date("2020-01-01");
- old.dailyRegister[4].status = AttendanceStatus.EXCUSED;
- old.dailyRegister[4].remarks = "some remark";
- old.dailyRegister[5].status = AttendanceStatus.ABSENT;
- old.dailyRegister[7].status = AttendanceStatus.PRESENT;
-
- const expectedNotes: EventNote[] = [
- EventNote.create(old.dailyRegister[4].date, "School"),
- EventNote.create(old.dailyRegister[5].date, "School"),
- EventNote.create(old.dailyRegister[7].date, "School"),
- ];
- for (const event of expectedNotes) {
- event.category = service.activities.school.type;
- event.relatesTo = service.activities.school._id;
- event.children = [testChild];
- }
- expectedNotes[0].getAttendance(
- testChild
- ).status = defaultAttendanceStatusTypes.find((t) => t.shortName === "E");
- expectedNotes[0].getAttendance(testChild).remarks = "some remark";
- expectedNotes[1].getAttendance(
- testChild
- ).status = defaultAttendanceStatusTypes.find((t) => t.shortName === "A");
- expectedNotes[2].getAttendance(
- testChild
- ).status = defaultAttendanceStatusTypes.find((t) => t.shortName === "P");
-
- await service.createEventsForAttendanceMonth(old);
-
- await expectEntitiesToMatch(service.existingEvents, expectedNotes, true);
- });
-
- it("should add to existing event for the same activity and date", async () => {
- const testChild1 = "1";
- const testChild2 = "2";
- const testInstitution = "school";
-
- const old1 = AttendanceMonth.createAttendanceMonth(
- testChild1,
- testInstitution
- );
- old1.month = new Date("2020-01-01");
- old1.dailyRegister[0].status = AttendanceStatus.ABSENT;
-
- const old2 = AttendanceMonth.createAttendanceMonth(
- testChild2,
- testInstitution
- );
- old2.month = new Date("2020-01-01");
- old2.dailyRegister[0].status = AttendanceStatus.PRESENT;
-
- const expectedNotes: EventNote[] = [
- EventNote.create(old1.dailyRegister[0].date, "School"),
- ];
- for (const event of expectedNotes) {
- event.category = service.activities.school.type;
- event.relatesTo = service.activities.school._id;
- event.children = [testChild1, testChild2];
- }
- expectedNotes[0].getAttendance(
- testChild1
- ).status = defaultAttendanceStatusTypes.find((t) => t.shortName === "A");
- expectedNotes[0].getAttendance(
- testChild2
- ).status = defaultAttendanceStatusTypes.find((t) => t.shortName === "P");
-
- await service.createEventsForAttendanceMonth(old1);
- await service.createEventsForAttendanceMonth(old2);
-
- await expectEntitiesToMatch(service.existingEvents, expectedNotes, true);
- });
-});
diff --git a/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.ts b/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.ts
deleted file mode 100644
index 295942938f..0000000000
--- a/src/app/child-dev-project/attendance/attendance-migration/attendance-migration.service.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-import { Injectable } from "@angular/core";
-import { EntityMapperService } from "../../../core/entity/entity-mapper.service";
-import { RecurringActivity } from "../model/recurring-activity";
-import { AttendanceMonth } from "../model/attendance-month";
-import { defaultInteractionTypes } from "../../../core/config/default-config/default-interaction-types";
-import { AttendanceStatus } from "../model/attendance-status";
-import { defaultAttendanceStatusTypes } from "../../../core/config/default-config/default-attendance-status-types";
-import { AttendanceService } from "../attendance.service";
-import moment from "moment";
-import { EventNote } from "../model/event-note";
-import { Note } from "../../notes/model/note";
-
-@Injectable({
- providedIn: "root",
-})
-export class AttendanceMigrationService {
- activities: { [key: string]: RecurringActivity } = {
- school: Object.assign(new RecurringActivity("school"), {
- entityId: "school",
- title: $localize`School`,
- type: defaultInteractionTypes.find((t) => t.id === "SCHOOL_CLASS"),
- }),
- coaching: Object.assign(new RecurringActivity("coaching"), {
- entityId: "coaching",
- title: $localize`Coaching`,
- type: defaultInteractionTypes.find((t) => t.id === "COACHING_CLASS"),
- }),
- };
-
- existingEvents: EventNote[] = [];
-
- constructor(
- private entityMapper: EntityMapperService,
- private attendanceService: AttendanceService
- ) {}
-
- async createEventsForAllAttendanceMonths() {
- await this.checkOrCreateActivities();
-
- this.existingEvents = await this.entityMapper.loadType(EventNote);
-
- const months = await this.entityMapper.loadType(AttendanceMonth);
- console.log(
- "starting to migrate attendance-month records:" + months.length
- );
- for (let i = 0; i < months.length; i++) {
- await this.createEventsForAttendanceMonth(months[i]);
- if (i % 100 === 1) {
- console.log("finished " + (i + 1));
- }
- }
- console.log("parsed all AttendanceMonths");
-
- for (const e of this.existingEvents) {
- await this.entityMapper.save(e);
- }
- console.log("wrote all events to database");
-
- console.log("updating activity participants");
- await this.addStudentsToActivityForAllAttendanceMonths();
- console.log("DONE");
- }
-
- async addStudentsToActivityForAllAttendanceMonths() {
- await this.checkOrCreateActivities();
-
- const months = await this.entityMapper.loadType(AttendanceMonth);
- for (const month of months) {
- if (!this.activities.hasOwnProperty(month.institution)) {
- console.warn(
- "cannot migrate attendance month because of unknown institution",
- month
- );
- continue;
- }
-
- this.activities[month.institution].participants.push(month.student);
- }
-
- const unique = (value, index, self) => {
- return self.indexOf(value) === index;
- };
- for (const activity of Object.values(this.activities)) {
- activity.participants = activity.participants.filter(unique);
- await this.entityMapper.save(activity);
- }
- }
-
- private async checkOrCreateActivities() {
- for (const key of Object.keys(this.activities)) {
- this.activities[key] = await this.entityMapper
- .load(RecurringActivity, this.activities[key].getId())
- .catch(async (err) => {
- if (err.status === 404) {
- await this.entityMapper.save(this.activities[key]);
- return this.entityMapper.load(
- RecurringActivity,
- this.activities[key].getId()
- );
- } else {
- throw err;
- }
- });
- }
- }
- async createEventsForAttendanceMonth(old: AttendanceMonth) {
- if (!this.activities.hasOwnProperty(old.institution)) {
- console.warn(
- "cannot migrate attendance month because of unknown institution",
- old
- );
- return;
- }
-
- const existingActivityEvents = this.existingEvents.filter(
- (e) => e.relatesTo === this.activities[old.institution]._id
- );
-
- for (const day of old.dailyRegister) {
- if (day.status === AttendanceStatus.UNKNOWN) {
- // skip status without actual information
- continue;
- }
-
- let newEvent = existingActivityEvents.find((e) =>
- moment(e.date).isSame(day.date, "day")
- );
- if (!newEvent) {
- // no Note in the database yet - create a new event
- newEvent = await this.attendanceService.createEventForActivity(
- this.activities[old.institution],
- day.date
- );
- newEvent.children = [];
- this.existingEvents.push(newEvent);
- }
-
- newEvent.children.push(old.student);
- newEvent.getAttendance(
- old.student
- ).status = defaultAttendanceStatusTypes.find(
- (t) => t.shortName === day.status
- );
- newEvent.getAttendance(old.student).remarks = day.remarks;
- }
- }
-
- async changeNotesToEventNotes() {
- const oldEventNotes = (await this.entityMapper.loadType(Note)).filter(
- (n) => n.relatesTo
- );
-
- for (const note of oldEventNotes) {
- const newEvent = new EventNote(note.getId());
- newEvent.date = note.date;
- newEvent.subject = note.subject;
- newEvent.children = note.children;
- newEvent.relatesTo = note.relatesTo;
- newEvent.category = note.category;
- // @ts-ignore
- newEvent.childrenAttendance = note.childrenAttendance;
-
- await this.entityMapper.remove(note);
- await this.entityMapper.save(newEvent);
- console.log("event-note migrated", newEvent.getId());
- }
- }
-}
diff --git a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts
index 6e0b47bdd7..c8b2711c3c 100644
--- a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts
+++ b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts
@@ -9,7 +9,7 @@ import { User } from "../../../core/user/user";
import { defaultInteractionTypes } from "../../../core/config/default-config/default-interaction-types";
/**
- * Generate AttendanceMonth entities for the last 15 months
+ * Generate RecurringActivity entities
* Builds upon the generated demo Child entities.
*/
@Injectable()
diff --git a/src/app/child-dev-project/attendance/model/attendance-day.spec.ts b/src/app/child-dev-project/attendance/model/attendance-day.spec.ts
deleted file mode 100644
index 3daabfa842..0000000000
--- a/src/app/child-dev-project/attendance/model/attendance-day.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * This file is part of ndb-core.
- *
- * ndb-core is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ndb-core is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with ndb-core. If not, see .
- */
-
-import { AttendanceDay } from "./attendance-day";
-import { waitForAsync } from "@angular/core/testing";
-import { EntitySchemaService } from "../../../core/entity/schema/entity-schema.service";
-import { AttendanceMonth } from "./attendance-month";
-
-describe("AttendanceDay", () => {
- let entitySchemaService: EntitySchemaService;
-
- beforeEach(
- waitForAsync(() => {
- entitySchemaService = new EntitySchemaService();
- })
- );
-
- it("(AttendanceMonth) saves date values as only YYYY-MM-dd", () => {
- const month = new Date("2018-01-01");
- const entity = new AttendanceMonth("");
- entity.month = month;
-
- const data = entitySchemaService.transformEntityToDatabaseFormat(entity);
- expect(data.dailyRegister[1].date).toBe("2018-01-02"); // dailyRegister array is zero-based, index 1 is second day
-
- const loadedEntity = new AttendanceMonth("");
- entitySchemaService.loadDataIntoEntity(loadedEntity, data);
- expect(loadedEntity.dailyRegister[1].date).toEqual(new Date("2018-01-02"));
- });
-});
diff --git a/src/app/child-dev-project/attendance/model/attendance-day.ts b/src/app/child-dev-project/attendance/model/attendance-day.ts
deleted file mode 100644
index ee37929aa2..0000000000
--- a/src/app/child-dev-project/attendance/model/attendance-day.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * This file is part of ndb-core.
- *
- * ndb-core is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ndb-core is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with ndb-core. If not, see .
- */
-
-import { DatabaseField } from "../../../core/entity/database-field.decorator";
-import { AttendanceStatus } from "./attendance-status";
-
-/**
- * @deprecated Use Event entities instead of the embedded AttendanceDay and AttendanceMonth
- */
-export class AttendanceDay {
- @DatabaseField({ dataType: "date-only" }) date: Date;
- @DatabaseField() status: AttendanceStatus;
- @DatabaseField() remarks: string = "";
-
- constructor(date: Date, status: AttendanceStatus = AttendanceStatus.UNKNOWN) {
- this.date = date;
- this.status = status;
- }
-}
diff --git a/src/app/child-dev-project/attendance/model/attendance-month.spec.ts b/src/app/child-dev-project/attendance/model/attendance-month.spec.ts
deleted file mode 100644
index 4393506d2f..0000000000
--- a/src/app/child-dev-project/attendance/model/attendance-month.spec.ts
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * This file is part of ndb-core.
- *
- * ndb-core is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ndb-core is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with ndb-core. If not, see .
- */
-
-import { AttendanceMonth, daysInMonth } from "./attendance-month";
-import { waitForAsync } from "@angular/core/testing";
-import { Entity } from "../../../core/entity/model/entity";
-import { EntitySchemaService } from "../../../core/entity/schema/entity-schema.service";
-import { WarningLevel } from "../../../core/entity/model/warning-level";
-
-describe("AttendanceMonth", () => {
- const ENTITY_TYPE = "AttendanceMonth";
- let entitySchemaService: EntitySchemaService;
-
- beforeEach(
- waitForAsync(() => {
- entitySchemaService = new EntitySchemaService();
- })
- );
-
- it("has correct _id and entityId and type", function () {
- const id = "test1";
- const entity = new AttendanceMonth(id);
-
- expect(entity.getId()).toBe(id);
- expect(Entity.extractEntityIdFromId(entity._id)).toBe(id);
- });
-
- it("has correct type/prefix", function () {
- const id = "test1";
- const entity = new AttendanceMonth(id);
-
- expect(entity.getType()).toBe(ENTITY_TYPE);
- expect(Entity.extractTypeFromId(entity._id)).toBe(ENTITY_TYPE);
- });
-
- it("has all and only defined schema fields in rawData", function () {
- const id = "test1";
- const expectedData = {
- _id: ENTITY_TYPE + ":" + id,
-
- student: "1",
- institution: "school",
- month: "2019-01",
- remarks: "more notes",
- daysWorking: 25,
- daysAttended: 20,
- daysExcused: 1,
- daysLate: 1,
- dailyRegister: [],
-
- searchIndices: [],
- };
-
- const entity = new AttendanceMonth(id);
- entity.student = expectedData.student;
- entity.institution = expectedData.institution;
- entity.month = new Date(expectedData.month);
- entity.remarks = expectedData.remarks;
- entity.daysWorking = expectedData.daysWorking;
- entity.daysAttended = expectedData.daysAttended;
- entity.daysExcused = expectedData.daysExcused;
- entity.daysLate = expectedData.daysLate;
-
- const rawData = entitySchemaService.transformEntityToDatabaseFormat(entity);
-
- expect(rawData.dailyRegister.length).toBe(31);
- expectedData.dailyRegister = rawData.dailyRegister; // simplify the overall comparison by ignoring dailyRegister diff
- expect(rawData).toEqual(expectedData);
- });
-
- it("calculates attendance percentage", () => {
- const working = 10;
- const attended = 1;
-
- const entity = new AttendanceMonth("");
- entity.month = new Date("2018-01-01");
- entity.daysWorking = working;
- entity.daysAttended = attended;
-
- expect(entity.getAttendancePercentage()).toBe(attended / working);
- });
-
- it("gives WarningLevel for low attendance", () => {
- const working = 10;
- const attended = 1;
-
- const entity = new AttendanceMonth("");
- entity.month = new Date("2018-01-01");
- entity.daysWorking = working;
- entity.daysAttended = attended;
-
- expect(entity.getWarningLevel()).toBe(WarningLevel.URGENT);
- });
-
- it("has dailyRegister array after creation", () => {
- const entity = new AttendanceMonth("");
-
- expect(entity.dailyRegister.length).toBeGreaterThan(-1);
- });
-
- it("adds/removes dailyRegister entries on month change", () => {
- const month = new Date("2018-01-01");
-
- const entity = new AttendanceMonth("");
- entity.month = month;
-
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
- });
-
- it("adds/removes dailyRegister entries on load", () => {
- const month = new Date("2018-01-01");
-
- const entity = new AttendanceMonth("");
- const data = { month: month, daysWorking: 10, daysAttended: 7 };
- entitySchemaService.loadDataIntoEntity(entity, data);
-
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
- });
-
- it("updates dailyRegister entries' date on month change (shorter)", () => {
- const month = new Date("2018-01-01");
- const month2 = new Date("2018-02-01");
-
- const entity = new AttendanceMonth("");
-
- entity.month = month;
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
-
- entity.month = month2;
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
- for (let i = 1; i <= daysInMonth(entity.month); i++) {
- expect(entity.dailyRegister[i - 1].date).toEqual(
- new Date(entity.month.getFullYear(), entity.month.getMonth(), i)
- );
- }
- });
-
- it("updates dailyRegister entries' date on month change (longer)", () => {
- const month = new Date("2018-02-01");
- const month2 = new Date("2018-01-01");
-
- const entity = new AttendanceMonth("");
-
- entity.month = month;
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
-
- entity.month = month2;
- expect(entity.dailyRegister.length).toBe(daysInMonth(entity.month));
- for (let i = 1; i <= daysInMonth(entity.month); i++) {
- expect(entity.dailyRegister[i - 1].date).toEqual(
- new Date(entity.month.getFullYear(), entity.month.getMonth(), i)
- );
- }
- });
-
- it("saves & loads manually entered attendance values", () => {
- const originalData = {
- month: new Date("2018-01-01"),
- daysWorking: 10,
- daysAttended: 7,
- daysExcused: 2,
- daysLate: 4,
- };
- const entity = new AttendanceMonth("");
-
- entitySchemaService.loadDataIntoEntity(entity, originalData);
- expect(entity.daysWorking).toBe(originalData.daysWorking);
- expect(entity.daysAttended).toBe(originalData.daysAttended);
- expect(entity.daysExcused).toBe(originalData.daysExcused);
- expect(entity.daysLate).toBe(originalData.daysLate);
-
- const data = entitySchemaService.transformEntityToDatabaseFormat(entity);
- expect(data.daysWorking).toBe(originalData.daysWorking);
- expect(data.daysAttended).toBe(originalData.daysAttended);
- expect(data.daysExcused).toBe(originalData.daysExcused);
- expect(data.daysLate).toBe(originalData.daysLate);
- });
-
- it("returns month as string in rawData", () => {
- const month = new Date("2018-01-01");
- const entity = new AttendanceMonth("");
- entity.month = month;
-
- const rawData = entitySchemaService.transformEntityToDatabaseFormat(entity);
- expect(typeof rawData.month).toBe("string");
- expect(rawData.month).toBe("2018-01");
- expect(rawData.p_month).toBeUndefined();
- });
-
- it("loads month as date from rawData", () => {
- const data = {
- month: "2018-1",
- };
- const entity = new AttendanceMonth("");
-
- entitySchemaService.loadDataIntoEntity(entity, data);
- expect(entity.month).toEqual(new Date(data.month));
- });
-
- it("creates correct instance using static createAttendanceMonth", () => {
- const testInstitution = "coaching";
- const testChildId = "childId1";
- const now = new Date();
-
- const actualInstance = AttendanceMonth.createAttendanceMonth(
- testChildId,
- testInstitution
- );
-
- expect(actualInstance.student).toBe(testChildId);
- expect(actualInstance.institution).toBe(testInstitution);
- expect(actualInstance.month.getFullYear()).toBe(now.getFullYear());
- expect(actualInstance.month.getMonth()).toBe(now.getMonth());
-
- expect(actualInstance.getId()).toContain(testChildId);
- expect(actualInstance.getId()).toContain(testInstitution);
- expect(actualInstance.getId()).toContain(
- now.getFullYear() + "-" + (now.getMonth() + 1)
- );
- });
-});
diff --git a/src/app/child-dev-project/attendance/model/attendance-month.ts b/src/app/child-dev-project/attendance/model/attendance-month.ts
deleted file mode 100644
index bf0fbe58ff..0000000000
--- a/src/app/child-dev-project/attendance/model/attendance-month.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * This file is part of ndb-core.
- *
- * ndb-core is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ndb-core is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with ndb-core. If not, see .
- */
-
-import { Entity } from "../../../core/entity/model/entity";
-import { AttendanceDay } from "./attendance-day";
-import { DatabaseEntity } from "../../../core/entity/database-entity.decorator";
-import { DatabaseField } from "../../../core/entity/database-field.decorator";
-import { AttendanceStatus } from "./attendance-status";
-import { WarningLevel } from "../../../core/entity/model/warning-level";
-
-/**
- * @deprecated Use new system based on EventNote and RecurrentActivity instead
- */
-@DatabaseEntity("AttendanceMonth")
-export class AttendanceMonth extends Entity {
- static readonly THRESHOLD_URGENT = 0.6;
- static readonly THRESHOLD_WARNING = 0.8;
-
- public static createAttendanceMonth(childId: string, institution: string) {
- const month = new Date();
- const newAtt = new AttendanceMonth(
- childId +
- "_" +
- month.getFullYear() +
- "-" +
- (month.getMonth() + 1) +
- "_" +
- institution
- );
- newAtt.month = month;
- newAtt.student = childId;
- newAtt.institution = institution;
- return newAtt;
- }
-
- @DatabaseField() student: string; // id of Child entity
- @DatabaseField() remarks: string = "";
- @DatabaseField() institution: string;
-
- private p_month: Date;
- @DatabaseField({ dataType: "month" })
- get month(): Date {
- return this.p_month;
- }
- set month(value: Date) {
- if (!(value instanceof Date)) {
- console.warn(
- "Trying to set invalid date " +
- JSON.stringify(value) +
- " to Entity " +
- this._id
- );
- return;
- }
-
- if (value.getDate() !== 2) {
- value.setDate(2);
- }
- this.p_month = new Date(value);
- this.updateDailyRegister();
- }
-
- daysWorking_manuallyEntered: number;
- @DatabaseField()
- get daysWorking(): number {
- if (this.daysWorking_manuallyEntered !== undefined) {
- return this.daysWorking_manuallyEntered;
- } else {
- return this.getDaysWorkingFromDailyAttendance();
- }
- }
-
- set daysWorking(value: number) {
- this.daysWorking_manuallyEntered = value;
- }
-
- daysAttended_manuallyEntered: number;
- @DatabaseField()
- get daysAttended(): number {
- if (this.daysAttended_manuallyEntered !== undefined) {
- return this.daysAttended_manuallyEntered;
- } else {
- return this.getDaysAttendedFromDailyAttendance();
- }
- }
-
- set daysAttended(value: number) {
- this.daysAttended_manuallyEntered = value;
- }
-
- daysExcused_manuallyEntered: number;
- @DatabaseField()
- get daysExcused(): number {
- if (this.daysExcused_manuallyEntered !== undefined) {
- return this.daysExcused_manuallyEntered;
- } else {
- return this.getDaysExcusedFromDailyAttendance();
- }
- }
-
- set daysExcused(value: number) {
- this.daysExcused_manuallyEntered = value;
- }
-
- daysLate_manuallyEntered: number;
- @DatabaseField()
- get daysLate(): number {
- if (this.daysLate_manuallyEntered !== undefined) {
- return this.daysLate_manuallyEntered;
- } else {
- return this.calculateFromDailyRegister(AttendanceStatus.LATE);
- }
- }
- set daysLate(value: number) {
- this.daysLate_manuallyEntered = value;
- }
-
- overridden = false; // indicates individual override during bulk adding
-
- private _dailyRegister = new Array();
- set dailyRegister(value: AttendanceDay[]) {
- if (!value) {
- return;
- }
-
- for (const attDay of value) {
- if (typeof attDay.date.getTime !== "function") {
- attDay.date = new Date(attDay.date);
- }
- }
- this._dailyRegister = value;
- }
- @DatabaseField({ innerDataType: "schema-embed", additional: AttendanceDay })
- get dailyRegister(): AttendanceDay[] {
- return this._dailyRegister;
- }
-
- constructor(id: string) {
- super(id);
- this.month = new Date();
- }
-
- private updateDailyRegister() {
- if (this.month === undefined) {
- return;
- }
-
- if (this.dailyRegister === undefined) {
- this.dailyRegister = new Array();
- }
-
- const expectedDays = daysInMonth(this.month);
- const currentDays = this.dailyRegister.length;
- if (currentDays < expectedDays) {
- for (let i = currentDays + 1; i <= expectedDays; i++) {
- const date = new Date(
- this.month.getFullYear(),
- this.month.getMonth(),
- i
- );
- const day = new AttendanceDay(date);
- this.dailyRegister.push(day);
- }
- } else if (currentDays > expectedDays) {
- this.dailyRegister.splice(expectedDays);
- }
-
- this.dailyRegister.forEach((day) => {
- day.date.setMonth(this.month.getMonth());
- day.date.setFullYear(this.month.getFullYear());
- });
- }
-
- private calculateFromDailyRegister(status: AttendanceStatus) {
- let count = 0;
- this.dailyRegister.forEach((day) => {
- if (day.status === status) {
- count++;
- }
- });
- return count;
- }
-
- public getDaysWorkingFromDailyAttendance() {
- return (
- this.dailyRegister.length -
- this.calculateFromDailyRegister(AttendanceStatus.HOLIDAY) -
- this.calculateFromDailyRegister(AttendanceStatus.UNKNOWN)
- );
- }
-
- public getDaysAttendedFromDailyAttendance() {
- return (
- this.calculateFromDailyRegister(AttendanceStatus.PRESENT) +
- this.calculateFromDailyRegister(AttendanceStatus.LATE)
- );
- }
-
- public getDaysExcusedFromDailyAttendance() {
- return this.calculateFromDailyRegister(AttendanceStatus.EXCUSED);
- }
-
- public getDaysLateFromDailyAttendance() {
- return this.calculateFromDailyRegister(AttendanceStatus.LATE);
- }
-
- getAttendancePercentage() {
- return this.daysAttended / (this.daysWorking - this.daysExcused);
- }
-
- getWarningLevel(): WarningLevel {
- const attendance = this.getAttendancePercentage();
- if (attendance < AttendanceMonth.THRESHOLD_URGENT) {
- return WarningLevel.URGENT;
- } else if (attendance < AttendanceMonth.THRESHOLD_WARNING) {
- return WarningLevel.WARNING;
- } else {
- return WarningLevel.OK;
- }
- }
-}
-
-export function daysInMonth(date: Date) {
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
-}
diff --git a/src/app/child-dev-project/attendance/model/attendance-status.ts b/src/app/child-dev-project/attendance/model/attendance-status.ts
index 4e714c3c95..64958e15e1 100644
--- a/src/app/child-dev-project/attendance/model/attendance-status.ts
+++ b/src/app/child-dev-project/attendance/model/attendance-status.ts
@@ -1,17 +1,5 @@
import { ConfigurableEnumValue } from "../../../core/configurable-enum/configurable-enum.interface";
-/**
- * @deprecated
- */
-export enum AttendanceStatus {
- UNKNOWN = "?",
- HOLIDAY = "H",
- ABSENT = "A",
- PRESENT = "P",
- LATE = "L",
- EXCUSED = "E",
-}
-
/**
* logical type of an attendance status, i.e. how it will be considered for statistics and analysis.
*/
diff --git a/src/app/child-dev-project/children/child-photo-service/children-migration.service.spec.ts b/src/app/child-dev-project/children/child-photo-service/children-migration.service.spec.ts
deleted file mode 100644
index f6ec043cbc..0000000000
--- a/src/app/child-dev-project/children/child-photo-service/children-migration.service.spec.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { TestBed } from "@angular/core/testing";
-
-import { ChildrenMigrationService } from "./children-migration.service";
-import { Database } from "../../../core/database/database";
-import { PouchDatabase } from "../../../core/database/pouch-database";
-import { Child } from "../model/child";
-
-describe("ChildrenMigrationService", () => {
- let service: ChildrenMigrationService;
- let database: PouchDatabase;
-
- beforeEach(() => {
- database = PouchDatabase.createWithInMemoryDB();
- TestBed.configureTestingModule({
- providers: [{ provide: Database, useValue: database }],
- });
- service = TestBed.inject(ChildrenMigrationService);
- });
-
- afterEach(async () => {
- await database.destroy();
- });
-
- it("should be created", () => {
- expect(service).toBeTruthy();
- });
-
- it("should migrate children with old format", async () => {
- await database.put({
- _id: `${Child.ENTITY_TYPE}:firstChild`,
- photoFile: "oldFile1.jpg",
- });
- await database.put({
- _id: `${Child.ENTITY_TYPE}:secondChild`,
- photoFile: "oldFile2.jpg",
- });
- await database.put({
- _id: `${Child.ENTITY_TYPE}:thirdChild`,
- photo: "newFormat.jpg",
- });
-
- await service.migratePhotoFormat();
-
- const firstChild = await database.get(`${Child.ENTITY_TYPE}:firstChild`);
- expect(firstChild["photo"]).toEqual("oldFile1.jpg");
- const secondChild = await database.get(`${Child.ENTITY_TYPE}:secondChild`);
- expect(secondChild["photo"]).toEqual("oldFile2.jpg");
- const thirdChild = await database.get(`${Child.ENTITY_TYPE}:thirdChild`);
- expect(thirdChild["photo"]).toEqual("newFormat.jpg");
- });
-});
diff --git a/src/app/child-dev-project/children/child-photo-service/children-migration.service.ts b/src/app/child-dev-project/children/child-photo-service/children-migration.service.ts
deleted file mode 100644
index d86fbee559..0000000000
--- a/src/app/child-dev-project/children/child-photo-service/children-migration.service.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Injectable } from "@angular/core";
-import { Database } from "../../../core/database/database";
-import { Child } from "../model/child";
-import { LoggingService } from "../../../core/logging/logging.service";
-
-@Injectable({
- providedIn: "root",
-})
-export class ChildrenMigrationService {
- constructor(private database: Database, private logging: LoggingService) {}
-
- async migratePhotoFormat(): Promise {
- const oldFormatChildren = (
- await this.database.getAll(Child.ENTITY_TYPE + ":")
- ).filter(
- (child) =>
- child.hasOwnProperty("photoFile") && child["photoFile"].trim() !== ""
- );
- oldFormatChildren.forEach((child) => {
- const photoFile = child["photoFile"];
- delete child.photoFile;
- child["photo"] = photoFile;
- });
-
- await Promise.all(
- oldFormatChildren.map((child) => this.database.put(child))
- );
- this.logging.info(`migrated ${oldFormatChildren.length} objects`);
- }
-}
diff --git a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts b/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts
index 37a93b198e..7d8417c063 100644
--- a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts
+++ b/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts
@@ -7,7 +7,6 @@ import {
} from "@angular/core/testing";
import { RecentAttendanceBlocksComponent } from "./recent-attendance-blocks.component";
-import { FilterPipeModule } from "ngx-filter-pipe";
import { Child } from "../../model/child";
import { AttendanceService } from "../../../attendance/attendance.service";
import { ActivityAttendance } from "../../../attendance/model/activity-attendance";
@@ -29,7 +28,6 @@ describe("RecentAttendanceBlocksComponent", () => {
TestBed.configureTestingModule({
declarations: [RecentAttendanceBlocksComponent],
- imports: [FilterPipeModule],
providers: [
{ provide: AttendanceService, useValue: mockAttendanceService },
],
diff --git a/src/app/child-dev-project/children/children.module.ts b/src/app/child-dev-project/children/children.module.ts
index 3ce007effb..c8fccb73ad 100644
--- a/src/app/child-dev-project/children/children.module.ts
+++ b/src/app/child-dev-project/children/children.module.ts
@@ -45,7 +45,6 @@ import { NotesOfChildComponent } from "../notes/notes-of-child/notes-of-child.co
import { SchoolsModule } from "../schools/schools.module";
import { EducationalMaterialComponent } from "../educational-material/educational-material-component/educational-material.component";
import { AserComponent } from "../aser/aser-component/aser.component";
-import { FilterPipeModule } from "ngx-filter-pipe";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { NotesDashboardComponent } from "../notes/dashboard-widgets/notes-dashboard/notes-dashboard.component";
import { HealthCheckupComponent } from "../health-checkup/health-checkup-component/health-checkup.component";
@@ -96,7 +95,6 @@ import { ExportModule } from "../../core/export/export.module";
MatChipsModule,
BrowserAnimationsModule,
ReactiveFormsModule,
- FilterPipeModule,
SchoolsModule,
ReactiveFormsModule,
MatDialogModule,
diff --git a/src/app/child-dev-project/children/children.service.ts b/src/app/child-dev-project/children/children.service.ts
index 2aa96fd264..fd22c27595 100644
--- a/src/app/child-dev-project/children/children.service.ts
+++ b/src/app/child-dev-project/children/children.service.ts
@@ -2,7 +2,6 @@ import { Injectable, Optional } from "@angular/core";
import { from, Observable, Subject } from "rxjs";
import { Child } from "./model/child";
import { EntityMapperService } from "../../core/entity/entity-mapper.service";
-import { AttendanceMonth } from "../attendance/model/attendance-month";
import { Note } from "../notes/model/note";
import { EducationalMaterial } from "../educational-material/model/educational-material";
import { Aser } from "../aser/model/aser";
@@ -28,9 +27,7 @@ export class ChildrenService {
}
public createDatabaseIndices() {
- this.createAttendanceAnalysisIndex();
this.createNotesIndex();
- this.createAttendancesIndex();
this.createChildSchoolRelationIndex();
}
@@ -47,10 +44,6 @@ export class ChildrenService {
const childCurrentSchoolInfo = await this.getCurrentSchoolInfoForChild(
loadedChild.getId()
);
- await this.migrateToNewChildSchoolRelationModel(
- loadedChild,
- childCurrentSchoolInfo
- );
loadedChild.schoolClass = childCurrentSchoolInfo.schoolClass;
loadedChild.schoolId = childCurrentSchoolInfo.schoolId;
}
@@ -61,43 +54,6 @@ export class ChildrenService {
return results;
}
- /**
- * DATA MODEL UPGRADE
- * Check if the Child Entity still contains direct links to schoolId and schoolClass
- * and create a new ChildSchoolRelation if necessary.
- * @param loadedChild Child entity to be checked and migrated
- * @param childCurrentSchoolInfo Currently available school information according to new data model from ChildSchoolRelation entities
- */
- private async migrateToNewChildSchoolRelationModel(
- loadedChild: Child,
- childCurrentSchoolInfo: { schoolId: string; schoolClass: string }
- ) {
- if (!loadedChild.schoolClass && !loadedChild.schoolId) {
- // no data from old model -> skip migration
- return;
- }
-
- if (
- loadedChild.schoolId !== childCurrentSchoolInfo.schoolId ||
- loadedChild.schoolClass !== childCurrentSchoolInfo.schoolClass
- ) {
- // generate a ChildSchoolRelation entity from the information of the previous data model
- const autoMigratedChildSchoolRelation = new ChildSchoolRelation();
- autoMigratedChildSchoolRelation.childId = loadedChild.getId();
- autoMigratedChildSchoolRelation.schoolId = loadedChild.schoolId;
- autoMigratedChildSchoolRelation.schoolClass = loadedChild.schoolClass;
- await this.entityMapper.save(autoMigratedChildSchoolRelation);
- this.logger?.debug(
- "migrated Child entity to new ChildSchoolRelation model " +
- loadedChild._id
- );
- console.log(autoMigratedChildSchoolRelation);
- }
-
- // save the Child entity to remove the deprecated attributes from the doc in the database
- await this.entityMapper.save(loadedChild);
- }
-
/**
* returns an observable which retrieves a single child and loads its photo
* @param id id of child
@@ -117,64 +73,6 @@ export class ChildrenService {
return from(promise);
}
- getAttendances(): Observable {
- return from(this.entityMapper.loadType(AttendanceMonth));
- }
-
- getAttendancesOfChild(childId: string): Observable {
- const promise = this.dbIndexing.queryIndexDocs(
- AttendanceMonth,
- "attendances_index/by_child",
- childId
- );
-
- return from(promise);
- }
-
- getAttendancesOfMonth(month: Date): Observable {
- const monthString =
- month.getFullYear().toString() + "-" + (month.getMonth() + 1).toString();
- const promise = this.dbIndexing.queryIndexDocs(
- AttendanceMonth,
- "attendances_index/by_month",
- monthString
- );
-
- return from(promise);
- }
-
- /**
- * @deprecated use AttendanceService instead. This can be removed after all AttendanceMigrationService tasks are completed.
- * @private
- */
- private createAttendancesIndex(): Promise {
- const designDoc = {
- _id: "_design/attendances_index",
- views: {
- by_child: {
- map:
- "(doc) => { " +
- 'if (!doc._id.startsWith("' +
- AttendanceMonth.ENTITY_TYPE +
- '")) return;' +
- "emit(doc.student); " +
- "}",
- },
- by_month: {
- map:
- "(doc) => { " +
- 'if (!doc._id.startsWith("' +
- AttendanceMonth.ENTITY_TYPE +
- '")) return;' +
- "emit(doc.month); " +
- "}",
- },
- },
- };
-
- return this.dbIndexing.createIndex(designDoc);
- }
-
private createChildSchoolRelationIndex(): Promise {
const designDoc = {
_id: "_design/childSchoolRelations_index",
@@ -248,69 +146,6 @@ export class ChildrenService {
);
}
- async queryAttendanceLast3Months() {
- return this.dbIndexing.queryIndexStats("avg_attendance_index/three_months");
- }
-
- async queryAttendanceLastMonth() {
- return this.dbIndexing.queryIndexStats("avg_attendance_index/last_month");
- }
-
- private createAttendanceAnalysisIndex(): Promise {
- const designDoc = {
- _id: "_design/avg_attendance_index",
- views: {
- three_months: {
- map: this.getAverageAttendanceMapFunction(),
- reduce: "_stats",
- },
- last_month: {
- map: this.getLastAverageAttendanceMapFunction(),
- reduce: "_stats",
- },
- },
- };
-
- return this.dbIndexing.createIndex(designDoc);
- }
-
- private getAverageAttendanceMapFunction() {
- return (
- "(doc) => {" +
- 'if (!doc._id.startsWith("AttendanceMonth:") ) { return; }' +
- "if (!isWithinLast3Months(new Date(doc.month), new Date())) { return; }" +
- "var attendance = (doc.daysAttended / (doc.daysWorking - doc.daysExcused));" +
- "if (!Number.isNaN(attendance)) { emit(doc.student, attendance); }" +
- "function isWithinLast3Months(date, now) {" +
- " let months;" +
- " months = (now.getFullYear() - date.getFullYear()) * 12;" +
- " months -= date.getMonth();" +
- " months += now.getMonth();" +
- " if (months < 0) { return false; }" +
- " return months <= 3;" +
- "}" +
- "}"
- );
- }
-
- private getLastAverageAttendanceMapFunction() {
- return (
- "(doc) => {" +
- 'if (!doc._id.startsWith("AttendanceMonth:")) { return; }' +
- "if (!isWithinLastMonth(new Date(doc.month), new Date())) { return; }" +
- "var attendance = (doc.daysAttended / (doc.daysWorking - doc.daysExcused));" +
- "if (!Number.isNaN(attendance)) { emit(doc.student, attendance); }" +
- "function isWithinLastMonth(date, now) {" +
- " let months;" +
- " months = (now.getFullYear() - date.getFullYear()) * 12;" +
- " months -= date.getMonth();" +
- " months += now.getMonth();" +
- " return months === 1;" +
- "}" +
- "}"
- );
- }
-
getNotesOfChild(childId: string): Observable {
const promise = this.dbIndexing.queryIndexDocs(
Note,
diff --git a/src/app/child-dev-project/notes/notes-migration/notes-migration.service.spec.ts b/src/app/child-dev-project/notes/notes-migration/notes-migration.service.spec.ts
deleted file mode 100644
index f93905f879..0000000000
--- a/src/app/child-dev-project/notes/notes-migration/notes-migration.service.spec.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import { TestBed } from "@angular/core/testing";
-
-import { NotesMigrationService } from "./notes-migration.service";
-import { EntityMapperService } from "../../../core/entity/entity-mapper.service";
-import { User } from "../../../core/user/user";
-import { Note } from "../model/note";
-import {
- mockEntityMapper,
- MockEntityMapperService,
-} from "../../../core/entity/mock-entity-mapper-service";
-import { AlertService } from "../../../core/alerts/alert.service";
-
-function legacyNote(author: string): Note {
- const note = new Note();
- note["author"] = author;
- return note;
-}
-
-function createUser(name: string): User {
- const user = new User();
- user.name = name;
- return user;
-}
-
-describe("NotesMigrationService", () => {
- let service: NotesMigrationService;
- const Peter = createUser("Peter");
- const Ursula = createUser("Ursula");
- const Jens = createUser("Jens");
- const Angela = createUser("Angela");
- const Albrecht = createUser("Albrecht");
- const Johanna = createUser("Johanna");
- const users = [Peter, Ursula, Jens, Angela, Albrecht, Johanna];
-
- let entityMapper: MockEntityMapperService;
-
- beforeEach(() => {
- entityMapper = mockEntityMapper([]);
- TestBed.configureTestingModule({
- providers: [
- {
- provide: EntityMapperService,
- useValue: entityMapper,
- },
- { provide: AlertService, useValue: jasmine.createSpyObj(["addAlert"]) },
- ],
- });
- service = TestBed.inject(NotesMigrationService);
- service.allUsers = new Map(users.map((u) => [u.name.toLowerCase(), u]));
- });
-
- it("should be created", () => {
- expect(service).toBeTruthy();
- });
-
- it("finds the correct user for different user-names", () => {
- const oldAuthors = [
- "Peter Lustig, Ursula",
- "Jens & Angela",
- "Albrecht",
- "Johanna",
- "Peter, Jens, Albrecht, Johanna",
- ];
- const expectedUsers = [
- [Peter, Ursula],
- [Jens, Angela],
- [Albrecht],
- [Johanna],
- [Peter, Jens, Albrecht, Johanna],
- ];
- oldAuthors.forEach((authorName, index) => {
- const foundUsers = service.findUsers(authorName);
- expect(foundUsers.detectedUsers).toEqual(expectedUsers[index]);
- expect(foundUsers.additional).toHaveSize(0);
- });
- });
-
- it("returns an array of users that could not be matched", () => {
- const nonMatchedUsers = [
- "Phillip",
- "Christian",
- "Agnes, Strack & Zimmermann",
- "Gregor Giselle",
- "Insalata and mista",
- ];
- const expected = [
- ["Phillip"],
- ["Christian"],
- ["Agnes", "Strack", "Zimmermann"],
- ["Gregor Giselle"],
- ["Insalata", "mista"],
- ];
- nonMatchedUsers.forEach((userName, index) => {
- const foundUsers = service.findUsers(userName);
- expect(foundUsers.additional).toEqual(expected[index]);
- expect(foundUsers.detectedUsers).toHaveSize(0);
- });
- });
-
- it("migrates a note with existing users", () => {
- const note = legacyNote("Peter L, Ursula & Jens");
- const expectedUsers = [Peter, Ursula, Jens].map((u) => u.getId());
- service.migrateSingleNote(note);
- expect(note["author"]).not.toBeDefined();
- expect(note.authors).toEqual(expectedUsers);
- expect(note.text).toHaveSize(0);
- });
-
- it("appends a note-text with all non-found users", () => {
- const note = legacyNote("Peter L, Ursula & Andi");
- note.text = "Lorem ipsum";
- const expectedUsers = [Peter, Ursula].map((u) => u.getId());
- service.migrateSingleNote(note);
- expect(note.authors).toEqual(expectedUsers);
- const newText = note.text.split("\n");
- expect(newText).toHaveSize(2);
- expect(newText[0]).toEqual("Lorem ipsum");
- expect(newText[1]).toEqual("Also authored by Andi");
- });
-
- it("migrates all existing notes", async () => {
- const notes = [
- legacyNote(""),
- legacyNote("Peter L, Ursula"),
- legacyNote("Peter L, Ursula & Andi"),
- legacyNote("Johannes"),
- ];
- entityMapper.addAll(notes);
- entityMapper.addAll(users);
- await service.migrateToMultiUser();
- const existingUsers = [Peter, Ursula].map((u) => u.getId());
- const migratedNotes = entityMapper.getAll("Note");
- expect(migratedNotes).toHaveSize(4);
- migratedNotes.forEach((note) => {
- expect(note["author"]).not.toBeDefined();
- });
- expect(migratedNotes[0].authors).toHaveSize(0);
- expect(migratedNotes[0].text).toHaveSize(0);
- expect(migratedNotes[1].authors).toEqual(existingUsers);
- expect(migratedNotes[1].text).toHaveSize(0);
- expect(migratedNotes[2].authors).toEqual(existingUsers);
- expect(migratedNotes[2].text).toEqual("Also authored by Andi");
- expect(migratedNotes[3].authors).toHaveSize(0);
- expect(migratedNotes[3].text).toEqual("Also authored by Johannes");
- });
-
- it("does not change an already migrated note", () => {
- const note = new Note();
- note.authors = [Peter, Ursula, Jens].map((u) => u.getId());
- note.text = "Lorem ipsum";
- note.date = new Date();
- const copy = note.copy();
- service.migrateSingleNote(note);
- expect(note).toEqual(copy);
- });
-});
diff --git a/src/app/child-dev-project/notes/notes-migration/notes-migration.service.ts b/src/app/child-dev-project/notes/notes-migration/notes-migration.service.ts
deleted file mode 100644
index 1dbe6998e1..0000000000
--- a/src/app/child-dev-project/notes/notes-migration/notes-migration.service.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { Injectable } from "@angular/core";
-import { EntityMapperService } from "../../../core/entity/entity-mapper.service";
-import { Note } from "../model/note";
-import { User } from "../../../core/user/user";
-import { AlertService } from "../../../core/alerts/alert.service";
-import { Alert } from "../../../core/alerts/alert";
-import { AlertDisplay } from "../../../core/alerts/alert-display";
-import { RecurringActivity } from "../../attendance/model/recurring-activity";
-
-@Injectable({
- providedIn: "root",
-})
-export class NotesMigrationService {
- allUsers: Map;
- constructor(
- private entityMapperService: EntityMapperService,
- private alertService: AlertService
- ) {}
-
- /**
- * migrates all notes in the database to the new format.
- * This format will include users as an array of string-id's
- * instead of a single field describing the author(s) of the note
- */
- async migrateToMultiUser() {
- console.log("Starting to migrate all Notes' authors");
- this.allUsers = new Map(
- (await this.entityMapperService.loadType(User)).map((u) => [
- u.name.toLowerCase(),
- u,
- ])
- );
- const allNotes: Note[] = await this.entityMapperService.loadType(Note);
- let amountOfMigratedNotes = 0;
- for (const note of allNotes) {
- amountOfMigratedNotes += this.migrateSingleNote(note);
- await this.entityMapperService.save(note);
- }
- this.alertService.addAlert(
- new Alert(
- `Migrated ${amountOfMigratedNotes} note(s) `,
- Alert.INFO,
- AlertDisplay.TEMPORARY
- )
- );
- console.log(
- `Completed to migrate all Notes' authors (${amountOfMigratedNotes} note(s) total)`
- );
-
- this.migrateToMultiAssignedActivities();
- }
-
- async migrateToMultiAssignedActivities() {
- const allActivities: RecurringActivity[] = await this.entityMapperService.loadType(
- RecurringActivity
- );
- for (const act of allActivities) {
- act.assignedTo = [];
- await this.entityMapperService.save(act);
- }
- }
-
- /**
- * migrate a single note to the new format.
- * The 'author'-field will be deleted after the migration is done
- * @param note The note to migrate
- */
- public migrateSingleNote(note: Note): number {
- const userStr = note["author"];
- if (userStr === undefined || userStr === null) {
- // no migration necessary
- return 0;
- }
- const newUsers = this.findUsers(userStr);
- note.authors = newUsers.detectedUsers.map((u) => u.getId());
- delete note["author"];
- if (newUsers.additional.length > 0) {
- console.log("could not match all users", note);
- this.updateNoteText(note, newUsers.additional);
- }
- return 1;
- }
-
- private updateNoteText(note: Note, additionalUsers: string[]) {
- const additionalText = "Also authored by " + additionalUsers.join(", ");
- if (note.text.length === 0) {
- note.text = additionalText;
- } else {
- note.text += "\n" + additionalText;
- }
- }
-
- /**
- * finds a user based on the following assumptions:
- *
All leading and trailing whitespaces are ignored
- *
A single user will be matched by his case-insensitive name
- *
When the search string contains a ',' or '&'-character, multiple
- * users will be matched
- *
If the string to match contains a whitespace, this name will be matched
- * as well as all 'parts' of that name, meaning every sub-string, split by whitespaces
- * @param str the string to search
- */
- public findUsers(
- str: string
- ): { detectedUsers: User[]; additional: string[] } {
- const detectedUsers: User[] = [];
- const additional: string[] = [];
- // split on '&', 'and' and ','
- // remove any non alphabet-characters and non-whitespace-characters
- const searchStrings = str
- .trim()
- .replace(/&/g, ",")
- .replace(/and/g, ",")
- .split(",")
- .map((s) => s.replace(/[^a-zA-Z\s]/, "").trim());
- for (const searchString of searchStrings) {
- const user = this.findSingleUser(searchString);
- if (user) {
- detectedUsers.push(user);
- } else if (searchString.trim().length > 0) {
- additional.push(searchString.trim());
- }
- }
- return { detectedUsers: detectedUsers, additional: additional };
- }
-
- /**
- * Find a single user based on a search string that should represent a single user
- * @param str The string to look for
- */
- private findSingleUser(str: string): User | undefined {
- const lowerCaseSearch = str.toLowerCase();
- if (lowerCaseSearch.match(/\s/)) {
- return (
- // first look for a user that matches exactly (including whitespaces)
- this.allUsers.get(lowerCaseSearch) ||
- // If none is found, look for a user where any name (in most cases name and surname) matches
- lowerCaseSearch
- .split(/\s/)
- .map((s) => this.allUsers.get(s))
- .filter((u) => !!u)
- .pop()
- );
- } else {
- return this.allUsers.get(lowerCaseSearch);
- }
- }
-}
diff --git a/src/app/child-dev-project/notes/notes.module.ts b/src/app/child-dev-project/notes/notes.module.ts
index 66777393cd..03325b83b0 100644
--- a/src/app/child-dev-project/notes/notes.module.ts
+++ b/src/app/child-dev-project/notes/notes.module.ts
@@ -23,7 +23,6 @@ import { MatProgressBarModule } from "@angular/material/progress-bar";
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatTooltipModule } from "@angular/material/tooltip";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
-import { FilterPipeModule } from "ngx-filter-pipe";
import { SchoolsModule } from "../schools/schools.module";
import { MatListModule } from "@angular/material/list";
import { ChildrenModule } from "../children/children.module";
@@ -79,7 +78,6 @@ import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
MatSlideToggleModule,
BrowserAnimationsModule,
ReactiveFormsModule,
- FilterPipeModule,
SchoolsModule,
ReactiveFormsModule,
MatDialogModule,
diff --git a/src/app/core/admin/admin/admin.component.html b/src/app/core/admin/admin/admin.component.html
index 3338054d70..0daf0dee35 100644
--- a/src/app/core/admin/admin/admin.component.html
+++ b/src/app/core/admin/admin/admin.component.html
@@ -19,33 +19,6 @@