From 762393974ce749b6916b1df8f62366ccdc370db0 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Mon, 26 Aug 2024 16:29:34 +0200 Subject: [PATCH 01/39] add some prototype data for the stores --- src/lib/stores/childrenData.js | 3 +++ src/lib/stores/contentStore.js | 26 ++++++++++++++++++++++++++ src/lib/stores/userData.js | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 src/lib/stores/childrenData.js create mode 100644 src/lib/stores/contentStore.js create mode 100644 src/lib/stores/userData.js diff --git a/src/lib/stores/childrenData.js b/src/lib/stores/childrenData.js new file mode 100644 index 00000000..74867cce --- /dev/null +++ b/src/lib/stores/childrenData.js @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/lib/stores/contentStore.js b/src/lib/stores/contentStore.js new file mode 100644 index 00000000..c38455c4 --- /dev/null +++ b/src/lib/stores/contentStore.js @@ -0,0 +1,26 @@ +import { writable } from 'svelte/store'; + +let contentlist = { + childrenSurveys: {}, + registrationForms: {} +}; + +const content = writable(contentlist); + +function addContent(type, key, content) { + content.update((contentlist) => { + contentlist[type][key] = content; + }); +} + +function removeContent(type, key) { + content.update((contentlist) => { + delete contentlist[type][key]; + }); +} + +function getContent(type, key) { + return content.value[type][key]; +} + +export { addContent, content, getContent, removeContent }; diff --git a/src/lib/stores/userData.js b/src/lib/stores/userData.js new file mode 100644 index 00000000..74818ea8 --- /dev/null +++ b/src/lib/stores/userData.js @@ -0,0 +1,34 @@ +// @ts-nocheck +import { writable } from 'svelte/store'; + +// FIXME: there must be something that is used to validate input - prototype for user data or so. +// this will eventually go into the backend, but for now it must reside here. + +let userlist = {}; + +const users = writable(userlist); + +async function addUser(userToken, userData) { + users.update((userlist) => { + if (userToken in userlist) { + // raise some error + } else { + userData['token'] = userToken; + userlist[userToken] = userData; + } + }); +} + +async function removeUser(userToken) { + users.update((userlist) => { + if (userToken in userlist) { + delete userlist[userToken]; + } + }); +} + +async function getUser(userToken) { + return users.value[userToken]; +} + +export { addUser, getUser, removeUser, users }; From 0e4087a0ce0245ac2c08ed6735aed71ba7c31de1 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Mon, 26 Aug 2024 16:34:54 +0200 Subject: [PATCH 02/39] add childrenskeleton --- src/lib/stores/childrenData.js | 14 +++++++++++--- src/lib/stores/contentStore.js | 6 +----- src/lib/stores/userData.js | 6 +----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/stores/childrenData.js b/src/lib/stores/childrenData.js index 74867cce..3bbfe2a0 100644 --- a/src/lib/stores/childrenData.js +++ b/src/lib/stores/childrenData.js @@ -1,3 +1,11 @@ - \ No newline at end of file +import { writable } from 'svelte/store'; + +let childrenlist = {}; + +const childrenData = writable(childrenlist); + +function addChildrenData(data, usertoken) {} + +function removeChildrenData(usertoken) {} + +export { addChildrenData, childrenData, removeChildrenData }; diff --git a/src/lib/stores/contentStore.js b/src/lib/stores/contentStore.js index c38455c4..8d8bb9b7 100644 --- a/src/lib/stores/contentStore.js +++ b/src/lib/stores/contentStore.js @@ -19,8 +19,4 @@ function removeContent(type, key) { }); } -function getContent(type, key) { - return content.value[type][key]; -} - -export { addContent, content, getContent, removeContent }; +export { addContent, content, removeContent }; diff --git a/src/lib/stores/userData.js b/src/lib/stores/userData.js index 74818ea8..53c05238 100644 --- a/src/lib/stores/userData.js +++ b/src/lib/stores/userData.js @@ -27,8 +27,4 @@ async function removeUser(userToken) { }); } -async function getUser(userToken) { - return users.value[userToken]; -} - -export { addUser, getUser, removeUser, users }; +export { addUser, removeUser, users }; From dfd3d6f37450726111c2f2bb1dfb5e61ddc33ffb Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Mon, 26 Aug 2024 16:38:29 +0200 Subject: [PATCH 03/39] work on childrendata skeleton --- src/lib/stores/childrenData.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lib/stores/childrenData.js b/src/lib/stores/childrenData.js index 3bbfe2a0..9bd570b0 100644 --- a/src/lib/stores/childrenData.js +++ b/src/lib/stores/childrenData.js @@ -4,8 +4,23 @@ let childrenlist = {}; const childrenData = writable(childrenlist); -function addChildrenData(data, usertoken) {} +async function addChildrenData(data, usertoken) { + childrenData.update((childrenlist) => { + if (usertoken in childrenlist) { + childrenlist[usertoken] = data; + } else { + childrenlist[usertoken] = {}; + childrenlist[usertoken][data.token] = data; + } + }); +} -function removeChildrenData(usertoken) {} +async function removeChildrenData(usertoken, childtoken) { + childrenData.update((childrenlist) => { + if (usertoken in childrenlist) { + delete childrenlist[usertoken][childtoken]; + } + }); +} export { addChildrenData, childrenData, removeChildrenData }; From 90c0e5e666e41d256c7a799947d2cc0819370df4 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Mon, 26 Aug 2024 20:29:34 +0200 Subject: [PATCH 04/39] use typescript --- src/lib/stores/childrenData.js | 26 ----------------- src/lib/stores/childrenData.ts | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 26 deletions(-) delete mode 100644 src/lib/stores/childrenData.js create mode 100644 src/lib/stores/childrenData.ts diff --git a/src/lib/stores/childrenData.js b/src/lib/stores/childrenData.js deleted file mode 100644 index 9bd570b0..00000000 --- a/src/lib/stores/childrenData.js +++ /dev/null @@ -1,26 +0,0 @@ -import { writable } from 'svelte/store'; - -let childrenlist = {}; - -const childrenData = writable(childrenlist); - -async function addChildrenData(data, usertoken) { - childrenData.update((childrenlist) => { - if (usertoken in childrenlist) { - childrenlist[usertoken] = data; - } else { - childrenlist[usertoken] = {}; - childrenlist[usertoken][data.token] = data; - } - }); -} - -async function removeChildrenData(usertoken, childtoken) { - childrenData.update((childrenlist) => { - if (usertoken in childrenlist) { - delete childrenlist[usertoken][childtoken]; - } - }); -} - -export { addChildrenData, childrenData, removeChildrenData }; diff --git a/src/lib/stores/childrenData.ts b/src/lib/stores/childrenData.ts new file mode 100644 index 00000000..5316776c --- /dev/null +++ b/src/lib/stores/childrenData.ts @@ -0,0 +1,53 @@ +import { writable } from 'svelte/store'; + +interface ChildData { + token: string; +} + +interface ChildrenList { + [usertoken: string]: { + [childtoken: string]: ChildData; + }; +} + +const childrenlist: ChildrenList = {}; + +const childrenData = writable(childrenlist); + +async function addChildrenData(data: ChildData, childtoken: string, usertoken: string) { + childrenData.update((childrenlist) => { + if (!(usertoken in childrenlist)) { + throw new Error(`User token ${usertoken} not found`); + } + + if (!(childtoken in childrenlist[usertoken])) { + throw new Error(`Child token ${childtoken} not found for user token ${usertoken}`); + } + + data['token'] = usertoken; + if (usertoken in childrenlist) { + childrenlist[usertoken][childtoken] = data; + } else { + childrenlist[usertoken][childtoken] = data; + } + return childrenlist; + }); +} + +async function removeChildrenData(childtoken: string, usertoken: string) { + childrenData.update((childrenlist) => { + if (!(usertoken in childrenlist)) { + throw new Error(`User token ${usertoken} not found`); + } + + if (!(childtoken in childrenlist[usertoken])) { + throw new Error(`Child token ${childtoken} not found for user token ${usertoken}`); + } + + delete childrenlist[usertoken][childtoken]; + + return childrenlist; + }); +} + +export { addChildrenData, childrenData, removeChildrenData }; From dba2aaf7c95e7fbb158f7e0b6c5656172388f801 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Mon, 26 Aug 2024 20:34:32 +0200 Subject: [PATCH 05/39] add typscript stuff --- src/lib/stores/childrenData.ts | 6 +++--- src/lib/stores/{contentStore.js => contentStore.ts} | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) rename src/lib/stores/{contentStore.js => contentStore.ts} (99%) diff --git a/src/lib/stores/childrenData.ts b/src/lib/stores/childrenData.ts index 5316776c..51b149c9 100644 --- a/src/lib/stores/childrenData.ts +++ b/src/lib/stores/childrenData.ts @@ -14,7 +14,7 @@ const childrenlist: ChildrenList = {}; const childrenData = writable(childrenlist); -async function addChildrenData(data: ChildData, childtoken: string, usertoken: string) { +async function addChildData(data: ChildData, childtoken: string, usertoken: string) { childrenData.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); @@ -34,7 +34,7 @@ async function addChildrenData(data: ChildData, childtoken: string, usertoken: s }); } -async function removeChildrenData(childtoken: string, usertoken: string) { +async function removeChildData(childtoken: string, usertoken: string) { childrenData.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); @@ -50,4 +50,4 @@ async function removeChildrenData(childtoken: string, usertoken: string) { }); } -export { addChildrenData, childrenData, removeChildrenData }; +export { addChildData, childrenData, removeChildData }; diff --git a/src/lib/stores/contentStore.js b/src/lib/stores/contentStore.ts similarity index 99% rename from src/lib/stores/contentStore.js rename to src/lib/stores/contentStore.ts index 8d8bb9b7..9adcb34c 100644 --- a/src/lib/stores/contentStore.js +++ b/src/lib/stores/contentStore.ts @@ -1,5 +1,7 @@ import { writable } from 'svelte/store'; + + let contentlist = { childrenSurveys: {}, registrationForms: {} From 597bd29e47376d50faa356927d3d2c8bf6d6ed33 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 08:24:46 +0200 Subject: [PATCH 06/39] finish typescript stuff --- src/lib/stores/childrenData.ts | 2 +- src/lib/stores/contentStore.ts | 28 ++++++++++++---- src/lib/stores/userData.js | 30 ----------------- src/lib/stores/userData.ts | 61 ++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 37 deletions(-) delete mode 100644 src/lib/stores/userData.js create mode 100644 src/lib/stores/userData.ts diff --git a/src/lib/stores/childrenData.ts b/src/lib/stores/childrenData.ts index 51b149c9..ad9c0922 100644 --- a/src/lib/stores/childrenData.ts +++ b/src/lib/stores/childrenData.ts @@ -1,7 +1,7 @@ import { writable } from 'svelte/store'; interface ChildData { - token: string; + [key: string]: string[] | string; } interface ChildrenList { diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 9adcb34c..0f7e5695 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -1,23 +1,39 @@ import { writable } from 'svelte/store'; +// types +interface ChildrenSurvey { + [name: string]: unknown; +} +interface RegistrationForms { + [name: string]: unknown; +} -let contentlist = { +interface ContentList { + childrenSurveys: ChildrenSurvey; + registrationForms: RegistrationForms; +} + +const contentlist: ContentList = { childrenSurveys: {}, registrationForms: {} }; +// store that has an object which stores the content. const content = writable(contentlist); -function addContent(type, key, content) { - content.update((contentlist) => { - contentlist[type][key] = content; +// functions to add and remove stuff from the store +function addContent(type: string, key: string) { + content.update((contentlist: ContentList) => { + contentlist[type as keyof ContentList][key] = content; + return contentlist; }); } -function removeContent(type, key) { +function removeContent(type: string, key: string) { content.update((contentlist) => { - delete contentlist[type][key]; + delete contentlist[type as keyof ContentList][key]; + return contentlist; }); } diff --git a/src/lib/stores/userData.js b/src/lib/stores/userData.js deleted file mode 100644 index 53c05238..00000000 --- a/src/lib/stores/userData.js +++ /dev/null @@ -1,30 +0,0 @@ -// @ts-nocheck -import { writable } from 'svelte/store'; - -// FIXME: there must be something that is used to validate input - prototype for user data or so. -// this will eventually go into the backend, but for now it must reside here. - -let userlist = {}; - -const users = writable(userlist); - -async function addUser(userToken, userData) { - users.update((userlist) => { - if (userToken in userlist) { - // raise some error - } else { - userData['token'] = userToken; - userlist[userToken] = userData; - } - }); -} - -async function removeUser(userToken) { - users.update((userlist) => { - if (userToken in userlist) { - delete userlist[userToken]; - } - }); -} - -export { addUser, removeUser, users }; diff --git a/src/lib/stores/userData.ts b/src/lib/stores/userData.ts new file mode 100644 index 00000000..fdec63a1 --- /dev/null +++ b/src/lib/stores/userData.ts @@ -0,0 +1,61 @@ +import { writable } from 'svelte/store'; + +// type interfaces +interface PersonalData { + yearofbirth: number; + gender: string; + profession: string; + education: string; + workinghours: string; + householdincome: string; +} + +interface ScientistData extends PersonalData { + project: string; +} + +interface AdminData extends PersonalData { + admintoken: string; +} + +interface UserData { + role: string; + email: string; + name: string; + token: string; + password: string; + data: PersonalData | ScientistData | AdminData; +} + +interface UserList { + [key: string]: UserData; +} + +// Initial user data +const userlist: UserList = {}; + +const users = writable(userlist); + +// functions to add and delete users +async function addUser(userToken: string, userData: UserData) { + users.update((userlist) => { + if (userToken in userlist) { + // raise some error + } else { + userData['token'] = userToken; + userlist[userToken] = userData; + } + return userlist; + }); +} + +async function removeUser(userToken: string) { + users.update((userlist) => { + if (userToken in userlist) { + delete userlist[userToken]; + } + return userlist; + }); +} + +export { addUser, removeUser, users }; From 98efe89bc5bdd9c8b7692d5e53f257d9618a3215 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 08:53:08 +0200 Subject: [PATCH 07/39] finish ts stuff, add test files, add tests for userstore --- src/lib/stores/childrenStore.test.ts | 0 .../{childrenData.ts => childrenStore.ts} | 0 src/lib/stores/contentStore.test.ts | 0 src/lib/stores/userStore.test.ts | 66 +++++++++++++++++++ src/lib/stores/{userData.ts => userStore.ts} | 6 +- 5 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/lib/stores/childrenStore.test.ts rename src/lib/stores/{childrenData.ts => childrenStore.ts} (100%) create mode 100644 src/lib/stores/contentStore.test.ts create mode 100644 src/lib/stores/userStore.test.ts rename src/lib/stores/{userData.ts => userStore.ts} (84%) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/stores/childrenData.ts b/src/lib/stores/childrenStore.ts similarity index 100% rename from src/lib/stores/childrenData.ts rename to src/lib/stores/childrenStore.ts diff --git a/src/lib/stores/contentStore.test.ts b/src/lib/stores/contentStore.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/stores/userStore.test.ts b/src/lib/stores/userStore.test.ts new file mode 100644 index 00000000..a1c0fd87 --- /dev/null +++ b/src/lib/stores/userStore.test.ts @@ -0,0 +1,66 @@ +// userStore.test.ts + +import { get } from 'svelte/store'; +import { describe, expect, it } from 'vitest'; +import { addUser, removeUser, type UserData, users } from './userStore'; + +// Mock user data + +// Test suite for userStore +describe('test_normal_function', () => { + const mockUserToken = 'mockUserToken'; + const mockUserData: UserData = { + role: 'mockRole', + email: 'mockEmail', + name: 'mockName', + token: '', + password: 'mockPassword', + data: { + yearofbirth: 1990, + gender: 'male', + profession: 'developer', + education: 'bachelor', + workinghours: 'full-time', + householdincome: 'above-average' + } + }; + + it('should add a new user successfully', async () => { + users.set({}); + + await addUser(mockUserToken, mockUserData); + + expect(get(users)[mockUserToken]).toEqual({ + ...mockUserData, + token: mockUserToken + }); + }); + + it('should not add a user with an existing token', async () => { + users.set({ mockUserToken: mockUserData }); + + try { + await addUser(mockUserToken, mockUserData); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('User with this token already exists'); + } + }); + + it('should remove a user successfully', async () => { + users.set({ mockUserToken: mockUserData }); + + await removeUser(mockUserToken); + + expect(get(users)[mockUserToken]).toBeUndefined(); + }); + + it('should not remove a user with a non-existing token', async () => { + users.set({ mockUserToken: mockUserData }); + + try { + await removeUser('notdefined'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('User with this token does not exist'); + } + }); +}); diff --git a/src/lib/stores/userData.ts b/src/lib/stores/userStore.ts similarity index 84% rename from src/lib/stores/userData.ts rename to src/lib/stores/userStore.ts index fdec63a1..5d5878ea 100644 --- a/src/lib/stores/userData.ts +++ b/src/lib/stores/userStore.ts @@ -40,7 +40,7 @@ const users = writable(userlist); async function addUser(userToken: string, userData: UserData) { users.update((userlist) => { if (userToken in userlist) { - // raise some error + throw new Error('User with this token already exists'); } else { userData['token'] = userToken; userlist[userToken] = userData; @@ -53,9 +53,11 @@ async function removeUser(userToken: string) { users.update((userlist) => { if (userToken in userlist) { delete userlist[userToken]; + } else { + throw new Error('User with this token does not exist'); } return userlist; }); } -export { addUser, removeUser, users }; +export { addUser, removeUser, users, type UserData, type UserList }; From 0e13f41e049115018373d9229bb283195cfac590 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 09:40:51 +0200 Subject: [PATCH 08/39] work on tests --- src/lib/stores/childrenStore.test.ts | 0 src/lib/stores/contentStore.test.ts | 63 ++++++++++++++++++++++++++++ src/lib/stores/contentStore.ts | 36 +++++++++++++--- src/lib/stores/userStore.test.ts | 5 --- 4 files changed, 93 insertions(+), 11 deletions(-) delete mode 100644 src/lib/stores/childrenStore.test.ts diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/lib/stores/contentStore.test.ts b/src/lib/stores/contentStore.test.ts index e69de29b..cadeeeee 100644 --- a/src/lib/stores/contentStore.test.ts +++ b/src/lib/stores/contentStore.test.ts @@ -0,0 +1,63 @@ +import { get } from 'svelte/store'; +import { describe, expect, it } from 'vitest'; +import { + addContent, + content, + removeContent, + type ChildrenSurvey, + type ContentList, + type RegistrationForm +} from './contentStore'; + +describe('test_normal_functionality', async () => { + const mockChildSurvey: ChildrenSurvey = { + can_do_this: 'incomplete', + can_do_that: 'fully', + can_do_the_other: 'no' + }; + + const mockRegistrationForm: RegistrationForm = { + name: 'Tom', + gender: 'male' + }; + + const mockContentList: ContentList = { + childrenSurveys: { + testsurvey: mockChildSurvey + }, + registrationForms: { + testregistration: mockRegistrationForm + } + }; + + it('should add content successfully when key is not yet there', async () => { + content.set(mockContentList); + + await addContent('childrenSurveys', 'dummySurvey', mockChildSurvey); + await addContent('registrationForms', 'childRegistration', mockRegistrationForm); + + expect(get(content)['childrenSurveys']['dummySurvey']).toEqual(mockChildSurvey); + expect(get(content)['registrationForms']['childRegistration']).toEqual(mockRegistrationForm); + }); + + it('should remove content from the contentstore successfully', async () => { + content.set(mockContentList); + await removeContent('childrenSurveys', 'testsurvey'); + // await removeContent('registrationForms', 'childRegistration'); + + expect(get(content)['childrenSurveys']['testsurvey']).toBeUndefined(); + // expect(get(content)['registrationForms']['childRegistration']).toBeUndefined(); + }); + + // it('throw error when adding an element with an existing key', async () => { + // content.set(mockContentList); + + // try { + // await removeContent('childrenSurveys', 'notthere'); + // } catch (error: Error | unknown) { + // expect((error as Error).message).toBe('No such key in the contentstore'); + // } + // }); + + it('throw error when removing an element with a non-existing key', async () => {}); +}); diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 0f7e5695..89ac174e 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -5,13 +5,13 @@ interface ChildrenSurvey { [name: string]: unknown; } -interface RegistrationForms { +interface RegistrationForm { [name: string]: unknown; } interface ContentList { childrenSurveys: ChildrenSurvey; - registrationForms: RegistrationForms; + registrationForms: RegistrationForm; } const contentlist: ContentList = { @@ -23,18 +23,42 @@ const contentlist: ContentList = { const content = writable(contentlist); // functions to add and remove stuff from the store -function addContent(type: string, key: string) { +async function addContent( + type: string, + key: string, + new_content: ChildrenSurvey | RegistrationForm +) { content.update((contentlist: ContentList) => { - contentlist[type as keyof ContentList][key] = content; + contentlist[type as keyof ContentList][key] = new_content; return contentlist; }); } -function removeContent(type: string, key: string) { +async function removeContent(type: string, key: string) { + if (!(type in contentlist)) { + throw new Error('No such register in the contentstore'); + } + console.log('removeContent', type, key, Object.keys(contentlist[type])); + + if (!(key in contentlist[type as keyof ContentList])) { + throw new Error('No such key in the contentstore'); + } content.update((contentlist) => { delete contentlist[type as keyof ContentList][key]; return contentlist; }); } -export { addContent, content, removeContent }; +async function dummyAdd(a: number, b: number) { + return a + b; +} + +export { + addContent, + content, + dummyAdd, + removeContent, + type ChildrenSurvey, + type ContentList, + type RegistrationForm +}; diff --git a/src/lib/stores/userStore.test.ts b/src/lib/stores/userStore.test.ts index a1c0fd87..d135342a 100644 --- a/src/lib/stores/userStore.test.ts +++ b/src/lib/stores/userStore.test.ts @@ -1,12 +1,7 @@ -// userStore.test.ts - import { get } from 'svelte/store'; import { describe, expect, it } from 'vitest'; import { addUser, removeUser, type UserData, users } from './userStore'; -// Mock user data - -// Test suite for userStore describe('test_normal_function', () => { const mockUserToken = 'mockUserToken'; const mockUserData: UserData = { From 36b21a2201f4cbd6734d88841c2da6d7243a9b80 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 14:14:59 +0200 Subject: [PATCH 09/39] add tests --- src/lib/stores/childrenStore.test.ts | 0 src/lib/stores/contentStore.test.ts | 31 ++++++++++++++++++---------- src/lib/stores/contentStore.ts | 7 ++----- 3 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 src/lib/stores/childrenStore.test.ts diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/stores/contentStore.test.ts b/src/lib/stores/contentStore.test.ts index cadeeeee..c3d7caee 100644 --- a/src/lib/stores/contentStore.test.ts +++ b/src/lib/stores/contentStore.test.ts @@ -42,22 +42,31 @@ describe('test_normal_functionality', async () => { it('should remove content from the contentstore successfully', async () => { content.set(mockContentList); + console.log(get(content)); await removeContent('childrenSurveys', 'testsurvey'); - // await removeContent('registrationForms', 'childRegistration'); + await removeContent('registrationForms', 'childRegistration'); expect(get(content)['childrenSurveys']['testsurvey']).toBeUndefined(); - // expect(get(content)['registrationForms']['childRegistration']).toBeUndefined(); + expect(get(content)['registrationForms']['childRegistration']).toBeUndefined(); }); - // it('throw error when adding an element with an existing key', async () => { - // content.set(mockContentList); + it('throw error when removing an element with an existing key', async () => { + content.set(mockContentList); - // try { - // await removeContent('childrenSurveys', 'notthere'); - // } catch (error: Error | unknown) { - // expect((error as Error).message).toBe('No such key in the contentstore'); - // } - // }); + try { + await removeContent('childrenSurveys', 'notthere'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such key in the contentstore'); + } + }); - it('throw error when removing an element with a non-existing key', async () => {}); + it('throw error when adding an element with an already present key', async () => { + content.set(mockContentList); + + try { + await addContent('childrenSurveys', 'testsurvey', mockChildSurvey); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such key in the contentstore'); + } + }); }); diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 89ac174e..6225c221 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -38,6 +38,8 @@ async function removeContent(type: string, key: string) { if (!(type in contentlist)) { throw new Error('No such register in the contentstore'); } + console.log('removeContent', type, key, Object.keys(contentlist)); + console.log('removeContent', type, key, Object.keys(contentlist[type])); if (!(key in contentlist[type as keyof ContentList])) { @@ -49,14 +51,9 @@ async function removeContent(type: string, key: string) { }); } -async function dummyAdd(a: number, b: number) { - return a + b; -} - export { addContent, content, - dummyAdd, removeContent, type ChildrenSurvey, type ContentList, From db7486a543751a1faa6cd3c34355a987b6017f74 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 14:55:18 +0200 Subject: [PATCH 10/39] finish tests --- src/lib/stores/childrenStore.test.ts | 83 ++++++++++++++++++++++++++++ src/lib/stores/childrenStore.ts | 25 ++++----- src/lib/stores/contentStore.test.ts | 8 +-- src/lib/stores/contentStore.ts | 23 ++++---- 4 files changed, 109 insertions(+), 30 deletions(-) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index e69de29b..74ba7238 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -0,0 +1,83 @@ +import { get } from 'svelte/store'; +import { describe, expect, it } from 'vitest'; +import { + addChildData, + children, + removeChildData, + type ChildData, + type ChildrenList +} from './childrenStore'; + +describe('normal functionality', () => { + const mockChildData: ChildData = { + name: 'foo', + age: 3, + nationality: 'turkish' + }; + const mockChildData2: ChildData = { + name: 'bar', + age: 5, + nationality: 'german' + }; + + const mockChildData3: ChildData = { + name: 'baz', + age: 2, + nationality: 'british' + }; + + const mockChildList: ChildrenList = { + alpha: { + childA: mockChildData, + childB: mockChildData2 + }, + beta: { + childA: mockChildData + } + }; + + it('should add child successfully', async () => { + children.set(mockChildList); + await addChildData(mockChildData3, 'alpha', 'childC'); + + expect(get(children)['alpha']['childC']).toEqual(mockChildData3); + }); + + it('should remove child successfully', async () => { + children.set(mockChildList); + await removeChildData('beta', 'childA'); + expect(get(children)['beta']['childA']).toEqual(undefined); + }); + + it('should throw when adding with nonexistant user or existing child key', async () => { + children.set(mockChildList); + try { + await addChildData(mockChildData3, 'alpha', 'childA'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe( + 'Child token childA already exists for user token alpha' + ); + } + + try { + await addChildData(mockChildData3, 'x', 'childA'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('User token x not found'); + } + }); + + it('should throw when adding with nonexistant user or existing child key', async () => { + children.set(mockChildList); + try { + await removeChildData('x', 'childA'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('User token x not found'); + } + + try { + await removeChildData('alpha', 'notthere'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('Child token notthere not found for user token alpha'); + } + }); +}); diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index ad9c0922..48e438ce 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -1,7 +1,7 @@ import { writable } from 'svelte/store'; interface ChildData { - [key: string]: string[] | string; + [key: string]: unknown; } interface ChildrenList { @@ -12,30 +12,25 @@ interface ChildrenList { const childrenlist: ChildrenList = {}; -const childrenData = writable(childrenlist); +const children = writable(childrenlist); -async function addChildData(data: ChildData, childtoken: string, usertoken: string) { - childrenData.update((childrenlist) => { +async function addChildData(data: ChildData, usertoken: string, childtoken: string) { + children.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); } - if (!(childtoken in childrenlist[usertoken])) { - throw new Error(`Child token ${childtoken} not found for user token ${usertoken}`); + if (childtoken in childrenlist[usertoken]) { + throw new Error(`Child token ${childtoken} already exists for user token ${usertoken}`); } + childrenlist[usertoken][childtoken] = data; - data['token'] = usertoken; - if (usertoken in childrenlist) { - childrenlist[usertoken][childtoken] = data; - } else { - childrenlist[usertoken][childtoken] = data; - } return childrenlist; }); } -async function removeChildData(childtoken: string, usertoken: string) { - childrenData.update((childrenlist) => { +async function removeChildData(usertoken: string, childtoken: string) { + children.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); } @@ -50,4 +45,4 @@ async function removeChildData(childtoken: string, usertoken: string) { }); } -export { addChildData, childrenData, removeChildData }; +export { addChildData, children, removeChildData, type ChildData, type ChildrenList }; diff --git a/src/lib/stores/contentStore.test.ts b/src/lib/stores/contentStore.test.ts index c3d7caee..02356bc2 100644 --- a/src/lib/stores/contentStore.test.ts +++ b/src/lib/stores/contentStore.test.ts @@ -32,7 +32,6 @@ describe('test_normal_functionality', async () => { it('should add content successfully when key is not yet there', async () => { content.set(mockContentList); - await addContent('childrenSurveys', 'dummySurvey', mockChildSurvey); await addContent('registrationForms', 'childRegistration', mockRegistrationForm); @@ -42,12 +41,11 @@ describe('test_normal_functionality', async () => { it('should remove content from the contentstore successfully', async () => { content.set(mockContentList); - console.log(get(content)); await removeContent('childrenSurveys', 'testsurvey'); - await removeContent('registrationForms', 'childRegistration'); + await removeContent('registrationForms', 'testregistration'); expect(get(content)['childrenSurveys']['testsurvey']).toBeUndefined(); - expect(get(content)['registrationForms']['childRegistration']).toBeUndefined(); + expect(get(content)['registrationForms']['testregistration']).toBeUndefined(); }); it('throw error when removing an element with an existing key', async () => { @@ -66,7 +64,7 @@ describe('test_normal_functionality', async () => { try { await addContent('childrenSurveys', 'testsurvey', mockChildSurvey); } catch (error: Error | unknown) { - expect((error as Error).message).toBe('No such key in the contentstore'); + expect((error as Error).message).toBe('Key already exists in the contentstore'); } }); }); diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 6225c221..f61aeb88 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -29,23 +29,26 @@ async function addContent( new_content: ChildrenSurvey | RegistrationForm ) { content.update((contentlist: ContentList) => { + if (!(type in contentlist)) { + throw new Error('No such register in the contentstore'); + } + + if (key in contentlist[type]) { + throw new Error('Key already exists in the contentstore'); + } contentlist[type as keyof ContentList][key] = new_content; return contentlist; }); } async function removeContent(type: string, key: string) { - if (!(type in contentlist)) { - throw new Error('No such register in the contentstore'); - } - console.log('removeContent', type, key, Object.keys(contentlist)); - - console.log('removeContent', type, key, Object.keys(contentlist[type])); - - if (!(key in contentlist[type as keyof ContentList])) { - throw new Error('No such key in the contentstore'); - } content.update((contentlist) => { + if (!(type in contentlist)) { + throw new Error('No such register in the contentstore'); + } + if (!(key in contentlist[type as keyof ContentList])) { + throw new Error('No such key in the contentstore'); + } delete contentlist[type as keyof ContentList][key]; return contentlist; }); From d903627af3d9db1b0e551145f4e9a9ce75ddfbe7 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 14:57:50 +0200 Subject: [PATCH 11/39] correct name of tests --- src/lib/stores/childrenStore.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index 74ba7238..88258edb 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -66,7 +66,7 @@ describe('normal functionality', () => { } }); - it('should throw when adding with nonexistant user or existing child key', async () => { + it('should throw when removing with nonexistant user or nonexisting child key', async () => { children.set(mockChildList); try { await removeChildData('x', 'childA'); From 386b6b5d9f9bea4f19ebbf3d8ca743b0ebd431ff Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 15:48:06 +0200 Subject: [PATCH 12/39] add dummy data and more functions to childrenstore --- src/lib/components/Childrenpage.svelte | 3 +- src/lib/stores/childrenStore.ts | 171 ++++++++++++++++++++++++- src/lib/stores/contentStore.ts | 20 ++- 3 files changed, 188 insertions(+), 6 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index d3891579..cc350cc3 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -5,7 +5,8 @@ header: item.name, summary: item.info, image: item.image, - href: item.href ? item.href : '/' + observationData: item.observationData, + href: '/trafficlightfeedback' }; }); diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 48e438ce..636a5179 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -1,6 +1,12 @@ -import { writable } from 'svelte/store'; +import { get, writable } from 'svelte/store'; + +interface ObservationData { + summary: object; + current: object; +} interface ChildData { + observationData: ObservationData; [key: string]: unknown; } @@ -14,7 +20,19 @@ const childrenlist: ChildrenList = {}; const children = writable(childrenlist); -async function addChildData(data: ChildData, usertoken: string, childtoken: string) { +async function addUser(usertoken: string) { + children.update((childrenlist) => { + if (usertoken in childrenlist) { + throw new Error(`User token ${usertoken} already exists`); + } + + childrenlist[usertoken] = {}; + + return childrenlist; + }); +} + +async function addChildData(usertoken: string, childtoken: string, data: ChildData) { children.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); @@ -45,4 +63,151 @@ async function removeChildData(usertoken: string, childtoken: string) { }); } -export { addChildData, children, removeChildData, type ChildData, type ChildrenList }; +async function fetchChildData(usertoken: string, childtoken: string) { + const contentData = get(children); + + if (!(usertoken in contentData)) { + throw new Error('No such user in the childrenstore'); + } + + if (!(childtoken in contentData[usertoken as keyof ChildrenList])) { + throw new Error('No such child in the childrenstore for user' + usertoken); + } + + return contentData[usertoken as keyof ChildrenList][childtoken]; +} + +// hardcode dummy child-data which later will live in the database on the server and be fetched from there. + +const dummyObservationData = { + summary: { + surveyA: { + '05-11-2017': 'good', + '08-05-2016': 'bad', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'warn', + '01-06-2022': 'good', + '30-12-2021': 'bad', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + suveryB: { + '05-11-2017': 'bad', + '08-05-2016': 'warn', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'bad', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + surveyC: { + '05-11-2017': 'warn', + '08-05-2016': 'warn', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'bad', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'bad' + }, + surveyD: { + '05-11-2017': 'good', + '08-05-2016': 'good', + '22-07-2012': 'good', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + surveyE: { + '05-11-2017': 'good', + '08-05-2016': 'warn', + '22-07-2012': 'warn', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'warn', + '30-10-2021': 'good' + } + }, + current: { + surveyA: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ], + surveyB: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ], + surveyC: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ] + } +}; + +await addUser('dummyUser'); + +await addChildData('dummyUser', 'childAnna', { + name: 'Anna', + image: 'child_avatar.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); +await addChildData('dummyUser', 'childBen', { + name: 'Ben', + image: 'child_avatar.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); +await addChildData('dummyUser', 'childC', { + name: 'C', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); +await addChildData('dummyUser', 'childDora', { + name: 'Dora', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); +await addChildData('dummyUser', 'childE', { + name: 'E', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); +await addChildData('dummyUser', 'childF', { + name: 'F', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.', + observationData: dummyObservationData +}); + +export { + addChildData, + addUser, + children, + fetchChildData, + removeChildData, + type ChildData, + type ChildrenList, + type ObservationData +}; diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index f61aeb88..03669aab 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -1,4 +1,4 @@ -import { writable } from 'svelte/store'; +import { get, writable } from 'svelte/store'; // types interface ChildrenSurvey { @@ -33,7 +33,7 @@ async function addContent( throw new Error('No such register in the contentstore'); } - if (key in contentlist[type]) { + if (key in contentlist[type as keyof ContentList]) { throw new Error('Key already exists in the contentstore'); } contentlist[type as keyof ContentList][key] = new_content; @@ -54,9 +54,25 @@ async function removeContent(type: string, key: string) { }); } +async function fetchContent(type: string, key: string) { + const contentData = get(content); + if (!(type in contentData)) { + throw new Error('No such register in the contentstore'); + } + + if (!(key in contentData[type as keyof ContentList])) { + throw new Error('No such key in the contentstore'); + } + + return contentData[type as keyof ContentList][key]; +} + +// this hardcodes some dummy content data. + export { addContent, content, + fetchContent, removeContent, type ChildrenSurvey, type ContentList, From 926228dc08955d4adf0c1ee575a435466d3d064f Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 16:34:16 +0200 Subject: [PATCH 13/39] use childrenstore in childrenpage overview --- src/lib/components/Childrenpage.svelte | 82 ++----- src/lib/stores/childrenStore.ts | 328 +++++++++++++++---------- 2 files changed, 230 insertions(+), 180 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index cc350cc3..b69ce6d4 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -5,7 +5,6 @@ header: item.name, summary: item.info, image: item.image, - observationData: item.observationData, href: '/trafficlightfeedback' }; }); @@ -48,66 +47,37 @@ } - - - Übersicht + +Übersicht -
-

- Wählen sie ein Kind zur Beobachtung aus oder legen melden sie ein neues Kind an. -

- -
-
+
+

+ Wählen sie ein Kind zur Beobachtung aus oder legen melden sie ein neues Kind an. +

+ +
+ diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 636a5179..2900e4a2 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -6,13 +6,17 @@ interface ObservationData { } interface ChildData { - observationData: ObservationData; [key: string]: unknown; } +interface ChildObject { + childData: ChildData; + observationData: ObservationData | null; +} + interface ChildrenList { [usertoken: string]: { - [childtoken: string]: ChildData; + [childtoken: string]: ChildObject; }; } @@ -33,6 +37,8 @@ async function addUser(usertoken: string) { } async function addChildData(usertoken: string, childtoken: string, data: ChildData) { + // /API calls/ fetch requests could go here. + children.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); @@ -41,13 +47,36 @@ async function addChildData(usertoken: string, childtoken: string, data: ChildDa if (childtoken in childrenlist[usertoken]) { throw new Error(`Child token ${childtoken} already exists for user token ${usertoken}`); } - childrenlist[usertoken][childtoken] = data; + + childrenlist[usertoken][childtoken] = { childData: data, observationData: null }; + + return childrenlist; + }); +} + +async function addChildObservation( + usertoken: string, + childtoken: string, + observationData: ObservationData +) { + children.update((childrenlist) => { + if (!(usertoken in childrenlist)) { + throw new Error(`User token ${usertoken} not found`); + } + + if (!(childtoken in childrenlist[usertoken])) { + throw new Error(`Child token ${childtoken} does not exist for user token ${usertoken}`); + } + + childrenlist[usertoken][childtoken].observationData = observationData; return childrenlist; }); } async function removeChildData(usertoken: string, childtoken: string) { + // /API calls/ fetch requests could go here. + children.update((childrenlist) => { if (!(usertoken in childrenlist)) { throw new Error(`User token ${usertoken} not found`); @@ -64,6 +93,22 @@ async function removeChildData(usertoken: string, childtoken: string) { } async function fetchChildData(usertoken: string, childtoken: string) { + // /API calls/ fetch requests could go here. + const contentData = get(children); + + if (!(usertoken in contentData)) { + throw new Error('No such user in the childrenstore'); + } + + if (!(childtoken in contentData[usertoken as keyof ChildrenList])) { + throw new Error('No such child in the childrenstore for user ' + usertoken); + } + + return contentData[usertoken as keyof ChildrenList][childtoken].childData; +} + +async function fetchObservationData(usertoken: string, childtoken: string) { + // /API calls/ fetch requests could go here. const contentData = get(children); if (!(usertoken in contentData)) { @@ -74,138 +119,173 @@ async function fetchChildData(usertoken: string, childtoken: string) { throw new Error('No such child in the childrenstore for user' + usertoken); } - return contentData[usertoken as keyof ChildrenList][childtoken]; + return contentData[usertoken as keyof ChildrenList][childtoken].observationData; } -// hardcode dummy child-data which later will live in the database on the server and be fetched from there. - -const dummyObservationData = { - summary: { - surveyA: { - '05-11-2017': 'good', - '08-05-2016': 'bad', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'warn', - '01-06-2022': 'good', - '30-12-2021': 'bad', - '30-11-2021': 'good', - '30-10-2021': 'good' - }, - suveryB: { - '05-11-2017': 'bad', - '08-05-2016': 'warn', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'bad', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'good' - }, - surveyC: { - '05-11-2017': 'warn', - '08-05-2016': 'warn', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'bad', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'bad' - }, - surveyD: { - '05-11-2017': 'good', - '08-05-2016': 'good', - '22-07-2012': 'good', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'good' +async function fetchChildrenDataforUser(usertoken: string) { + const contentData = get(children); + if (!(usertoken in contentData)) { + throw new Error('No such user in the childrenstore'); + } + + return Object.keys(contentData[usertoken]).map((child) => { + return contentData[usertoken][child].childData; + }); +} +async function fetchObservationDataForUser(usertoken: string) { + const contentData = get(children); + if (!(usertoken in contentData)) { + throw new Error('No such user in the childrenstore'); + } + + return Object.keys(contentData[usertoken]).map((child) => { + return contentData[usertoken][child].observationData; + }); +} + +// --> README: +// hardcode dummy child data which later will live in the database on the server and be fetched from there via API calls +// the stuff below is therefore temporary and is an approximation of the datastructure that may be returned perhaps from the backend, +// and thus is subject to change +async function createDummyData() { + const dummyObservationData = { + summary: { + surveyA: { + '05-11-2017': 'good', + '08-05-2016': 'bad', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'warn', + '01-06-2022': 'good', + '30-12-2021': 'bad', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + suveryB: { + '05-11-2017': 'bad', + '08-05-2016': 'warn', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'bad', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + surveyC: { + '05-11-2017': 'warn', + '08-05-2016': 'warn', + '22-07-2012': 'bad', + '11-11-2020': 'warn', + '30-03-2019': 'bad', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'bad' + }, + surveyD: { + '05-11-2017': 'good', + '08-05-2016': 'good', + '22-07-2012': 'good', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'good', + '30-10-2021': 'good' + }, + surveyE: { + '05-11-2017': 'good', + '08-05-2016': 'warn', + '22-07-2012': 'warn', + '11-11-2020': 'warn', + '30-03-2019': 'good', + '01-06-2022': 'good', + '30-12-2021': 'good', + '30-11-2021': 'warn', + '30-10-2021': 'good' + } }, - surveyE: { - '05-11-2017': 'good', - '08-05-2016': 'warn', - '22-07-2012': 'warn', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'warn', - '30-10-2021': 'good' + current: { + surveyA: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ], + surveyB: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ], + surveyC: [ + { name: 'milestoneA', status: 'done' }, + { name: 'milestoneB', status: 'open' }, + { name: 'milestoneC', status: 'open' }, + { name: 'milestoneD', status: 'incomplete' }, + { name: 'milestoneE', status: 'done' } + ] } - }, - current: { - surveyA: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ], - surveyB: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ], - surveyC: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ] - } -}; + }; + await addUser('dummyUser'); + + await addChildData('dummyUser', 'childAnna', { + name: 'Anna', + image: 'child_avatar.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childAnna', dummyObservationData); + + await addChildData('dummyUser', 'childBen', { + name: 'Ben', + image: 'child_avatar.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childBen', dummyObservationData); + + await addChildData('dummyUser', 'childC', { + name: 'C', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childC', dummyObservationData); + + await addChildData('dummyUser', 'childDora', { + name: 'Dora', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childDora', dummyObservationData); + + await addChildData('dummyUser', 'childE', { + name: 'E', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childE', dummyObservationData); + + await addChildData('dummyUser', 'childF', { + name: 'F', + image: 'children.png', + info: 'A child that is making a mess and is doing good. Click to view more.' + }); + await addChildObservation('dummyUser', 'childF', dummyObservationData); +} -await addUser('dummyUser'); - -await addChildData('dummyUser', 'childAnna', { - name: 'Anna', - image: 'child_avatar.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); -await addChildData('dummyUser', 'childBen', { - name: 'Ben', - image: 'child_avatar.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); -await addChildData('dummyUser', 'childC', { - name: 'C', - image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); -await addChildData('dummyUser', 'childDora', { - name: 'Dora', - image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); -await addChildData('dummyUser', 'childE', { - name: 'E', - image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); -await addChildData('dummyUser', 'childF', { - name: 'F', - image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.', - observationData: dummyObservationData -}); +// <-- export { addChildData, addUser, children, + createDummyData, fetchChildData, + fetchChildrenDataforUser, + fetchObservationData, + fetchObservationDataForUser, removeChildData, type ChildData, type ChildrenList, From a4376d02d3b694bddf0f18b4d4f98c99c8026bd3 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 16:42:59 +0200 Subject: [PATCH 14/39] add comments --- src/lib/stores/childrenStore.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 2900e4a2..1268b1ce 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -1,5 +1,8 @@ import { get, writable } from 'svelte/store'; +// README: this API is experimental and not by any means a final design + +// Types interface ObservationData { summary: object; current: object; @@ -20,6 +23,7 @@ interface ChildrenList { }; } +// store itself: README: const childrenlist: ChildrenList = {}; const children = writable(childrenlist); From 890fae5d81bee6e0a7ae23c78f14823266b6ce43 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 16:50:37 +0200 Subject: [PATCH 15/39] correct existing tests --- src/lib/stores/childrenStore.test.ts | 52 ++++++++++++++++++---------- src/lib/stores/childrenStore.ts | 1 + 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index 88258edb..7a32b68e 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -4,26 +4,41 @@ import { addChildData, children, removeChildData, - type ChildData, - type ChildrenList + type ChildObject, + type ChildrenList, + type ObservationData } from './childrenStore'; describe('normal functionality', () => { - const mockChildData: ChildData = { - name: 'foo', - age: 3, - nationality: 'turkish' + const mockObservationData: ObservationData = { + current: ['a', 'b', 'c'], + summary: ['x', 'y'] }; - const mockChildData2: ChildData = { - name: 'bar', - age: 5, - nationality: 'german' + + const mockChildData: ChildObject = { + childData: { + name: 'foo', + age: 3, + nationality: 'turkish' + }, + observationData: mockObservationData + }; + const mockChildData2: ChildObject = { + childData: { + name: 'bar', + age: 5, + nationality: 'german' + }, + observationData: mockObservationData }; - const mockChildData3: ChildData = { - name: 'baz', - age: 2, - nationality: 'british' + const mockChildData3: ChildObject = { + childData: { + name: 'baz', + age: 2, + nationality: 'british' + }, + observationData: mockObservationData }; const mockChildList: ChildrenList = { @@ -38,9 +53,8 @@ describe('normal functionality', () => { it('should add child successfully', async () => { children.set(mockChildList); - await addChildData(mockChildData3, 'alpha', 'childC'); - - expect(get(children)['alpha']['childC']).toEqual(mockChildData3); + await addChildData('alpha', 'childC', mockChildData3.childData); + expect(get(children)['alpha']['childC'].childData).toEqual(mockChildData3.childData); }); it('should remove child successfully', async () => { @@ -52,7 +66,7 @@ describe('normal functionality', () => { it('should throw when adding with nonexistant user or existing child key', async () => { children.set(mockChildList); try { - await addChildData(mockChildData3, 'alpha', 'childA'); + await addChildData('alpha', 'childA', mockChildData3.childData); } catch (error: Error | unknown) { expect((error as Error).message).toBe( 'Child token childA already exists for user token alpha' @@ -60,7 +74,7 @@ describe('normal functionality', () => { } try { - await addChildData(mockChildData3, 'x', 'childA'); + await addChildData('x', 'childA', mockChildData3.childData); } catch (error: Error | unknown) { expect((error as Error).message).toBe('User token x not found'); } diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 1268b1ce..af5e6028 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -292,6 +292,7 @@ export { fetchObservationDataForUser, removeChildData, type ChildData, + type ChildObject, type ChildrenList, type ObservationData }; From 84473ff8f55ac8a2b9cb1c59ccfd64428d3bb0fe Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 16:57:03 +0200 Subject: [PATCH 16/39] add more tests --- src/lib/stores/childrenStore.test.ts | 31 ++++++++++++++++++++++++++++ src/lib/stores/childrenStore.ts | 1 + 2 files changed, 32 insertions(+) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index 7a32b68e..487afdd3 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -2,6 +2,7 @@ import { get } from 'svelte/store'; import { describe, expect, it } from 'vitest'; import { addChildData, + addChildObservation, children, removeChildData, type ChildObject, @@ -57,6 +58,36 @@ describe('normal functionality', () => { expect(get(children)['alpha']['childC'].childData).toEqual(mockChildData3.childData); }); + it('should add child observationdata successfully', async () => { + children.set(mockChildList); + await addChildData('alpha', 'childC', mockChildData3.childData); + await addChildObservation('alpha', 'childC', mockChildData3.observationData); + + expect(get(children)['alpha']['childC'].observationData).toEqual( + mockChildData3.observationData + ); + }); + + it('cannot assign observationdata when childData is missing', async () => { + children.set(mockChildList); + try { + await addChildObservation('alpha', 'childC', mockChildData3.observationData); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe( + 'Child token childC does not exist for user token alpha' + ); + } + }); + + it('cannot assign observationdata for unknown user', async () => { + children.set(mockChildList); + try { + await addChildObservation('x', 'childC', mockChildData3.observationData); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('User token x not found'); + } + }); + it('should remove child successfully', async () => { children.set(mockChildList); await removeChildData('beta', 'childA'); diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index af5e6028..52b7f31c 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -283,6 +283,7 @@ async function createDummyData() { export { addChildData, + addChildObservation, addUser, children, createDummyData, From 1f1b6c1da907a82176daee689d95a0eaa63172a6 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Tue, 27 Aug 2024 17:03:30 +0200 Subject: [PATCH 17/39] add more tests --- src/lib/stores/childrenStore.test.ts | 38 ++++++++++++++++++++++++++++ src/lib/stores/childrenStore.ts | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index 487afdd3..e56372db 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -4,6 +4,8 @@ import { addChildData, addChildObservation, children, + fetchChildData, + fetchObservationData, removeChildData, type ChildObject, type ChildrenList, @@ -125,4 +127,40 @@ describe('normal functionality', () => { expect((error as Error).message).toBe('Child token notthere not found for user token alpha'); } }); + + it('should fetch observation data', async () => { + children.set(mockChildList); + expect(await fetchObservationData('alpha', 'childA')).toEqual(mockObservationData); + }); + + it('should fetch child data', async () => { + children.set(mockChildList); + expect(await fetchChildData('alpha', 'childA')).toEqual({ + name: 'foo', + age: 3, + nationality: 'turkish' + }); + }); + + it('cannot fetch from uknown keys', async () => { + children.set(mockChildList); + + try { + await fetchObservationData('x', 'childA'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such user in the childrenstore'); + } + + try { + await fetchObservationData('alpha', 'unknown'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such child in the childrenstore for user alpha'); + } + }); + + it('should fetch list of childrendata', async () => {}); + + it('cannot fetch childrendata from uknown', async () => {}); + + it('cannot fetch observationdata from uknown', async () => {}); }); diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 52b7f31c..98151fab 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -120,7 +120,7 @@ async function fetchObservationData(usertoken: string, childtoken: string) { } if (!(childtoken in contentData[usertoken as keyof ChildrenList])) { - throw new Error('No such child in the childrenstore for user' + usertoken); + throw new Error('No such child in the childrenstore for user ' + usertoken); } return contentData[usertoken as keyof ChildrenList][childtoken].observationData; From 66a8268a9a055aecb681ce8212711b567658e08d Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 09:01:42 +0200 Subject: [PATCH 18/39] use subscribe in childrenpage --- src/lib/components/Childrenpage.svelte | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index b69ce6d4..2ce8bfe4 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -50,23 +50,31 @@ - Übersicht
@@ -80,4 +88,3 @@ componentProps={createStyle(data)} />
- From 315107d7e2cd11dd98b1f61fe62f003160e8399b Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 09:05:53 +0200 Subject: [PATCH 19/39] add unsubscribe --- src/lib/components/Childrenpage.svelte | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index 2ce8bfe4..0dddcc7e 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -58,21 +58,24 @@ type ChildrenList } from '$lib/stores/childrenStore'; import { Heading } from 'flowbite-svelte'; - import { onMount } from 'svelte'; + import { onDestroy, onMount } from 'svelte'; // create data and let data: ChildData[] = []; - children.subscribe(async (childlist: ChildrenList) => { + const unsubscribe = children.subscribe(async (childlist: ChildrenList) => { let rawdata = await fetchChildrenDataforUser('dummyUser'); data = convertData(rawdata); }); - // this fetches dummy child data from the database for the dummy user + // this fetches dummy child data for the dummy user whenever the component is mounted into the dom + // it is conceptualized as emulating an API call that would normally fetch this from the server. onMount(async () => { children.set({}); await createDummyData(); }); + + onDestroy(unsubscribe); Übersicht From 9d9725824f9e69ecf26727b12c766d6d5884b3e4 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 09:56:07 +0200 Subject: [PATCH 20/39] finish childrenstore test --- src/lib/stores/childrenStore.test.ts | 50 ++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/lib/stores/childrenStore.test.ts b/src/lib/stores/childrenStore.test.ts index e56372db..b85cb491 100644 --- a/src/lib/stores/childrenStore.test.ts +++ b/src/lib/stores/childrenStore.test.ts @@ -5,7 +5,9 @@ import { addChildObservation, children, fetchChildData, + fetchChildrenDataforUser, fetchObservationData, + fetchObservationDataForUser, removeChildData, type ChildObject, type ChildrenList, @@ -158,9 +160,51 @@ describe('normal functionality', () => { } }); - it('should fetch list of childrendata', async () => {}); + it('should fetch list of childrendata', async () => { + children.set(mockChildList); + const data = await fetchChildrenDataforUser('alpha'); + + expect(data).toEqual([ + { + name: 'bar', + age: 5, + nationality: 'german' + }, + { + name: 'foo', + age: 3, + nationality: 'turkish' + } + ]); + }); + + it('should fetch list of observationdata successfully', async () => { + children.set(mockChildList); + const data = await fetchObservationDataForUser('alpha'); + + expect(data).toEqual([ + ['childA', mockObservationData], + ['childB', mockObservationData] + ]); + }); + + it('cannot fetch childrendata from uknown', async () => { + children.set(mockChildList); + + try { + await fetchChildrenDataforUser('x'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such user in the childrenstore'); + } + }); - it('cannot fetch childrendata from uknown', async () => {}); + it('cannot fetch observationdata from uknown', async () => { + children.set(mockChildList); - it('cannot fetch observationdata from uknown', async () => {}); + try { + fetchObservationDataForUser('x'); + } catch (error: Error | unknown) { + expect((error as Error).message).toBe('No such user in the childrenstore'); + } + }); }); From 549aacb9997878850c1ddda0c7b8fd37b9c3420e Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 09:56:20 +0200 Subject: [PATCH 21/39] correct some mistatkes --- src/lib/components/Childrenpage.svelte | 43 ++++++++++++++------------ src/lib/stores/childrenStore.ts | 13 +++++--- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index 0dddcc7e..26bece61 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -54,40 +54,43 @@ children, createDummyData, fetchChildrenDataforUser, - type ChildData, - type ChildrenList + type ChildData } from '$lib/stores/childrenStore'; import { Heading } from 'flowbite-svelte'; - import { onDestroy, onMount } from 'svelte'; + import { onMount } from 'svelte'; // create data and let data: ChildData[] = []; + let loading = true; - const unsubscribe = children.subscribe(async (childlist: ChildrenList) => { + async function init() { + loading = true; + children.set({}); + await createDummyData(); let rawdata = await fetchChildrenDataforUser('dummyUser'); data = convertData(rawdata); - }); + loading = false; + } // this fetches dummy child data for the dummy user whenever the component is mounted into the dom // it is conceptualized as emulating an API call that would normally fetch this from the server. - onMount(async () => { - children.set({}); - await createDummyData(); - }); - - onDestroy(unsubscribe); + onMount(init); Übersicht
-

- Wählen sie ein Kind zur Beobachtung aus oder legen melden sie ein neues Kind an. -

- + {#if loading} +

Daten werden geladen...

+ {:else} +

+ Wählen sie ein Kind zur Beobachtung aus oder legen melden sie ein neues Kind an. +

+ + {/if}
diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 98151fab..9d936bd3 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -9,6 +9,7 @@ interface ObservationData { } interface ChildData { + name: string; [key: string]: unknown; } @@ -128,13 +129,17 @@ async function fetchObservationData(usertoken: string, childtoken: string) { async function fetchChildrenDataforUser(usertoken: string) { const contentData = get(children); + if (!(usertoken in contentData)) { throw new Error('No such user in the childrenstore'); } - return Object.keys(contentData[usertoken]).map((child) => { - return contentData[usertoken][child].childData; - }); + // sort them alphabetically + return Object.keys(contentData[usertoken]) + .map((child) => { + return contentData[usertoken][child].childData; + }) + .sort((a, b) => a.name.localeCompare(b.name)); } async function fetchObservationDataForUser(usertoken: string) { const contentData = get(children); @@ -143,7 +148,7 @@ async function fetchObservationDataForUser(usertoken: string) { } return Object.keys(contentData[usertoken]).map((child) => { - return contentData[usertoken][child].observationData; + return [child, contentData[usertoken][child].observationData]; }); } From 7c92a1748cb27a3b134600b30f56e1b7034ca3fb Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 10:24:04 +0200 Subject: [PATCH 22/39] make dummy data random and generate programmatically --- src/lib/stores/childrenStore.ts | 142 ++++++++++++++------------------ 1 file changed, 62 insertions(+), 80 deletions(-) diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 9d936bd3..4d0ac1b6 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -10,6 +10,7 @@ interface ObservationData { interface ChildData { name: string; + id: string; [key: string]: unknown; } @@ -156,93 +157,69 @@ async function fetchObservationDataForUser(usertoken: string) { // hardcode dummy child data which later will live in the database on the server and be fetched from there via API calls // the stuff below is therefore temporary and is an approximation of the datastructure that may be returned perhaps from the backend, // and thus is subject to change + +function getRandomInt(max: number) { + return Math.floor(Math.random() * max); +} + +function chooseRandom(values: string[]) { + return values[getRandomInt(values.length)]; +} + async function createDummyData() { - const dummyObservationData = { - summary: { - surveyA: { - '05-11-2017': 'good', - '08-05-2016': 'bad', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'warn', - '01-06-2022': 'good', - '30-12-2021': 'bad', - '30-11-2021': 'good', - '30-10-2021': 'good' - }, - suveryB: { - '05-11-2017': 'bad', - '08-05-2016': 'warn', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'bad', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'good' - }, - surveyC: { - '05-11-2017': 'warn', - '08-05-2016': 'warn', - '22-07-2012': 'bad', - '11-11-2020': 'warn', - '30-03-2019': 'bad', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'bad' - }, - surveyD: { - '05-11-2017': 'good', - '08-05-2016': 'good', - '22-07-2012': 'good', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'good', - '30-10-2021': 'good' - }, - surveyE: { - '05-11-2017': 'good', - '08-05-2016': 'warn', - '22-07-2012': 'warn', - '11-11-2020': 'warn', - '30-03-2019': 'good', - '01-06-2022': 'good', - '30-12-2021': 'good', - '30-11-2021': 'warn', - '30-10-2021': 'good' - } - }, - current: { - surveyA: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ], - surveyB: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ], - surveyC: [ - { name: 'milestoneA', status: 'done' }, - { name: 'milestoneB', status: 'open' }, - { name: 'milestoneC', status: 'open' }, - { name: 'milestoneD', status: 'incomplete' }, - { name: 'milestoneE', status: 'done' } - ] + const values = ['good', 'warn', 'bad']; + const dates = [ + '05-11-2017', + '08-05-2016', + '22-07-2012', + '11-11-2020', + '30-03-2019', + '01-06-2022', + '30-12-2021', + '30-11-2021', + '30-10-2021' + ]; + const surveys: string[] = ['surveyA', 'surveyB', 'surveyC', 'surveyD', 'surveyE']; + + const summary: { + [key: string]: { + [key: string]: string; + }; + } = {}; + + for (const s of surveys) { + summary[s] = {}; + + for (const date of dates) { + summary[s][date] = chooseRandom(values); + } + } + + const current: { [survey: string]: { name: string; status: string }[] } = {}; + + const milestones = ['milestoneA', 'milestoneB', 'milestoneC', 'milestoneD', 'milestoneE']; + const completionValues = ['done', 'open', 'incomplete']; + + for (const survey of surveys) { + current[survey] = []; + for (const milestone of milestones) { + current[survey].push({ + name: milestone, + status: chooseRandom(completionValues) + }); } + } + + const dummyObservationData = { + summary: summary, + current: current }; + await addUser('dummyUser'); await addChildData('dummyUser', 'childAnna', { name: 'Anna', + id: 'childAnna', image: 'child_avatar.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); @@ -250,6 +227,7 @@ async function createDummyData() { await addChildData('dummyUser', 'childBen', { name: 'Ben', + id: 'childBen', image: 'child_avatar.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); @@ -257,6 +235,7 @@ async function createDummyData() { await addChildData('dummyUser', 'childC', { name: 'C', + id: 'childC', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); @@ -264,6 +243,7 @@ async function createDummyData() { await addChildData('dummyUser', 'childDora', { name: 'Dora', + id: 'childDora', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); @@ -271,6 +251,7 @@ async function createDummyData() { await addChildData('dummyUser', 'childE', { name: 'E', + id: 'childE', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); @@ -278,6 +259,7 @@ async function createDummyData() { await addChildData('dummyUser', 'childF', { name: 'F', + id: 'childF', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); From a73a1613c2eca1e44a5716ebf336411a7ecd4e20 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 10:24:18 +0200 Subject: [PATCH 23/39] add dynamic routing --- src/lib/components/Childrenpage.svelte | 2 +- .../{trafficlightfeedback => childLand/[childID]}/+page.svelte | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) rename src/routes/{trafficlightfeedback => childLand/[childID]}/+page.svelte (98%) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index 26bece61..5393abb8 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -5,7 +5,7 @@ header: item.name, summary: item.info, image: item.image, - href: '/trafficlightfeedback' + href: `/childLand/${item.id}` }; }); diff --git a/src/routes/trafficlightfeedback/+page.svelte b/src/routes/childLand/[childID]/+page.svelte similarity index 98% rename from src/routes/trafficlightfeedback/+page.svelte rename to src/routes/childLand/[childID]/+page.svelte index 0df45b11..3cd589ea 100644 --- a/src/routes/trafficlightfeedback/+page.svelte +++ b/src/routes/childLand/[childID]/+page.svelte @@ -1,3 +1,6 @@ + + diff --git a/src/lib/components/DataInput/AbstractRegistrationForm.svelte b/src/lib/components/DataInput/AbstractRegistrationForm.svelte index 58a70097..1be48c16 100644 --- a/src/lib/components/DataInput/AbstractRegistrationForm.svelte +++ b/src/lib/components/DataInput/AbstractRegistrationForm.svelte @@ -5,7 +5,6 @@ export let heading = null; export let buttons = null; export let description = null; - console.log('props: ', props);
diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 4d0ac1b6..e34de742 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -4,6 +4,8 @@ import { get, writable } from 'svelte/store'; // Types interface ObservationData { + id: string; + user: string; summary: object; current: object; } @@ -11,6 +13,7 @@ interface ObservationData { interface ChildData { name: string; id: string; + user: string; [key: string]: unknown; } @@ -114,6 +117,7 @@ async function fetchChildData(usertoken: string, childtoken: string) { } async function fetchObservationData(usertoken: string, childtoken: string) { + // /API calls/ fetch requests could go here. const contentData = get(children); @@ -181,18 +185,19 @@ async function createDummyData() { ]; const surveys: string[] = ['surveyA', 'surveyB', 'surveyC', 'surveyD', 'surveyE']; - const summary: { - [key: string]: { - [key: string]: string; - }; - } = {}; + interface SummaryElement { + name: string; + [key: string]: unknown; + } + const summary: SummaryElement[] = []; for (const s of surveys) { - summary[s] = {}; + const element: SummaryElement = { name: s }; for (const date of dates) { - summary[s][date] = chooseRandom(values); + element[date] = chooseRandom(values); } + summary.push(element); } const current: { [survey: string]: { name: string; status: string }[] } = {}; @@ -210,62 +215,96 @@ async function createDummyData() { } } - const dummyObservationData = { - summary: summary, - current: current - }; - await addUser('dummyUser'); await addChildData('dummyUser', 'childAnna', { name: 'Anna', id: 'childAnna', + user: 'dummyUser', image: 'child_avatar.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childAnna', dummyObservationData); + await addChildObservation('dummyUser', 'childAnna', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); await addChildData('dummyUser', 'childBen', { name: 'Ben', id: 'childBen', + user: 'dummyUser', image: 'child_avatar.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childBen', dummyObservationData); + await addChildObservation('dummyUser', 'childBen', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); await addChildData('dummyUser', 'childC', { name: 'C', id: 'childC', + user: 'dummyUser', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childC', dummyObservationData); + await addChildObservation('dummyUser', 'childC', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); await addChildData('dummyUser', 'childDora', { name: 'Dora', id: 'childDora', + user: 'dummyUser', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childDora', dummyObservationData); + await addChildObservation('dummyUser', 'childDora', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); await addChildData('dummyUser', 'childE', { name: 'E', id: 'childE', + user: 'dummyUser', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childE', dummyObservationData); + await addChildObservation('dummyUser', 'childE', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); await addChildData('dummyUser', 'childF', { name: 'F', id: 'childF', + user: 'dummyUser', image: 'children.png', info: 'A child that is making a mess and is doing good. Click to view more.' }); - await addChildObservation('dummyUser', 'childF', dummyObservationData); + await addChildObservation('dummyUser', 'childF', { + id: 'childAnna', + user: 'dummyUser', + summary: summary, + current: current + }); } +// add data. This must later be done in another way via API calls, or as an intermediate step via localstorage +await createDummyData(); + // <-- export { diff --git a/src/routes/childLand/[childID]/+page.svelte b/src/routes/childLand/[childID]/+page.svelte deleted file mode 100644 index 3cd589ea..00000000 --- a/src/routes/childLand/[childID]/+page.svelte +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - diff --git a/src/routes/childLand/[userID]/[childID]/+page.js b/src/routes/childLand/[userID]/[childID]/+page.js new file mode 100644 index 00000000..46eb72ed --- /dev/null +++ b/src/routes/childLand/[userID]/[childID]/+page.js @@ -0,0 +1,17 @@ +import { fetchChildData, fetchObservationData } from '$lib/stores/childrenStore'; +import { error } from '@sveltejs/kit'; + +/** @type {import('./$types').PageLoad} */ +export async function load({ params }) { + const observationData = await fetchObservationData(params.userID, params.childID); + const childData = await fetchChildData(params.userID, params.childID); + + if (observationData && childData) { + return { + observationData: observationData, + childData: childData + }; + } else { + error(404, 'Not Found'); + } +} diff --git a/src/routes/childLand/[userID]/[childID]/+page.svelte b/src/routes/childLand/[userID]/[childID]/+page.svelte new file mode 100644 index 00000000..6c6c5426 --- /dev/null +++ b/src/routes/childLand/[userID]/[childID]/+page.svelte @@ -0,0 +1,29 @@ + + + + + From 61f5c59f90e1c478d5778abee1ed9163c00b1114 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 12:00:39 +0200 Subject: [PATCH 25/39] correct some mistakes, change some routign --- src/lib/components/Childrenpage.svelte | 2 +- src/lib/components/UserLogin.svelte | 6 ++++ src/lib/stores/childrenStore.ts | 25 ++++++++------- src/lib/stores/contentStore.ts | 2 -- src/lib/stores/userStore.ts | 31 +++++++++++++++++-- .../childLand/[userID]/[childID]/+page.js | 4 ++- .../childLand/[userID]/[childID]/+page.svelte | 2 +- src/routes/firstdropdown/+page.svelte | 7 ++++- src/routes/surveyfeedback/+page.svelte | 7 +---- 9 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index 907c9fa2..8d46017d 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -51,7 +51,6 @@ import CardDisplay from '$lib/components/DataDisplay/CardDisplay.svelte'; import GalleryDisplay from '$lib/components/DataDisplay/GalleryDisplay.svelte'; import { - children, createDummyData, fetchChildrenDataforUser, type ChildData @@ -65,6 +64,7 @@ async function init() { loading = true; + await createDummyData(); let rawdata = await fetchChildrenDataforUser('dummyUser'); data = convertData(rawdata); loading = false; diff --git a/src/lib/components/UserLogin.svelte b/src/lib/components/UserLogin.svelte index 7ed24281..75cf9f22 100644 --- a/src/lib/components/UserLogin.svelte +++ b/src/lib/components/UserLogin.svelte @@ -1,6 +1,8 @@
diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index e34de742..67872236 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -117,7 +117,6 @@ async function fetchChildData(usertoken: string, childtoken: string) { } async function fetchObservationData(usertoken: string, childtoken: string) { - // /API calls/ fetch requests could go here. const contentData = get(children); @@ -171,6 +170,13 @@ function chooseRandom(values: string[]) { } async function createDummyData() { + if ('dummyUser' in childrenlist) { + return; + } + // add user + await addUser('dummyUser'); + + // create data const values = ['good', 'warn', 'bad']; const dates = [ '05-11-2017', @@ -215,14 +221,12 @@ async function createDummyData() { } } - await addUser('dummyUser'); - await addChildData('dummyUser', 'childAnna', { name: 'Anna', id: 'childAnna', user: 'dummyUser', image: 'child_avatar.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'Anna child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childAnna', { id: 'childAnna', @@ -236,7 +240,7 @@ async function createDummyData() { id: 'childBen', user: 'dummyUser', image: 'child_avatar.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'Ben child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childBen', { id: 'childAnna', @@ -250,7 +254,7 @@ async function createDummyData() { id: 'childC', user: 'dummyUser', image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'C child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childC', { id: 'childAnna', @@ -264,7 +268,7 @@ async function createDummyData() { id: 'childDora', user: 'dummyUser', image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'Dora child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childDora', { id: 'childAnna', @@ -278,7 +282,7 @@ async function createDummyData() { id: 'childE', user: 'dummyUser', image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'E child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childE', { id: 'childAnna', @@ -292,7 +296,7 @@ async function createDummyData() { id: 'childF', user: 'dummyUser', image: 'children.png', - info: 'A child that is making a mess and is doing good. Click to view more.' + info: 'F child that is making a mess and is doing good. Click to view more.' }); await addChildObservation('dummyUser', 'childF', { id: 'childAnna', @@ -302,9 +306,6 @@ async function createDummyData() { }); } -// add data. This must later be done in another way via API calls, or as an intermediate step via localstorage -await createDummyData(); - // <-- export { diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 03669aab..6b4d874c 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -67,8 +67,6 @@ async function fetchContent(type: string, key: string) { return contentData[type as keyof ContentList][key]; } -// this hardcodes some dummy content data. - export { addContent, content, diff --git a/src/lib/stores/userStore.ts b/src/lib/stores/userStore.ts index 5d5878ea..45a1a47c 100644 --- a/src/lib/stores/userStore.ts +++ b/src/lib/stores/userStore.ts @@ -1,4 +1,4 @@ -import { writable } from 'svelte/store'; +import { get, writable } from 'svelte/store'; // type interfaces interface PersonalData { @@ -60,4 +60,31 @@ async function removeUser(userToken: string) { }); } -export { addUser, removeUser, users, type UserData, type UserList }; +async function fetchUser(userToken: string) { + if (!(userToken in userlist)) { + throw new Error('User with this token does not exist'); + } else { + const userdata = get(users); + return userdata[userToken]; + } +} + +async function createDummyUser() { + await addUser('dummy', { + role: 'admin', + email: 'dummy@nothing.com', + name: 'dummy', + token: 'whatever', + password: '123', + data: { + yearofbirth: 1990, + gender: 'male', + profession: 'student', + education: 'bachelor', + workinghours: 'fulltime', + householdincome: '35000-40000' + } + }); +} + +export { addUser, createDummyUser, fetchUser, removeUser, users, type UserData, type UserList }; diff --git a/src/routes/childLand/[userID]/[childID]/+page.js b/src/routes/childLand/[userID]/[childID]/+page.js index 46eb72ed..f68237c8 100644 --- a/src/routes/childLand/[userID]/[childID]/+page.js +++ b/src/routes/childLand/[userID]/[childID]/+page.js @@ -1,8 +1,10 @@ -import { fetchChildData, fetchObservationData } from '$lib/stores/childrenStore'; +import { createDummyData, fetchChildData, fetchObservationData } from '$lib/stores/childrenStore'; import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export async function load({ params }) { + await createDummyData(); + const observationData = await fetchObservationData(params.userID, params.childID); const childData = await fetchChildData(params.userID, params.childID); diff --git a/src/routes/childLand/[userID]/[childID]/+page.svelte b/src/routes/childLand/[userID]/[childID]/+page.svelte index 6c6c5426..07a5c2ad 100644 --- a/src/routes/childLand/[userID]/[childID]/+page.svelte +++ b/src/routes/childLand/[userID]/[childID]/+page.svelte @@ -18,7 +18,7 @@ const statusColumns = Object.keys(data_to_display[0]).filter((key) => key !== 'name'); - + - + diff --git a/src/routes/surveyfeedback/+page.svelte b/src/routes/surveyfeedback/+page.svelte index 75b61e9b..964ce32c 100644 --- a/src/routes/surveyfeedback/+page.svelte +++ b/src/routes/surveyfeedback/+page.svelte @@ -20,12 +20,7 @@ 'This is an overview over which milestones for the current survey have been completed'; - + Date: Wed, 28 Aug 2024 14:26:09 +0200 Subject: [PATCH 26/39] remove userstore because usage too unclear, add dynamic routing for surveys --- src/lib/components/Frontpage.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/Frontpage.svelte b/src/lib/components/Frontpage.svelte index 885c0ef1..62fe9155 100644 --- a/src/lib/components/Frontpage.svelte +++ b/src/lib/components/Frontpage.svelte @@ -3,7 +3,7 @@ import CardDisplay from './DataDisplay/CardDisplay.svelte'; import GalleryDisplay from './DataDisplay/GalleryDisplay.svelte'; - export let getStarted = '/firstdropdown'; + export let getStarted = ''; export let items = [ { From 292646fbcaa3a735b799ab3ab15a5554ded5a58f Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 14:26:38 +0200 Subject: [PATCH 27/39] ... actually add dynamic routing and remove userstore... --- src/lib/stores/contentStore.ts | 142 +++++++++++++----- src/lib/stores/userStore.test.ts | 61 -------- src/lib/stores/userStore.ts | 90 ----------- .../childLand/[userID]/[childID]/+page.svelte | 6 +- .../dataAcquisition/[surveyName]/+page.js | 15 ++ .../dataAcquisition/[surveyName]/+page.svelte | 41 +++++ .../firstdropdown/+page.svelte | 2 +- .../nextdropdown/+page.svelte | 2 +- src/routes/surveyfeedback/+page.svelte | 7 +- 9 files changed, 176 insertions(+), 190 deletions(-) delete mode 100644 src/lib/stores/userStore.test.ts delete mode 100644 src/lib/stores/userStore.ts create mode 100644 src/routes/dataAcquisition/[surveyName]/+page.js create mode 100644 src/routes/dataAcquisition/[surveyName]/+page.svelte rename src/routes/{ => dataAcquisition}/firstdropdown/+page.svelte (97%) rename src/routes/{ => dataAcquisition}/nextdropdown/+page.svelte (97%) diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 6b4d874c..4d8faba8 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -1,78 +1,150 @@ import { get, writable } from 'svelte/store'; // types -interface ChildrenSurvey { - [name: string]: unknown; +interface MilestoneDef { + name: string; + items: string[]; + label: string; } -interface RegistrationForm { - [name: string]: unknown; +interface ContentNode { + milestones: MilestoneDef[]; + description: string; + last: string | null; + next: string | null; } interface ContentList { - childrenSurveys: ChildrenSurvey; - registrationForms: RegistrationForm; + [name: string]: ContentNode; } -const contentlist: ContentList = { - childrenSurveys: {}, - registrationForms: {} -}; +const contentlist: ContentList = {}; // store that has an object which stores the content. const content = writable(contentlist); // functions to add and remove stuff from the store -async function addContent( - type: string, - key: string, - new_content: ChildrenSurvey | RegistrationForm -) { +async function addContent(key: string, new_content: ContentNode) { content.update((contentlist: ContentList) => { - if (!(type in contentlist)) { - throw new Error('No such register in the contentstore'); - } - - if (key in contentlist[type as keyof ContentList]) { + if (key in contentlist) { throw new Error('Key already exists in the contentstore'); } - contentlist[type as keyof ContentList][key] = new_content; + contentlist[key] = new_content; return contentlist; }); } -async function removeContent(type: string, key: string) { +async function removeContent(key: string) { content.update((contentlist) => { - if (!(type in contentlist)) { - throw new Error('No such register in the contentstore'); - } - if (!(key in contentlist[type as keyof ContentList])) { + if (!(key in contentlist)) { throw new Error('No such key in the contentstore'); } - delete contentlist[type as keyof ContentList][key]; + delete contentlist[key]; return contentlist; }); } -async function fetchContent(type: string, key: string) { +async function fetchContent(key: string) { const contentData = get(content); - if (!(type in contentData)) { - throw new Error('No such register in the contentstore'); - } - if (!(key in contentData[type as keyof ContentList])) { + if (!(key in contentData)) { throw new Error('No such key in the contentstore'); } - return contentData[type as keyof ContentList][key]; + return contentData[key]; +} + +function getSurveyNumber() { + const contentData = get(content); + return Object.keys(contentData).length; +} + +async function createDummyData() { + const dummySurveys = { + surveyA: { + description: 'This is the first survey called A', + milestones: [ + { + name: 'Standing up', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: + 'How well can the child stand up from sitting or crawling around and how readily is it able to do so' + }, + { + name: 'Gripping a pen the right way', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: 'How well can the child hold a pen or pencil and how coordinated can it use it' + }, + { + name: 'Talking in full sentences', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: + 'How well articulated is the child in its speech and how well can it express itself' + } + ], + last: null, + next: 'surveyB' + }, + surveyB: { + description: 'This is another survey called B', + milestones: [ + { + name: 'Standing up', + label: 'How well can the child hold a pen or pencil and how coordinated can it use it', + items: ['not at all', 'to some extend', 'mostly', 'reliably'] + }, + { + name: 'Gripping a pen the right way', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: 'How well can the child hold a pen or pencil and how coordinated can it use it' + }, + { + name: 'Talking in full sentences', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: + 'How well articulated is the child in its speech and how well can it express itself' + }, + { + name: 'Counting to 10', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: + 'How well can the child count to 10 and how well does it understand numbers within that range' + } + ], + last: 'surveyA', + next: 'surveyC' + }, + surveyC: { + description: 'This is another survey called C', + milestones: [ + { + name: 'Solving a shape-sorting toy', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: 'How well can the child solve a shape-sorting toy' + }, + { + name: 'Counting to 10', + items: ['not at all', 'to some extend', 'mostly', 'reliably'], + label: + 'How well can the child count to 10 and how well does it understand numbers within that range' + } + ], + last: 'surveyB', + next: null + } + }; + + content.set(dummySurveys); } export { addContent, content, + createDummyData, fetchContent, + getSurveyNumber, removeContent, - type ChildrenSurvey, type ContentList, - type RegistrationForm + type ContentNode, + type MilestoneDef }; diff --git a/src/lib/stores/userStore.test.ts b/src/lib/stores/userStore.test.ts deleted file mode 100644 index d135342a..00000000 --- a/src/lib/stores/userStore.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { get } from 'svelte/store'; -import { describe, expect, it } from 'vitest'; -import { addUser, removeUser, type UserData, users } from './userStore'; - -describe('test_normal_function', () => { - const mockUserToken = 'mockUserToken'; - const mockUserData: UserData = { - role: 'mockRole', - email: 'mockEmail', - name: 'mockName', - token: '', - password: 'mockPassword', - data: { - yearofbirth: 1990, - gender: 'male', - profession: 'developer', - education: 'bachelor', - workinghours: 'full-time', - householdincome: 'above-average' - } - }; - - it('should add a new user successfully', async () => { - users.set({}); - - await addUser(mockUserToken, mockUserData); - - expect(get(users)[mockUserToken]).toEqual({ - ...mockUserData, - token: mockUserToken - }); - }); - - it('should not add a user with an existing token', async () => { - users.set({ mockUserToken: mockUserData }); - - try { - await addUser(mockUserToken, mockUserData); - } catch (error: Error | unknown) { - expect((error as Error).message).toBe('User with this token already exists'); - } - }); - - it('should remove a user successfully', async () => { - users.set({ mockUserToken: mockUserData }); - - await removeUser(mockUserToken); - - expect(get(users)[mockUserToken]).toBeUndefined(); - }); - - it('should not remove a user with a non-existing token', async () => { - users.set({ mockUserToken: mockUserData }); - - try { - await removeUser('notdefined'); - } catch (error: Error | unknown) { - expect((error as Error).message).toBe('User with this token does not exist'); - } - }); -}); diff --git a/src/lib/stores/userStore.ts b/src/lib/stores/userStore.ts deleted file mode 100644 index 45a1a47c..00000000 --- a/src/lib/stores/userStore.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { get, writable } from 'svelte/store'; - -// type interfaces -interface PersonalData { - yearofbirth: number; - gender: string; - profession: string; - education: string; - workinghours: string; - householdincome: string; -} - -interface ScientistData extends PersonalData { - project: string; -} - -interface AdminData extends PersonalData { - admintoken: string; -} - -interface UserData { - role: string; - email: string; - name: string; - token: string; - password: string; - data: PersonalData | ScientistData | AdminData; -} - -interface UserList { - [key: string]: UserData; -} - -// Initial user data -const userlist: UserList = {}; - -const users = writable(userlist); - -// functions to add and delete users -async function addUser(userToken: string, userData: UserData) { - users.update((userlist) => { - if (userToken in userlist) { - throw new Error('User with this token already exists'); - } else { - userData['token'] = userToken; - userlist[userToken] = userData; - } - return userlist; - }); -} - -async function removeUser(userToken: string) { - users.update((userlist) => { - if (userToken in userlist) { - delete userlist[userToken]; - } else { - throw new Error('User with this token does not exist'); - } - return userlist; - }); -} - -async function fetchUser(userToken: string) { - if (!(userToken in userlist)) { - throw new Error('User with this token does not exist'); - } else { - const userdata = get(users); - return userdata[userToken]; - } -} - -async function createDummyUser() { - await addUser('dummy', { - role: 'admin', - email: 'dummy@nothing.com', - name: 'dummy', - token: 'whatever', - password: '123', - data: { - yearofbirth: 1990, - gender: 'male', - profession: 'student', - education: 'bachelor', - workinghours: 'fulltime', - householdincome: '35000-40000' - } - }); -} - -export { addUser, createDummyUser, fetchUser, removeUser, users, type UserData, type UserList }; diff --git a/src/routes/childLand/[userID]/[childID]/+page.svelte b/src/routes/childLand/[userID]/[childID]/+page.svelte index 07a5c2ad..56d4a108 100644 --- a/src/routes/childLand/[userID]/[childID]/+page.svelte +++ b/src/routes/childLand/[userID]/[childID]/+page.svelte @@ -18,7 +18,11 @@ const statusColumns = Object.keys(data_to_display[0]).filter((key) => key !== 'name'); - + + import AbstractContent from '$lib/components/AbstractContent.svelte'; + import AbstractDataInput from '$lib/components/DataInput/AbstractDataInput.svelte'; + import AbstractDropdownItem from '$lib/components/DataInput/AbstractDropdownItem.svelte'; + + import { type MilestoneDef } from '$lib/stores/contentStore'; + /** + * Converts the milestone data and adds components to display each element. + * @param milestoneData list of milestone data recovered from the backend + */ + function convertData(milestoneData: MilestoneDef[]) { + return milestoneData.map((milestone) => { + return { + itemComponent: AbstractDropdownItem, + componentProps: milestone + }; + }); + } + + // use reactive statements to rerender the page each time the data changes. + // this should make sure that each time the page is rerouted to another instance of itself, + // the data is updated correctly and the new content is shown. + let x = null; + $: console.log($$props.data.data.next, $$props.data.data.last); + $: heading = $$props.data.surveyName; + $: description = $$props.data.data.description; + $: data_to_display = convertData($$props.data.data.milestones); + $: next = + $$props.data.data.next !== null + ? `/dataAcquisition/${$$props.data.data.next}` + : '/surveyfeedback'; + + $: last = + $$props.data.data.last !== null + ? `/dataAcquisition/${$$props.data.data.last}` + : '/childrengallery'; + + + + + diff --git a/src/routes/firstdropdown/+page.svelte b/src/routes/dataAcquisition/firstdropdown/+page.svelte similarity index 97% rename from src/routes/firstdropdown/+page.svelte rename to src/routes/dataAcquisition/firstdropdown/+page.svelte index 46fd5c58..35c58506 100644 --- a/src/routes/firstdropdown/+page.svelte +++ b/src/routes/dataAcquisition/firstdropdown/+page.svelte @@ -46,7 +46,7 @@ diff --git a/src/routes/nextdropdown/+page.svelte b/src/routes/dataAcquisition/nextdropdown/+page.svelte similarity index 97% rename from src/routes/nextdropdown/+page.svelte rename to src/routes/dataAcquisition/nextdropdown/+page.svelte index 75463669..3f198e0b 100644 --- a/src/routes/nextdropdown/+page.svelte +++ b/src/routes/dataAcquisition/nextdropdown/+page.svelte @@ -55,7 +55,7 @@ diff --git a/src/routes/surveyfeedback/+page.svelte b/src/routes/surveyfeedback/+page.svelte index 964ce32c..0c21678e 100644 --- a/src/routes/surveyfeedback/+page.svelte +++ b/src/routes/surveyfeedback/+page.svelte @@ -20,7 +20,12 @@ 'This is an overview over which milestones for the current survey have been completed'; - + Date: Wed, 28 Aug 2024 14:28:18 +0200 Subject: [PATCH 28/39] undo changes to the user login page --- src/lib/components/UserLogin.svelte | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/components/UserLogin.svelte b/src/lib/components/UserLogin.svelte index 75cf9f22..7ed24281 100644 --- a/src/lib/components/UserLogin.svelte +++ b/src/lib/components/UserLogin.svelte @@ -1,8 +1,6 @@
From 449737fdea4b4f92671aa5ff3bc5f7643162ea42 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 14:38:44 +0200 Subject: [PATCH 29/39] add docstrings --- src/lib/stores/childrenStore.ts | 61 +++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/lib/stores/childrenStore.ts b/src/lib/stores/childrenStore.ts index 67872236..8e6b2176 100644 --- a/src/lib/stores/childrenStore.ts +++ b/src/lib/stores/childrenStore.ts @@ -2,7 +2,7 @@ import { get, writable } from 'svelte/store'; // README: this API is experimental and not by any means a final design -// Types +// Types: create interfaces for the elements that are stored and are expected to be returned from the 'backend' (or its mockup at the moment) interface ObservationData { id: string; user: string; @@ -28,11 +28,15 @@ interface ChildrenList { }; } -// store itself: README: +// the store itself: README: TODO: Consider creating a derived store for maps that exposes some key-value retrieval functionality const childrenlist: ChildrenList = {}; const children = writable(childrenlist); +/** + * Add a new user with an empty children store + * @param usertoken the token of the user to add + */ async function addUser(usertoken: string) { children.update((childrenlist) => { if (usertoken in childrenlist) { @@ -45,6 +49,12 @@ async function addUser(usertoken: string) { }); } +/** + * Add new child data to the data of a user + * @param usertoken User identifier to add the child to + * @param childtoken Child identifier to add the child to + * @param data data of the child to add + */ async function addChildData(usertoken: string, childtoken: string, data: ChildData) { // /API calls/ fetch requests could go here. @@ -63,6 +73,12 @@ async function addChildData(usertoken: string, childtoken: string, data: ChildDa }); } +/** + * Add observation data to the data of a child + * @param usertoken User identifier to add the child to + * @param childtoken Child identifier to add observation data to + * @param observationData The observationdata to add + */ async function addChildObservation( usertoken: string, childtoken: string, @@ -83,6 +99,11 @@ async function addChildObservation( }); } +/** + * Remove a child from the data of a user + * @param usertoken user identifer to remove the child from + * @param childtoken childidentifier to remove + */ async function removeChildData(usertoken: string, childtoken: string) { // /API calls/ fetch requests could go here. @@ -101,6 +122,12 @@ async function removeChildData(usertoken: string, childtoken: string) { }); } +/** + * Retrieve the data of a child + * @param usertoken user to fetch child data from + * @param childtoken child identifier to fetch data from + * @returns ChildData of the child + */ async function fetchChildData(usertoken: string, childtoken: string) { // /API calls/ fetch requests could go here. const contentData = get(children); @@ -116,6 +143,12 @@ async function fetchChildData(usertoken: string, childtoken: string) { return contentData[usertoken as keyof ChildrenList][childtoken].childData; } +/** + * Retrieve the observation data of a child + * @param usertoken user to fetch child observations from + * @param childtoken child identifier to fetch observations from + * @returns ObservationData of the child + */ async function fetchObservationData(usertoken: string, childtoken: string) { // /API calls/ fetch requests could go here. const contentData = get(children); @@ -131,6 +164,11 @@ async function fetchObservationData(usertoken: string, childtoken: string) { return contentData[usertoken as keyof ChildrenList][childtoken].observationData; } +/** + * fetch all the children data of a user + * @param usertoken User to fetch data for + * @returns a list of ChildData objects for the user, sorted alphabetically by child name. + */ async function fetchChildrenDataforUser(usertoken: string) { const contentData = get(children); @@ -145,6 +183,12 @@ async function fetchChildrenDataforUser(usertoken: string) { }) .sort((a, b) => a.name.localeCompare(b.name)); } + +/** + * fetch all observationdata for all children of a user + * @param usertoken user to fetch data for + * @returns A list of tuples, where the first element is the child identifier and the second element is the observation data of the child + */ async function fetchObservationDataForUser(usertoken: string) { const contentData = get(children); if (!(usertoken in contentData)) { @@ -161,14 +205,27 @@ async function fetchObservationDataForUser(usertoken: string) { // the stuff below is therefore temporary and is an approximation of the datastructure that may be returned perhaps from the backend, // and thus is subject to change +/** + * helper function for generating random integers + * @param max the maximum value of the random integer (always starts from 0) + * @returns + */ function getRandomInt(max: number) { return Math.floor(Math.random() * max); } +/** + * helper function for choosing a random value from a list + * @param values list of values to choose from + * @returns + */ function chooseRandom(values: string[]) { return values[getRandomInt(values.length)]; } +/** + * create dummy data for the children store. This will later be replaced by API calls to the server + */ async function createDummyData() { if ('dummyUser' in childrenlist) { return; From 751cefa98c18678abc48b3a2dfcb150853c5c341 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 14:48:11 +0200 Subject: [PATCH 30/39] add docstrings --- src/lib/stores/contentStore.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib/stores/contentStore.ts b/src/lib/stores/contentStore.ts index 4d8faba8..dfa61fd8 100644 --- a/src/lib/stores/contentStore.ts +++ b/src/lib/stores/contentStore.ts @@ -1,6 +1,6 @@ import { get, writable } from 'svelte/store'; -// types +// types. Create some interfaces to define the structure of the content and make clear what will be expected from API calls interface MilestoneDef { name: string; items: string[]; @@ -20,10 +20,17 @@ interface ContentList { const contentlist: ContentList = {}; +// README: perhaps put this into a derived store that is a map of keys to content nodes. This way we can have a single // store that has an object which stores the content. const content = writable(contentlist); // functions to add and remove stuff from the store + +/** + * Add content to the store + * @param key identifier for the content to add + * @param new_content content to add to the store + */ async function addContent(key: string, new_content: ContentNode) { content.update((contentlist: ContentList) => { if (key in contentlist) { @@ -34,6 +41,10 @@ async function addContent(key: string, new_content: ContentNode) { }); } +/** + * Remove content from the store corresponding to the key given. + * @param key identifier for the content to remove + */ async function removeContent(key: string) { content.update((contentlist) => { if (!(key in contentlist)) { @@ -44,6 +55,11 @@ async function removeContent(key: string) { }); } +/** + * Retrieve content from the store + * @param key identifier for the content to fetch + * @returns content element corresponding to the key + */ async function fetchContent(key: string) { const contentData = get(content); @@ -54,11 +70,17 @@ async function fetchContent(key: string) { return contentData[key]; } +/** + * Get the number of surveys in the store + */ function getSurveyNumber() { const contentData = get(content); return Object.keys(contentData).length; } +/** + * Create some dummy data to test the store + */ async function createDummyData() { const dummySurveys = { surveyA: { From 7934701f2e95241dc3c654744af6c00d2943d288 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 14:51:39 +0200 Subject: [PATCH 31/39] remove dummy dropdowns --- .../firstdropdown/+page.svelte | 53 ---------------- .../dataAcquisition/nextdropdown/+page.svelte | 63 ------------------- src/routes/surveyfeedback/+page.svelte | 2 +- 3 files changed, 1 insertion(+), 117 deletions(-) delete mode 100644 src/routes/dataAcquisition/firstdropdown/+page.svelte delete mode 100644 src/routes/dataAcquisition/nextdropdown/+page.svelte diff --git a/src/routes/dataAcquisition/firstdropdown/+page.svelte b/src/routes/dataAcquisition/firstdropdown/+page.svelte deleted file mode 100644 index 35c58506..00000000 --- a/src/routes/dataAcquisition/firstdropdown/+page.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - - - - diff --git a/src/routes/dataAcquisition/nextdropdown/+page.svelte b/src/routes/dataAcquisition/nextdropdown/+page.svelte deleted file mode 100644 index 3f198e0b..00000000 --- a/src/routes/dataAcquisition/nextdropdown/+page.svelte +++ /dev/null @@ -1,63 +0,0 @@ - - - - - diff --git a/src/routes/surveyfeedback/+page.svelte b/src/routes/surveyfeedback/+page.svelte index 0c21678e..56bf71c2 100644 --- a/src/routes/surveyfeedback/+page.svelte +++ b/src/routes/surveyfeedback/+page.svelte @@ -22,7 +22,7 @@ From 93480cca0ba7de76de99b81078560f7fe01dff5c Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 14:58:29 +0200 Subject: [PATCH 32/39] fix contentstore tests --- src/lib/stores/contentStore.test.ts | 79 ++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/lib/stores/contentStore.test.ts b/src/lib/stores/contentStore.test.ts index 02356bc2..3ef6f17a 100644 --- a/src/lib/stores/contentStore.test.ts +++ b/src/lib/stores/contentStore.test.ts @@ -4,55 +4,79 @@ import { addContent, content, removeContent, - type ChildrenSurvey, type ContentList, - type RegistrationForm + type MilestoneDef } from './contentStore'; describe('test_normal_functionality', async () => { - const mockChildSurvey: ChildrenSurvey = { - can_do_this: 'incomplete', - can_do_that: 'fully', - can_do_the_other: 'no' - }; - - const mockRegistrationForm: RegistrationForm = { - name: 'Tom', - gender: 'male' + const mockChildSurvey: MilestoneDef = { + name: 'dummy', + label: 'dummy description', + items: ['dummy1', 'dummy2', 'dummy3', 'dummy4'] }; const mockContentList: ContentList = { - childrenSurveys: { - testsurvey: mockChildSurvey + A: { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: '/', + next: 'B' + }, + B: { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: 'A', + next: 'C' }, - registrationForms: { - testregistration: mockRegistrationForm + C: { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: 'B', + next: '/' } }; it('should add content successfully when key is not yet there', async () => { content.set(mockContentList); - await addContent('childrenSurveys', 'dummySurvey', mockChildSurvey); - await addContent('registrationForms', 'childRegistration', mockRegistrationForm); + await addContent('this', { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: '/', + next: 'B' + }); + await addContent('that', { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: 'B', + next: 'C' + }); - expect(get(content)['childrenSurveys']['dummySurvey']).toEqual(mockChildSurvey); - expect(get(content)['registrationForms']['childRegistration']).toEqual(mockRegistrationForm); + expect(get(content)['this']).toEqual({ + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: '/', + next: 'B' + }); + expect(get(content)['that']).toEqual({ + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: 'B', + next: 'C' + }); }); it('should remove content from the contentstore successfully', async () => { content.set(mockContentList); - await removeContent('childrenSurveys', 'testsurvey'); - await removeContent('registrationForms', 'testregistration'); + await removeContent('A'); - expect(get(content)['childrenSurveys']['testsurvey']).toBeUndefined(); - expect(get(content)['registrationForms']['testregistration']).toBeUndefined(); + expect(get(content)['A']).toBeUndefined(); }); it('throw error when removing an element with an existing key', async () => { content.set(mockContentList); try { - await removeContent('childrenSurveys', 'notthere'); + await removeContent('notthere'); } catch (error: Error | unknown) { expect((error as Error).message).toBe('No such key in the contentstore'); } @@ -62,7 +86,12 @@ describe('test_normal_functionality', async () => { content.set(mockContentList); try { - await addContent('childrenSurveys', 'testsurvey', mockChildSurvey); + await addContent('A', { + milestones: [mockChildSurvey, mockChildSurvey, mockChildSurvey], + description: 'nothing to be seen here', + last: '/', + next: 'B' + }); } catch (error: Error | unknown) { expect((error as Error).message).toBe('Key already exists in the contentstore'); } From faff33fc71f64750cccbaa175a8053028cf94174 Mon Sep 17 00:00:00 2001 From: Harald Mack Date: Wed, 28 Aug 2024 15:23:27 +0200 Subject: [PATCH 33/39] fix tests --- src/lib/components/Childrenpage.svelte | 1 + src/lib/stores/childrenStore.test.ts | 98 ++++++++++++++------------ src/lib/stores/childrenStore.ts | 17 ++++- src/lib/stores/contentStore.test.ts | 60 +++++++--------- 4 files changed, 96 insertions(+), 80 deletions(-) diff --git a/src/lib/components/Childrenpage.svelte b/src/lib/components/Childrenpage.svelte index 8d46017d..5d966d55 100644 --- a/src/lib/components/Childrenpage.svelte +++ b/src/lib/components/Childrenpage.svelte @@ -20,6 +20,7 @@ // dynamically create the styles for individual gallery tiles based on the data. // The 'Neu' element needs to be styled differently in particular + // FIXME: this needs to go. styles have no business being defined in