UNPKG

7.21 kBJavaScriptView Raw
1'use strict'
2
3const _ = require('lodash')
4const request = require('request')
5// require('request-debug')(request)
6const attributeSelection = require('./attributeSelection')()
7const isJSON = require('is-json')
8
9const config = require('./rc')().get()
10const auth = config.medx.auth
11const c3 = config.medx.c3.replace(/c3$/i, 'ada')
12const OAUTH_BASICAUTH = 'Basic YWRhOjl4Q0dqZFJVcEJaVlVHdzJjVUROcTd2S3JCckFNQUU0'
13
14const no = ['no', 'none', 'nope', 'nein']
15const abortSearch = (term) => _.includes(no, term.trim().toLowerCase())
16
17let currentProfile
18
19const convertToDate = (date) => {
20 const from = date.trim().split('.')
21 return new Date(from[2], from[1] - 1, from[0], 12).toISOString()
22}
23
24const invalidResponse = `The server's response is invalid.
25There is probably a deployment ongoing.
26Please check back in a few minutes.`
27
28module.exports = (username, password) => {
29 let api = null
30 const initialFormData = JSON.stringify({answer: {state: 'GET_HEALTH_ADVICE'}})
31 const getResult = (adaCaseKey, cb) => {
32 api.get({url: `result/${adaCaseKey}`}, (err, res, body) => {
33 if (err) cb(err)
34 cb(null, JSON.parse(body))
35 })
36 }
37
38 const normalizeResponse = (response) => {
39 const firstType = _.get(response, 'answerOptions[0].type')
40 const secondType = _.get(response, 'answerOptions[1].type')
41
42 response.answerType = firstType === 'SELECT' ? 'SELECT' : 'TEXT'
43 response.search = firstType === 'SEARCH'
44
45 if (firstType === 'SEARCH' && secondType) {
46 response.question += ' Type "no" or enter a symptom.'
47 }
48 if (firstType === 'DATE') {
49 response.question += ' e.g.: 19.07.1994'
50 }
51 if (firstType === 'ATTRIBUTE_SELECTION_MAP') {
52 response.answerType = 'SELECT'
53 response.answerOptions = attributeSelection.map((a) => {
54 return _.defaults(a, response.answerOptions[0])
55 })
56 }
57 return response
58 }
59
60 const symptomSearch = (answer, previousMessage, isCli, cb) => {
61 const keys = previousMessage.answerOptions[0]
62 const search = (sex, birthday) => {
63 api.get({
64 url: `/dialog_search?limit=20&offset=0&query=${encodeURIComponent(answer)}&` +
65 `selfAssessmentKey=${keys.selfAssessmentKey}&sex=${sex}&birthday=${birthday}`
66 },
67 (err, res, body) => {
68 let results = JSON.parse(body).results
69 results.forEach((result) => {
70 if (!result.name) return
71 if (isCli) result.patientName += ` // medical: ${result.name}`
72 if (!result.matchedName) return
73 if (isCli) result.patientName += ` // matched: ${result.matchedName}`
74 })
75 const answerOptions = results.concat({
76 key: 'backToSearch',
77 name: '< back to search',
78 patientName: '< back to search'
79 })
80
81 let newMessage = {
82 question: 'Select a result:',
83 answerType: 'SELECT',
84 stateStack: previousMessage.stateStack,
85 answerOptions: answerOptions,
86 previousAnswerOptions: previousMessage.answerOptions,
87 previousMessage
88 }
89 if (!results.length) console.log('No results were found.')
90 cb(err, !err && results.length ? newMessage : previousMessage)
91 })
92 }
93 if (!currentProfile || currentProfile.key !== keys.profileKey) {
94 api.get({url: '/profiles'}, (err, res, rawProfiles) => {
95 if (err) cb(err)
96 const profiles = JSON.parse(rawProfiles)
97 currentProfile = _.find(profiles, {key: keys.profileKey})
98 search(currentProfile.sex, currentProfile.birthday)
99 })
100 } else {
101 search(currentProfile.sex, currentProfile.birthday)
102 }
103 }
104
105 const initiateChat = (cb) => {
106 api.post({url: '/dialog', body: initialFormData}, (err, res, body) => {
107 if (!isJSON(body)) return cb(invalidResponse)
108 cb(err, !err && normalizeResponse(JSON.parse(body)))
109 })
110 }
111
112 const deleteProfiles = (cb) => {
113 api.del({url: '/profiles'}, (err, res, body) => {
114 if (err) return cb(new Error(err))
115 if (res.statusCode !== 204) return cb(new Error('Some error occured.'))
116 cb(null, 'Successfully deleted all Profiles. Please add a new main profile via Start Self Assessment > Someone else')
117 })
118 }
119
120 return {
121 login: (deleteAllProfiles, cb) => {
122 const url = `${auth.replace(/api\/login$/i, '')}oauth/token?username=${encodeURIComponent(
123 username)}&password=${encodeURIComponent(password)}&grant_type=password`
124 request.post({url, headers: {
125 Authorization: OAUTH_BASICAUTH
126 }}, (err, res) => {
127 if (err) return cb(new Error(err))
128 const token = JSON.parse(res.body).access_token
129 if (!token) return cb(new Error('login failed'))
130
131 api = request.defaults({
132 baseUrl: c3,
133 headers: {
134 Authorization: `Bearer ${token}`,
135 'Content-Type': 'application/json;charset=UTF-8'
136 }
137 })
138 if (deleteAllProfiles) return deleteProfiles(cb)
139 initiateChat(cb)
140 })
141 },
142
143 isLoggedIn: () => !!api,
144
145 ask: (answer, previousMessage, isCli, cb) => {
146 if (!api) cb(new Error('wrong function'))
147 if (!previousMessage) return initiateChat(cb)
148
149 if (answer.key === 'backToSearch') {
150 return cb(null, previousMessage.previousMessage)
151 }
152
153 if (previousMessage.search && !abortSearch(answer)) {
154 return symptomSearch(answer, previousMessage, isCli, cb)
155 }
156
157 let dialogFormData = {
158 answer,
159 stateStack: previousMessage.stateStack
160 }
161
162 if (previousMessage.answerType === 'TEXT') {
163 const type = previousMessage.answerOptions[0].type
164 const abortSearchAnswer = previousMessage.answerOptions[1] && previousMessage.answerOptions[1]
165
166 answer = answer.trim()
167 dialogFormData.answer = abortSearch(answer) ? abortSearchAnswer
168 : {key: answer, input: type === 'DATE' ? convertToDate(answer) : answer}
169
170 if (!abortSearchAnswer) {
171 if (!dialogFormData.answer) return cb(null, previousMessage)
172 dialogFormData.answer.state = previousMessage.answerOptions[0].state
173 }
174
175 dialogFormData.answer = _.defaults(dialogFormData.answer, previousMessage.answerOptions[0])
176 }
177
178 if (answer.score) {
179 dialogFormData.answer.state = previousMessage.previousAnswerOptions[0].state
180 dialogFormData.answer.input = answer.key
181 Object.assign(dialogFormData.answer, previousMessage.previousAnswerOptions[0])
182 }
183
184 dialogFormData = JSON.stringify(dialogFormData)
185
186 api.post({url: '/dialog', body: dialogFormData}, (err, res, body) => {
187 if (!isJSON(body)) return cb(invalidResponse)
188 body = JSON.parse(body)
189 if (res.statusCode >= '300') return cb(new Error(res))
190 if (!body.answerOptions) cb(new Error('No answer options were defined ', body))
191 if (_.includes(['NAVIGATE', 'NAVIGATE_INSTANT'], body.answerOptions[0].type)) {
192 if (body.answerOptions[0].route === 'assessmentReport') {
193 return getResult(body.answerOptions[0].adaCaseKey, cb)
194 }
195 body.isOver = true
196 return cb(null, body)
197 }
198 cb(err, !err && normalizeResponse(body))
199 })
200 }
201 }
202}