/
survey.hpp
290 lines (243 loc) · 10.2 KB
/
survey.hpp
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
//#pragma once
#include <eosio/eosio.hpp>
#include <eosio/system.hpp>
#include "freeosgov.hpp"
#include "tables.hpp"
using namespace eosio;
using namespace freedao;
using namespace std;
/** @defgroup survey Survey
* These Actions and functions are related to processing user surveys.
* @{
*/
/**
* Utility function to split a string into a vector of strings, using a delimiter.
* This function is used by parse_survey_ranges() to interpret the surveyranges parameter.
*
* @param s The string to be split
* @param delimiter The delimiter to use to split the string.
*
* @return A vector of strings.
*/
vector<string> split (string s, string delimiter) {
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
string token;
vector<string> res;
while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
token = s.substr (pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
res.push_back (token);
}
res.push_back (s.substr (pos_start));
return res;
}
/**
* This function parses the ranges in the surveyranges parameter.
* It takes a string like "q2:1-48,q4:1-36" and returns a vector of integers like [1, 48, 1, 36]
*
* @param surveyranges a string that looks like this: q2:1-48,q4:1-48
*
* @return A vector of integers.
*/
std::vector<int> parse_survey_ranges(string surveyranges) {
// the surveyranges string looks like this: q2:1-48,q4:1-48
std::vector<int> limits;
std::vector<std::string> tokenlist = split(surveyranges, ",");
std::vector q1_2_param = split(tokenlist[0], ":");
std::vector q2_2_param = split(tokenlist[1], ":");
std::vector q1_minmax = split(q1_2_param[1], "-");
std::vector q2_minmax = split(q2_2_param[1], "-");
limits.push_back (stoi(q1_minmax[0]));
limits.push_back (stoi(q1_minmax[1]));
limits.push_back (stoi(q2_minmax[0]));
limits.push_back (stoi(q2_minmax[1]));
return limits;
}
/**
* Function to create and initialise the survey results record.
* If the surveyrecord table is empty, then create the (single record) in the surveyrecord table
*/
void freeosgov::survey_init() {
survey_index survey_table(get_self(), get_self().value);
auto survey_iterator = survey_table.begin();
if (survey_iterator == survey_table.end()) {
// emplace
survey_table.emplace(get_self(), [&](auto &s) { s.iteration = current_iteration(); });
}
}
/**
* Function to reset the survey record at the beginning of a new iteration
*/
void freeosgov::survey_reset() {
survey_index survey_table(get_self(), get_self().value);
auto survey_iterator = survey_table.begin();
if (survey_iterator != survey_table.end()) {
survey_table.modify(survey_iterator, get_self(), [&](auto &survey) {
survey.iteration = current_iteration();
survey.participants = 0;
survey.q1choice1 = 0;
survey.q1choice2 = 0;
survey.q1choice3 = 0;
survey.q2average = 0.0;
survey.q3choice1 = 0;
survey.q3choice2 = 0;
survey.q3choice3 = 0;
survey.q4average = 0.0;
survey.q5choice1 = 0;
survey.q5choice2 = 0;
survey.q5choice3 = 0;
survey.q5choice4 = 0;
survey.q5choice5 = 0;
survey.q5choice6 = 0;
survey.q5choice7 = 0;
survey.q5choice8 = 0;
});
}
}
/**
* Action called by a user to submit their responses to the weekly survey
*
* @param user the account name of the user
* @param q1response 1, 2, or 3
* @param q2response a number between 1 and 10
* @param q3response 1, 2, 3
* @param q4response
* @param q5choice1 the user's first choice for question 5
* @param q5choice2 the user's second choice for question 5
* @param q5choice3 the user's 3rd choice for question 5
*/
void freeosgov::survey(name user, uint8_t q1response, uint8_t q2response, uint8_t q3response, uint8_t q4response, uint8_t q5choice1, uint8_t q5choice2, uint8_t q5choice3) {
require_auth(user);
// note that tick() checks that system is operational (masterswitch parameter set to "1")
tick();
// is the user registered?
check(is_registered(user), "survey is not open to unregistered users");
// is the user verified?
check(is_user_verified(user), "survey is not open to unverified users");
// is the system operational?
uint32_t this_iteration = current_iteration();
check(this_iteration != 0, "The freeos system is not available at this time");
// is the user alive
check(is_user_alive(user), "user has exceeded the maximum number of iterations");
// are we in the survey period?
check(is_action_period("survey"), "It is outside of the survey period");
// has the user already completed the survey?
svr_index svrs_table(get_self(), user.value);
auto svr_iterator = svrs_table.begin();
// if there is no svr record for the user then create it - we will update it at the end of the action
if (svr_iterator == svrs_table.end()) {
// emplace
svrs_table.emplace(get_self(), [&](auto &svr) { ; });
svr_iterator = svrs_table.begin();
} else {
check(svr_iterator->survey0 != this_iteration &&
svr_iterator->survey1 != this_iteration &&
svr_iterator->survey2 != this_iteration &&
svr_iterator->survey3 != this_iteration &&
svr_iterator->survey4 != this_iteration,
"user has already completed the survey");
}
// parameter checking
// get and parse the survey slider ranges
string surveyranges = get_parameter(name("surveyranges"));
std::vector<int> range_values = parse_survey_ranges(surveyranges);
// responses 1 to 4 - must be an integer between bounds
check(q1response >= 1 && q1response <= 3, "Response 1 must be a number between 1 and 3");
check(q2response >= range_values[0] && q2response <= range_values[1], "Response 2 is out of range");
check(q3response >= 1 && q3response <= 3, "Response 3 must be a number between 1 and 3");
check(q4response >= range_values[2] && q4response <= range_values[3], "Response 4 is out of range");
check(q5choice1 >= 1 && q5choice1 <= 6, "Response 5 choice 1 must be a number between 1 and 6");
check(q5choice2 >= 1 && q5choice2 <= 6, "Response 5 choice 2 must be a number between 1 and 6");
check(q5choice3 >= 1 && q5choice3 <= 6, "Response 5 choice 3 must be a number between 1 and 6");
// response 5 - the 3 choices must not contain duplicates
check((q5choice1 != q5choice2) && (q5choice2 != q5choice3) && (q5choice3 != q5choice1), "Response 5 has duplicate values");
// store the responses
survey_index survey_table(get_self(), get_self().value);
auto survey_iterator = survey_table.begin();
check(survey_iterator != survey_table.end(), "survey record is not defined");
// process the responses from the user
// for multiple choice options, increment to add the user's selection
// for running averages, compute new running average
survey_table.modify(survey_iterator, get_self(), [&](auto &survey) {
// question 1
switch(q1response) {
case 1:
survey.q1choice1++;
break;
case 2:
survey.q1choice2++;
break;
case 3:
survey.q1choice3++;
break;
}
// question 2 - running average
survey.q2average = ((survey.q2average * survey.participants) + q2response) / (survey.participants + 1);
// question 3
switch(q3response) {
case 1:
survey.q3choice1++;
break;
case 2:
survey.q3choice2++;
break;
case 3:
survey.q3choice3++;
break;
}
// question 4 - running average
survey.q4average = ((survey.q4average * survey.participants) + q4response) / (survey.participants + 1);
// question 5 - need to iterate the list of choices
uint8_t q5choices[3] = { q5choice1, q5choice2, q5choice3 };
uint8_t points[3] = { 3,2,1 }; // points to distribute for 1st, 2nd, 3rd priorities
for (size_t i = 0; i < sizeof(q5choices); i++) {
switch(q5choices[i]) {
case 1:
survey.q5choice1 += points[i];
break;
case 2:
survey.q5choice2 += points[i];
break;
case 3:
survey.q5choice3 += points[i];
break;
case 4:
survey.q5choice4 += points[i];
break;
case 5:
survey.q5choice5 += points[i];
break;
case 6:
survey.q5choice6 += points[i];
break;
}
}
// update the number of participants
survey.participants += 1;
}); // end of modify
// record that the user has responded to this iteration's vote
svrs_table.modify(svr_iterator, get_self(), [&](auto &svr) {
switch (this_iteration % 5) {
case 0: svr.survey0 = this_iteration; break;
case 1: svr.survey1 = this_iteration; break;
case 2: svr.survey2 = this_iteration; break;
case 3: svr.survey3 = this_iteration; break;
case 4: svr.survey4 = this_iteration; break;
}
}); // end of modify
// increment the number of participants in this iteration
system_index system_table(get_self(), get_self().value);
auto system_iterator = system_table.begin();
check(system_iterator != system_table.end(), "system record is undefined");
system_table.modify(system_iterator, get_self(), [&](auto &s) {
s.participants += 1;
});
// update the surveys counter in the user's participant record
participants_index participants_table(get_self(), user.value);
auto participant_iterator = participants_table.begin();
check(participant_iterator != participants_table.end(), "participant record not found");
participants_table.modify(participant_iterator, get_self(), [&](auto &p) {
p.surveys += 1;
});
}
/** @} */ // end of survey group