-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdr-lib.js
More file actions
230 lines (201 loc) · 7.38 KB
/
dr-lib.js
File metadata and controls
230 lines (201 loc) · 7.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import { dataDefs } from "./common-data-defs.js";
import { serviceInfoUrl, stateSaveApp } from "./common-lib.js"
// NEW
/** The "base" stream for this App */
const APP_MANAGING_STREAMID = 'app-dr-hds';
/** The name of this application */
const APP_MANAGING_NAME = 'HDS Dr App PoC';
export const drLib = {
showLoginButton,
getPatientsData,
getPatientDetails
};
function showLoginButton (loginSpanId, stateChangeCallBack) {
const authSettings = {
spanButtonID: loginSpanId, // div id the DOM that will be replaced by the Service specific button
onStateChange: pryvAuthStateChange, // event Listener for Authentication steps
authRequest: {
// See: https://api.pryv.com/reference/#auth-request
requestingAppId: APP_MANAGING_STREAMID, // to customize for your own app
requestedPermissions: [
{
streamId: APP_MANAGING_STREAMID,
defaultName: APP_MANAGING_NAME,
level: "manage",
},
],
clientData: {
'app-web-auth:ensureBaseStreams': [ // this is handled by custom app web Auth3 (might be migrated in permission request)
{ id: 'applications', name: 'Applications' },
{ id: APP_MANAGING_STREAMID, name: APP_MANAGING_NAME, parentId: 'applications' }
],
"app-web-auth:description": {
type: "note/txt",
content:
"This app allows to send invitation links to patients and visualize and export answers.",
},
},
},
};
HDSLib.pryv.Browser.setupAuth(authSettings, serviceInfoUrl);
async function pryvAuthStateChange(state) {
// called each time the authentication state changes
console.log("##pryvAuthStateChange", state);
if (state.id === HDSLib.pryv.Browser.AuthStates.AUTHORIZED) {
await HDSLib.initHDSModel(); // hds model needs to be initialized
const appManaging = await HDSLib.appTemplates.AppManagingAccount.newFromApiEndpoint(APP_MANAGING_STREAMID, state.apiEndpoint, APP_MANAGING_NAME);
stateSaveApp('managing', appManaging);
await initDemoAccount(appManaging);
stateChangeCallBack("loggedIN");
}
if (state.id === HDSLib.pryv.Browser.AuthStates.INITIALIZED) {
stateSaveApp('managing', null);
stateChangeCallBack("loggedOUT");
}
}
}
/**
* Right after beeing logged in.
* Check if the account has the two forms
* This step will be implemented in the dr's App when the "create form" will be developped
* */
async function initDemoAccount (appManaging) {
// name should be better than "username" - to be changed
const drConnectionInfo = await appManaging.connection.accessInfo();
const drUserName = drConnectionInfo.user.username;
// -- check current collectors (forms)
const collectors = await appManaging.getCollectors();
for (const [questionaryId, questionary] of Object.entries(dataDefs.v2questionnaires)) {
// check if collector exists
const found = collectors.find(c => c.name === questionary.title);
if (found) {
console.log('##2 initDemoAccount found', found);
continue; // stop here if exists
}
console.log('##2 initDemoAccount creating collector for', questionary);
const newCollector = await appManaging.createCollector(questionary.title);
const request = newCollector.request;
request.appId = 'dr-form';
request.appUrl = 'https://xxx.yyy';
request.title = { en: questionary.title };
request.requesterName = 'Username ' + drUserName;
request.description = { en: 'Short Description to be updated: ' + questionary.title };
request.consent = { en: 'This is a consent message to be set' };
if (questionary.features?.chat) {
request.addChatFeature();
}
// add static permissions
for (const extraPermission of questionary.permissionsPreRequest) {
request.addPermissionExtra(extraPermission);
}
// create the sections contents
for (const [key, form] of Object.entries(questionary.forms)) {
const section = request.createSection(key, form.type);
section.setNameLocal('en', form.name);
section.addItemKeys(form.itemKeys);
}
// build permissions needed
request.buildPermissions();
// checkContent
console.log('$$$$$$$ ', request.content);
await newCollector.save(); // save the data (done when the form is edited)
await newCollector.publish(); console.log('##2 initDemoAccount published', newCollector);
}
console.log('##2 initDemoAccount with ', collectors);
}
// -------- Fetch patient list --------
async function getPatientsData (collector) {
// static headers
const headers = {
status: 'Status',
inviteName: 'Invite',
username: 'Username',
createdAt: 'Date'
}
// headers from first form
const firstSection = collector.request.sections[0];
if (!firstSection) return null;
const itemDefs = [];
for (const itemKey of firstSection.itemKeys) {
const itemDef = HDSLib.getHDSModel().itemsDefs.forKey(itemKey);
itemDefs.push(itemDef);
headers[itemDef.key] = itemDef.label;
}
// add lines (1 per patient)
const invites = await collector.getInvites();
const activeInvites = invites.filter(i => i.status === 'active');
// fetch patient data
const patientPromises = activeInvites.map((invite) =>
drLib.getPatientDetails(invite, itemDefs)
);
const patientsData = await Promise.all(patientPromises);
patientsData.sort((a, b) => b.dateCreation - a.dateCreation); // sort by creation date reverse
console.log('## patientsResults', patientsData);
return { headers, patientsData }
}
/**
* get patients details
*/
async function getPatientDetails(invite, itemDefs) {
const patient = {
invite,
status: invite.status,
username: null,
inviteName: invite.displayName,
createdAt: invite.dateCreation.toLocaleString(),
dateCreation: invite.dateCreation // keep it as a date for sorting
};
console.log('## getPatientDetails.invite', invite, invite.status, invite.eventData.streamIds);
// --
const patientInfo = await invite.checkAndGetAccessInfo();
if (patientInfo === null) return patient;
patient.username = patientInfo.user.username;
// -- get data
// get the last value of each itemKey
const apiCalls = itemDefs.map((itemDef) => {
return {
method: "events.get",
params: {
streams: [itemDef.data.streamId],
types: itemDef.eventTypes,
limit: 1,
}
};
});
const profileEventsResults = await invite.connection.api(apiCalls);
for (const profileEventRes of profileEventsResults) {
const profileEvent = profileEventRes?.events?.[0];
if (!profileEvent) continue;
const field = dataFieldFromEvent(profileEvent);
patient[field.key] = ( field.value != null) ? field.value : '' ;
}
return patient;
}
/**
* Link an event to a data field from form
* @param {*} event
*/
function dataFieldFromEvent(event) {
const itemDef = HDSLib.getHDSModel().itemsDefs.forEvent(event, false);
if (!itemDef) {
console.error("## itemDef not found for event", event);
return null;
}
const field = {
key: itemDef.key,
label: itemDef.label,
type: itemDef.data.type,
value: event.content,
event: event,
};
if (field.type === "date") {
const date = new Date(event.content);
if (!isNaN(date)) {
field.value = date.toISOString().split("T")[0]; // format YYYY-MM-DD
} else {
console.error("## Error parsing date", event.content);
field.value = "";
}
}
return field;
}