-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathQuestionnaire+C3-PRO.swift
159 lines (138 loc) · 5.13 KB
/
Questionnaire+C3-PRO.swift
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
//
// QuestionnaireExtensions.swift
// C3PRO
//
// Created by Pascal Pfiffner on 5/20/15.
// Copyright © 2015 Boston Children's Hospital. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SMART
import ResearchKit
/** Extending `SMART.QuestionnaireItem` for use with ResearchKit. */
extension QuestionnaireItem {
/**
Checks whether the item has "enableWhen" conditions, and if there are any instantiates ResultRequirements representing those.
*/
func c3_enableQuestionnaireElementWhen() throws -> [ResultRequirement]? {
if let enableWhen = enableWhen {
var requirements = [ResultRequirement]()
for when in enableWhen {
let questionIdentifier = try when.c3_questionIdentifier()
let result = try when.c3_answerResult(questionIdentifier)
let req = ResultRequirement(step: questionIdentifier, result: result)
requirements.append(req)
}
return requirements
}
return nil
}
}
/** Extending `SMART.QuestionnaireItemEnableWhen` for use with ResearchKit. */
extension QuestionnaireItemEnableWhen {
/**
Returns the question step identifier, throws if there is none.
- returns: A String representing the step identifier the receiver applies to
*/
func c3_questionIdentifier() throws -> String {
guard let questionIdentifier = question?.string else {
throw C3Error.questionnaireEnableWhenIncomplete("\(self) has no `question` to refer to")
}
return questionIdentifier
}
/**
Returns the result that is required for the parent element to be shown.
Throws if the receiver cannot be converted to a result, you might want to be graceful catching these errors. Currently supports:
- answerBoolean
- answerCoding
- parameter questionIdentifier: The identifier of the question step this extension applies to
- returns: An `ORKQuestionResult` representing the result that is required for the item to be shown
*/
func c3_answerResult(_ questionIdentifier: String) throws -> ORKQuestionResult {
let questionIdentifier = try c3_questionIdentifier()
if let answer = answerBoolean?.bool {
let result = ORKBooleanQuestionResult(identifier: questionIdentifier)
result.answer = answer
return result
}
if let answer = answerString?.string {
let result = ORKTextQuestionResult(identifier: questionIdentifier)
result.answer = answer
return result
}
if let answer = answerCoding {
if let code = answer.code {
let result = ORKChoiceQuestionResult(identifier: questionIdentifier)
let system = answer.system?.absoluteString ?? kORKTextChoiceDefaultSystem
let value = "\(system)\(kORKTextChoiceSystemSeparator)\(code)"
result.answer = [value]
return result
}
throw C3Error.questionnaireEnableWhenIncomplete("\(self) has `answerCoding` but is missing a code, cannot create a question result")
}
throw C3Error.questionnaireEnableWhenIncomplete("\(self) has no `answerType` type that is supported right now")
}
}
extension QuestionnaireResponse {
/**
Inspects all the instance's `item` items and removes those with duplicate `linkId`, consolidating its items into the first item with the
same linkId. Calls out to `QuestionnaireResponseItem.itemsDeduplicatedByLinkId(items:)`.
*/
public func deduplicateItemsByLinkId() {
guard let items = item, items.count > 0 else {
return
}
item = QuestionnaireResponseItem.itemsDeduplicatedByLinkId(items)
}
}
extension QuestionnaireResponseItem {
/**
Inspects all the instance's `item` items and removes those with duplicate `linkId`, consolidating its items into the first item with the
same linkId. Calls out to `QuestionnaireResponseItem.itemsDeduplicatedByLinkId(items:)`.
*/
public func deduplicateItemsByLinkId() {
guard let items = item, items.count > 0 else {
return
}
item = type(of: self).itemsDeduplicatedByLinkId(items)
}
/**
Consolidates all items with the same `linkId` into one (the first) item. Recurses into subitems.
*/
public class func itemsDeduplicatedByLinkId(_ items: [QuestionnaireResponseItem]) -> [QuestionnaireResponseItem] {
guard items.count > 0 else {
return items
}
var itemDict = [String: QuestionnaireResponseItem]()
var finalItems = [QuestionnaireResponseItem]()
for item in items {
guard let thisLinkId = item.linkId?.string else { // is invalid without linkId anyway
continue
}
if let existing = itemDict[thisLinkId] {
if nil == existing.item {
existing.item = item.item
}
else if let theseItems = item.item {
existing.item!.append(contentsOf: theseItems)
}
}
else {
itemDict[thisLinkId] = item
finalItems.append(item)
}
}
finalItems.forEach() { $0.deduplicateItemsByLinkId() }
return finalItems
}
}