rt300@0
|
1 //
|
rt300@0
|
2 // eventLogger.mm
|
rt300@0
|
3 // oscSenderExample
|
rt300@0
|
4 //
|
rt300@0
|
5 // Created by Robert Tubb on 05/11/2012.
|
rt300@0
|
6 //
|
rt300@0
|
7 //
|
rt300@4
|
8
|
rt300@8
|
9 //---------------------------------------------------------------------------
|
rt300@0
|
10 #include "eventLogger.h"
|
rt300@33
|
11 #include "grid.h"
|
rt300@33
|
12
|
rt300@33
|
13 extern Grid theGridView;
|
rt300@1
|
14
|
rt300@1
|
15 EventLogger eventLogger;
|
rt300@32
|
16 extern PresetManager presetManager;
|
rt300@33
|
17 //---------------------------------------------------------------------------
|
rt300@33
|
18 void lEvent::draw(){
|
rt300@33
|
19 // can't draw lines you fool
|
rt300@33
|
20 double size = 2.0;
|
rt300@33
|
21 TwoVector pos = theGridView.coordToPixel(TwoVector(val1,val2)); // euch -rely on grid view?
|
rt300@33
|
22
|
rt300@33
|
23 if(eventType == SCROLL){
|
rt300@33
|
24 ofSetColor(255,123,56);
|
rt300@33
|
25 ofRect(pos.x, pos.y,size,size);
|
rt300@33
|
26 }else if(eventType == EVALUATION_POINT){
|
rt300@33
|
27 size = sliderID/150.0; // slider id is the hover time. sorry sorry.
|
rt300@33
|
28 if(size > 40) size = 40;
|
rt300@33
|
29 ofSetColor(25,123,216);
|
rt300@33
|
30 ofNoFill();
|
rt300@33
|
31 ofEllipse(pos.x, pos.y, size, size);
|
rt300@33
|
32
|
rt300@33
|
33 }
|
rt300@33
|
34 }
|
rt300@29
|
35
|
rt300@8
|
36 //---------------------------------------------------------------------------
|
rt300@1
|
37 EventLogger::EventLogger(){
|
rt300@8
|
38
|
rt300@31
|
39 consentGiven = true; // unless told otherwise firstAppOpen
|
rt300@8
|
40 loggingEnabled = true;
|
rt300@22
|
41 serverConnectionOK = false;
|
rt300@22
|
42 questionnaireCompleted = false;
|
rt300@22
|
43 questionnaireUploaded = false;
|
rt300@27
|
44 logUploadInProgress = false;
|
rt300@22
|
45 ofxiPhoneDeviceType iOSdeviceType = ofxiPhoneGetDeviceType();
|
rt300@22
|
46 cout << "Device: " << iOSdeviceType << '\n';
|
rt300@22
|
47
|
rt300@25
|
48 nextUploadQty = UPLOAD_CHUNK_SIZE; // amount of data uploaded is always more than UPLOAD_CHUNK_SIZE events
|
rt300@27
|
49
|
rt300@8
|
50 }
|
rt300@8
|
51 //---------------------------------------------------------------------------
|
rt300@24
|
52 // draw() - show path of last N scroll events - can be scrubbed along?
|
rt300@24
|
53 //
|
rt300@24
|
54 //---------------------------------------------------------------------------
|
rt300@29
|
55
|
rt300@8
|
56 void EventLogger::init(){
|
rt300@4
|
57
|
rt300@30
|
58 checkExistingLogFile(ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME);
|
rt300@8
|
59 sessionStartTime = ofGetSystemTime();
|
rt300@31
|
60
|
rt300@31
|
61 logEvent(APP_STARTED);
|
rt300@31
|
62
|
rt300@31
|
63 serverComms = [[ServerComms alloc] init];
|
rt300@9
|
64
|
rt300@9
|
65 testConnection();
|
rt300@7
|
66
|
rt300@31
|
67 }
|
rt300@31
|
68 //---------------------------------------------------------------------------
|
rt300@31
|
69 bool EventLogger::testConnection(){
|
rt300@31
|
70 Json::Value root;
|
rt300@31
|
71 root["x"] = "y";
|
rt300@31
|
72 cout << "testConnection\n";
|
rt300@31
|
73 sendToServer("testConnection", root, true);
|
rt300@31
|
74
|
rt300@30
|
75 }
|
rt300@30
|
76 //---------------------------------------------------------------------------
|
rt300@30
|
77 // this reads the persistent log file , checks if we've used the app before and
|
rt300@30
|
78 // if we've answered questionnaire or not
|
rt300@30
|
79 void EventLogger::checkExistingLogFile(const string &jsonFile){
|
rt300@30
|
80 Json::Value root;
|
rt300@30
|
81 Json::Reader reader;
|
rt300@30
|
82
|
rt300@30
|
83 /////////////
|
rt300@30
|
84 // read file
|
rt300@30
|
85
|
rt300@30
|
86 ifstream theFile(jsonFile.c_str());
|
rt300@30
|
87 stringstream fileText;
|
rt300@30
|
88 string line;
|
rt300@30
|
89 if(!theFile){
|
rt300@30
|
90
|
rt300@30
|
91 firstEverAppOpen();
|
rt300@30
|
92 return;
|
rt300@30
|
93 }else{
|
rt300@30
|
94 while(theFile){
|
rt300@30
|
95 theFile >> line;
|
rt300@30
|
96 // cout << line; // lots!!!!
|
rt300@30
|
97 fileText << line;
|
rt300@30
|
98 }
|
rt300@30
|
99 theFile.close();
|
rt300@30
|
100 }
|
rt300@30
|
101
|
rt300@30
|
102 // otherwise, we've been in here before
|
rt300@30
|
103
|
rt300@30
|
104 /////////////
|
rt300@30
|
105 // Parse json
|
rt300@30
|
106 cout << "size of log JSON string:" << fileText.str().length() << "BYTES \n";
|
rt300@30
|
107
|
rt300@30
|
108 bool parsingSuccessful = reader.parse( fileText.str(), root );
|
rt300@30
|
109
|
rt300@30
|
110 if ( !parsingSuccessful )
|
rt300@30
|
111 {
|
rt300@30
|
112 // report to the user the failure and their locations in the document.
|
rt300@30
|
113 std::cout << "Failed to parse event log JSON: \n"
|
rt300@30
|
114 << reader.getFormattedErrorMessages();
|
rt300@30
|
115 return;
|
rt300@30
|
116 }
|
rt300@30
|
117
|
rt300@30
|
118 /////////////////
|
rt300@30
|
119 // now put user deets into variables
|
rt300@30
|
120
|
rt300@30
|
121 userName = root["userName"].asString();
|
rt300@30
|
122 deviceID = root["deviceID"].asLargestInt();
|
rt300@30
|
123 nextUploadNumber = root["uploadNumber"].asInt();
|
rt300@30
|
124 savedInteractionTime = root["savedInteractionTime"].asLargestInt();
|
rt300@30
|
125 questionnaireCompleted = root["questionnaireCompleted"].asBool();
|
rt300@30
|
126 questionnaireUploaded = root["questionnaireUploaded"].asBool();
|
rt300@30
|
127 interfaceOrder = root["interfaceOrder"].asInt();
|
rt300@30
|
128
|
rt300@29
|
129
|
rt300@30
|
130 // check for unuploaded evts
|
rt300@30
|
131 const Json::Value jlogs = root["events"];
|
rt300@29
|
132
|
rt300@30
|
133 for ( int index = 0; index < jlogs.size(); ++index ) theEvents.push_back(lEvent(jlogs[index]));
|
rt300@30
|
134 if(theEvents.size() > nextUploadQty && ! logUploadInProgress){
|
rt300@30
|
135 //try to upload
|
rt300@30
|
136 uploadEventLog(true);
|
rt300@30
|
137 }
|
rt300@30
|
138
|
rt300@30
|
139 //////////////
|
rt300@30
|
140 // what stage is the experiment at??
|
rt300@30
|
141
|
rt300@30
|
142 if(questionnaireCompleted && !questionnaireUploaded){
|
rt300@30
|
143 // then read it in and upload it
|
rt300@30
|
144 Json::Value JQ = root["questionnaire"];
|
rt300@30
|
145 Json::Value JArray = JQ["qAnswers"];
|
rt300@30
|
146 if(JArray.size() < 2){
|
rt300@30
|
147 cout << "Error - status of questionnaire is wierd\n";
|
rt300@30
|
148 }
|
rt300@30
|
149 for ( unsigned int i = 0; i < JArray.size(); i++ )
|
rt300@30
|
150 {
|
rt300@30
|
151 questionnaireAnswers.push_back(JArray[i].asInt());
|
rt300@30
|
152 }
|
rt300@30
|
153 questionnaireComments = JQ["comments"].toStyledString();
|
rt300@30
|
154 uploadQuestionnaire();
|
rt300@30
|
155 }
|
rt300@30
|
156 //TODO if questionnaire wasn't reached but still opened then set the timer to something sensible
|
rt300@30
|
157
|
rt300@30
|
158 if(!questionnaireCompleted){
|
rt300@30
|
159 // for now sod it, start from scratch
|
rt300@30
|
160 firstEverAppOpen();
|
rt300@30
|
161 return;
|
rt300@30
|
162 }
|
rt300@30
|
163 cout << "Total interaction time: " << savedInteractionTime << '\n';
|
rt300@30
|
164
|
rt300@30
|
165 //timer.setInteractionTime(savedInteractionTime);
|
rt300@30
|
166 //timer.setOrderFromPrevious(interfaceOrder);
|
rt300@30
|
167
|
rt300@8
|
168 }
|
rt300@29
|
169
|
rt300@8
|
170 //---------------------------------------------------------------------------
|
rt300@30
|
171
|
rt300@30
|
172 void EventLogger::firstEverAppOpen(){
|
rt300@30
|
173 cout<<"no event log file - first APP open\n";
|
rt300@30
|
174 nextUploadNumber = 0;
|
rt300@30
|
175 deviceID = ofGetSystemTimeMicros();
|
rt300@30
|
176 savedInteractionTime = 0;
|
rt300@30
|
177 questionnaireCompleted = false;
|
rt300@30
|
178 questionnaireUploaded = false;
|
rt300@30
|
179
|
rt300@33
|
180 //((testApp *)ofGetAppPtr())->showIntro();
|
rt300@33
|
181 //consentGiven = false;
|
rt300@33
|
182 ((testApp *)ofGetAppPtr())->justStart();
|
rt300@33
|
183
|
rt300@30
|
184
|
rt300@30
|
185 }
|
rt300@30
|
186
|
rt300@30
|
187
|
rt300@30
|
188 //---------------------------------------------------------------------------
|
rt300@28
|
189 void EventLogger::questionnaireAnswersObtained(vector<int> answers, const char* userComments){
|
rt300@22
|
190
|
rt300@22
|
191 questionnaireCompleted = true;
|
rt300@22
|
192 questionnaireAnswers = answers;
|
rt300@28
|
193 questionnaireComments = userComments;
|
rt300@22
|
194 uploadQuestionnaire();
|
rt300@31
|
195 logEvent(QUESTIONNAIRE_COMPLETED);
|
rt300@22
|
196
|
rt300@22
|
197 }
|
rt300@22
|
198 //---------------------------------------------------------------------------
|
rt300@27
|
199 void EventLogger::uploadQuestionnaire(){
|
rt300@22
|
200 // show indicator
|
rt300@22
|
201 cout << "^^^^^^^^ UPLOADING QUESTIONNAIRE ^^^^^^^^ \n";
|
rt300@31
|
202 cout << questionnaireToJson() << "\n";
|
rt300@27
|
203 sendToServer("questionnaire", questionnaireToJson(), true);
|
rt300@27
|
204
|
rt300@22
|
205 }
|
rt300@22
|
206 //---------------------------------------------------------------------------
|
rt300@27
|
207 bool EventLogger::sendToServer(string functionName, Json::Value jsonData, bool async = false){
|
rt300@22
|
208
|
rt300@22
|
209 Json::FastWriter writer;
|
rt300@22
|
210 string jsontext = writer.write( jsonData );
|
rt300@22
|
211
|
rt300@27
|
212 // remove newline
|
rt300@22
|
213 if (!jsontext.empty() && jsontext[jsontext.length()-1] == '\n') {
|
rt300@22
|
214 jsontext.erase(jsontext.length()-1);
|
rt300@22
|
215 }
|
rt300@30
|
216 ostringstream jd;
|
rt300@30
|
217 jd << jsontext;
|
rt300@30
|
218 NSString *theData = [NSString stringWithUTF8String:jd.str().c_str()];
|
rt300@30
|
219 NSString *theType = [NSString stringWithUTF8String:functionName.c_str()];
|
rt300@22
|
220
|
rt300@30
|
221 if(async){
|
rt300@30
|
222 [serverComms doPostRequest:theType withData:theData];
|
rt300@30
|
223 }else{
|
rt300@30
|
224 bool success = [serverComms doSyncPostRequest:theType withData:theData];
|
rt300@30
|
225 return success;
|
rt300@30
|
226 }
|
rt300@29
|
227
|
rt300@27
|
228 }
|
rt300@27
|
229 //-----------------------------
|
rt300@30
|
230 void EventLogger::questionnaireOK(){
|
rt300@30
|
231 questionnaireUploaded = true;
|
rt300@30
|
232 }
|
rt300@30
|
233 //-----------------------------
|
rt300@30
|
234 void EventLogger::eventlogOK(){
|
rt300@33
|
235 // COMMENT THIS IF UPLAODING FROM IPAD TO XCODE
|
rt300@32
|
236 theEvents.clear();
|
rt300@30
|
237 cout << "EVENT LOG UPLOAD SUCCESS\n";
|
rt300@30
|
238 nextUploadNumber++;
|
rt300@30
|
239 logUploadInProgress = false;
|
rt300@30
|
240 }
|
rt300@30
|
241 //-----------------------------
|
rt300@30
|
242 void EventLogger::testConnectionOK(){
|
rt300@30
|
243 cout << "^^^^^^^^ server connection OK ^^^^^^^^ \n";
|
rt300@30
|
244 serverConnectionOK = true;
|
rt300@30
|
245 }
|
rt300@30
|
246 //-----------------------------
|
rt300@30
|
247 void EventLogger::questionnaireNotOK(){
|
rt300@32
|
248 cout << "XXXXX questionnaire NOT OK XXXXXXX \n";
|
rt300@30
|
249 questionnaireUploaded = false;
|
rt300@30
|
250 }
|
rt300@30
|
251 //-----------------------------
|
rt300@30
|
252 void EventLogger::eventlogNotOK(){
|
rt300@30
|
253 // try later
|
rt300@32
|
254 cout << "XXXXX event log NOT OK XXXXXXX \n";
|
rt300@30
|
255 nextUploadQty += UPLOAD_CHUNK_SIZE;
|
rt300@30
|
256 logUploadInProgress = false;
|
rt300@30
|
257 }
|
rt300@30
|
258 //-----------------------------
|
rt300@30
|
259 void EventLogger::testConnectionNotOK(){
|
rt300@32
|
260 cout << "XXXXX server connection NOT OK XXXXXXX \n";
|
rt300@30
|
261 serverConnectionOK = false;
|
rt300@30
|
262 // alert?
|
rt300@30
|
263
|
rt300@30
|
264 }
|
rt300@27
|
265
|
rt300@27
|
266
|
rt300@9
|
267 //---------------------------------------------------------------------------
|
rt300@9
|
268
|
rt300@27
|
269 bool EventLogger::uploadEventLog(bool async){
|
rt300@33
|
270 // COMMENT THIS OUTRT !!!!!!!!
|
rt300@33
|
271 return false;
|
rt300@22
|
272 // show indicator
|
rt300@27
|
273 logUploadInProgress = true;
|
rt300@25
|
274 cout << "^^^^^^^^ ATTEMPTING TO UPLOAD " << theEvents.size() << " EVENTS ^^^^^^^^ .\n";
|
rt300@27
|
275 if(!async){
|
rt300@27
|
276 bool success = sendToServer("eventlog", logsToJson(), async);
|
rt300@27
|
277 if(!success){
|
rt300@29
|
278 // try later : NOPE has maximum size
|
rt300@27
|
279 nextUploadQty += UPLOAD_CHUNK_SIZE;
|
rt300@27
|
280 }else{
|
rt300@27
|
281
|
rt300@27
|
282 // if success - clear memory
|
rt300@31
|
283 // IF UPLAODING FROM IPAD TO XCODE COMMENT OUT
|
rt300@32
|
284 theEvents.clear();
|
rt300@27
|
285 cout << "UPLOAD SUCCESS\n";
|
rt300@27
|
286 nextUploadNumber++;
|
rt300@27
|
287 }
|
rt300@27
|
288 logUploadInProgress = false;
|
rt300@27
|
289 return success;
|
rt300@9
|
290 }else{
|
rt300@27
|
291 sendToServer("eventlog", logsToJson(), async);
|
rt300@9
|
292 }
|
rt300@1
|
293 }
|
rt300@8
|
294 //----------------------------------------------------------------------------
|
rt300@33
|
295 bool eventWasInRegion(vector<lEvent>::iterator eiter, TwoVector regionTopLeft, TwoVector regionBottomRight){
|
rt300@33
|
296 if( ((*eiter).val1 > regionTopLeft.x ) && ((*eiter).val2 > regionTopLeft.y ) && ((*eiter).val1 < regionBottomRight.x ) && ((*eiter).val2 < regionBottomRight.y )){
|
rt300@33
|
297 return true;
|
rt300@33
|
298 }else{
|
rt300@33
|
299 return false;
|
rt300@33
|
300 }
|
rt300@33
|
301
|
rt300@33
|
302 }
|
rt300@33
|
303 //----------------------------------------------------------------------------
|
rt300@33
|
304 void EventLogger::drawTrail(const TwoVector min, const TwoVector max){
|
rt300@33
|
305 // horribly inefficient
|
rt300@33
|
306 vector<lEvent>::iterator eiter;
|
rt300@33
|
307 vector<lEvent>::iterator preveiter;
|
rt300@33
|
308 int i = 0;
|
rt300@33
|
309
|
rt300@33
|
310
|
rt300@33
|
311
|
rt300@33
|
312 preveiter = theEvents.begin();
|
rt300@33
|
313 for(eiter = --theEvents.end(); eiter > theEvents.begin(); eiter--){
|
rt300@27
|
314
|
rt300@33
|
315 //cout << i << '\n';
|
rt300@33
|
316 if( (*eiter).eventType == SCROLL || (*eiter).eventType == EVALUATION_POINT){
|
rt300@33
|
317 i++;
|
rt300@33
|
318 if(i > SCROLL_TRAIL_LENGTH){
|
rt300@33
|
319 return;
|
rt300@33
|
320 }
|
rt300@33
|
321 if(eventWasInRegion(eiter, min, max) || eventWasInRegion(preveiter, min, max)){
|
rt300@33
|
322 // draw a line between prev and this
|
rt300@33
|
323 if(preveiter != theEvents.begin()){
|
rt300@33
|
324 TwoVector start = theGridView.coordToPixel(TwoVector((*preveiter).val1,(*preveiter).val2));
|
rt300@33
|
325 TwoVector end = theGridView.coordToPixel(TwoVector((*eiter).val1,(*eiter).val2));
|
rt300@33
|
326 ofSetColor(255,255,255,96);
|
rt300@33
|
327 ofLine(start.x,start.y, end.x, end.y);
|
rt300@33
|
328
|
rt300@33
|
329 }else{
|
rt300@33
|
330 // draw line from listen point to last evt
|
rt300@33
|
331 TwoVector start = TwoVector(ofGetWidth()*0.5,ofGetHeight()*0.5);
|
rt300@33
|
332 TwoVector end = theGridView.coordToPixel(TwoVector((*eiter).val1,(*eiter).val2));
|
rt300@33
|
333 ofSetColor(255,255,255,96);
|
rt300@33
|
334 ofLine(start.x,start.y, end.x, end.y);
|
rt300@33
|
335 // draw ever growing listen point
|
rt300@33
|
336
|
rt300@33
|
337 }
|
rt300@33
|
338
|
rt300@33
|
339 }
|
rt300@33
|
340 preveiter = eiter;
|
rt300@33
|
341 }
|
rt300@33
|
342 if( (*eiter).eventType == EVALUATION_POINT){
|
rt300@33
|
343 if( ((*eiter).val1 > min.x ) && ((*eiter).val2 > min.y ) && ((*eiter).val1 < max.x ) && ((*eiter).val2 < max.y )){
|
rt300@33
|
344 // draw it
|
rt300@33
|
345 (*eiter).draw();
|
rt300@33
|
346 }
|
rt300@33
|
347 }
|
rt300@33
|
348 }
|
rt300@33
|
349
|
rt300@33
|
350 }
|
rt300@8
|
351
|
rt300@28
|
352 //---------------------------------------------------------------------------
|
rt300@28
|
353 // only called when doing supervised tests
|
rt300@27
|
354 void EventLogger::newUser(){
|
rt300@32
|
355 // store old stuff
|
rt300@32
|
356
|
rt300@32
|
357 saveSessionToFile();
|
rt300@32
|
358 presetManager.saveSessionToFile(userName);
|
rt300@27
|
359 cout<<"setup new user\n";
|
rt300@29
|
360 deleteLogs();
|
rt300@27
|
361 nextUploadNumber = 0;
|
rt300@27
|
362 deviceID = ofGetSystemTimeMicros();
|
rt300@27
|
363 savedInteractionTime = 0;
|
rt300@28
|
364 totalInteractionTime = 0;
|
rt300@28
|
365 sessionStartTime = ofGetSystemTime();
|
rt300@27
|
366 questionnaireCompleted = false;
|
rt300@29
|
367 questionnaireUploaded = false;
|
rt300@29
|
368
|
rt300@29
|
369 ((testApp *)ofGetAppPtr())->showIntro();
|
rt300@27
|
370
|
rt300@27
|
371 }
|
rt300@8
|
372 //---------------------------------------------------------------------------
|
rt300@8
|
373 // called from alertView OK in iViewController
|
rt300@8
|
374 void EventLogger::setUsername(const char *u){
|
rt300@8
|
375 userName = u;
|
rt300@28
|
376 // start interaction clock
|
rt300@29
|
377
|
rt300@29
|
378 //timer.startInteractionClock();
|
rt300@29
|
379 [((testApp *)ofGetAppPtr())->tsc startTimer];
|
rt300@33
|
380
|
rt300@33
|
381 // press play??
|
rt300@33
|
382 }
|
rt300@33
|
383 //---------------------------------------------------------------------------
|
rt300@33
|
384 void EventLogger::thinnedScrollEvent(lEvent newEvent){
|
rt300@33
|
385 static lEvent previousEvent(EMPTY_EVENT); // initialised as whatever
|
rt300@33
|
386 static int eventCounter = 0;
|
rt300@33
|
387
|
rt300@33
|
388 // if first event then log it. it won't be, but still.
|
rt300@33
|
389 if(theEvents.size() == 0){
|
rt300@33
|
390 theEvents.push_back(newEvent);
|
rt300@33
|
391 previousEvent = newEvent;
|
rt300@33
|
392 return;
|
rt300@33
|
393 }
|
rt300@33
|
394
|
rt300@33
|
395 // if previous event is more than 300ms ago, or event type has changed, log both of them (lots of events on move+zoom combinations!!)
|
rt300@33
|
396 int gap = newEvent.eventTime - previousEvent.eventTime;
|
rt300@33
|
397 if(gap > 300){
|
rt300@33
|
398 // log previous event as a evaluation point MAYBE TODO if previous event was logged as scroll chuck it out?
|
rt300@33
|
399 theEvents.push_back(lEvent(EVALUATION_POINT, previousEvent.val1, previousEvent.val2, gap));
|
rt300@33
|
400 // and now new event as scroll
|
rt300@33
|
401 theEvents.push_back(newEvent);
|
rt300@33
|
402 eventCounter = 0;
|
rt300@33
|
403
|
rt300@33
|
404 }else if(eventCounter >= EVENT_THIN_FACTOR){ // otherwise only record every Nth event
|
rt300@33
|
405 theEvents.push_back(newEvent);
|
rt300@33
|
406 eventCounter = 0;
|
rt300@33
|
407
|
rt300@33
|
408 }
|
rt300@33
|
409 eventCounter++;
|
rt300@33
|
410 previousEvent = newEvent;
|
rt300@33
|
411
|
rt300@28
|
412 }
|
rt300@28
|
413 //---------------------------------------------------------------------------
|
rt300@28
|
414 void EventLogger::thinnedLogEvent(lEvent newEvent){
|
rt300@33
|
415 static lEvent previousEvent(EMPTY_EVENT); // initialised as whatever. hopefully won't log
|
rt300@28
|
416 static int eventCounter = 0;
|
rt300@28
|
417
|
rt300@28
|
418 // if first event then log it. it won't be, but still.
|
rt300@28
|
419 if(theEvents.size() == 0){
|
rt300@28
|
420 theEvents.push_back(newEvent);
|
rt300@28
|
421 previousEvent = newEvent;
|
rt300@28
|
422 return;
|
rt300@28
|
423 }
|
rt300@28
|
424
|
rt300@31
|
425 // if previous event is more than 300ms ago, or event type has changed, log both of them (lots of events on move+zoom combinations!!)
|
rt300@28
|
426 int gap = newEvent.eventTime - previousEvent.eventTime;
|
rt300@28
|
427 if(gap > 300 || newEvent.eventType != previousEvent.eventType){
|
rt300@28
|
428 // if prev event not logged, log it
|
rt300@31
|
429 if(theEvents.back().eventTime != previousEvent.eventTime){
|
rt300@28
|
430 theEvents.push_back(previousEvent);
|
rt300@28
|
431 }
|
rt300@28
|
432 theEvents.push_back(newEvent);
|
rt300@7
|
433
|
rt300@28
|
434 }
|
rt300@28
|
435
|
rt300@28
|
436 // otherwise only record every Nth event
|
rt300@28
|
437 if(eventCounter >= EVENT_THIN_FACTOR){
|
rt300@28
|
438 theEvents.push_back(newEvent);
|
rt300@28
|
439 eventCounter = 0;
|
rt300@28
|
440
|
rt300@28
|
441 }
|
rt300@28
|
442 eventCounter++;
|
rt300@28
|
443 previousEvent = newEvent;
|
rt300@7
|
444 }
|
rt300@8
|
445 //---------------------------------------------------------------------------
|
rt300@5
|
446 void EventLogger::logEvent(const leventType& evtType,const TwoVector& centre, const double& scale, const int& sliderID, const double& sliderVal){
|
rt300@33
|
447
|
rt300@1
|
448
|
rt300@1
|
449 // scroll has 2 double coords
|
rt300@1
|
450 // zoom has 1 double scale
|
rt300@1
|
451 // save preset has 2 coords
|
rt300@1
|
452 // switch view has view type
|
rt300@9
|
453 // slider change has int slider index and 1 float value
|
rt300@1
|
454
|
rt300@4
|
455 // get time for key index
|
rt300@4
|
456
|
rt300@5
|
457 // thinFactor
|
rt300@5
|
458 if(!loggingEnabled) return;
|
rt300@4
|
459 switch ( evtType ) {
|
rt300@28
|
460 // data thinning here
|
rt300@28
|
461 case SCROLL:
|
rt300@33
|
462 thinnedScrollEvent(lEvent(evtType,centre.x,centre.y));
|
rt300@33
|
463 //theEvents.push_back(lEvent(evtType,centre.x,centre.y));
|
rt300@28
|
464 break;
|
rt300@28
|
465 case ZOOM:
|
rt300@28
|
466 thinnedLogEvent(lEvent(evtType,scale));
|
rt300@28
|
467 break;
|
rt300@28
|
468 case CHANGE_SLIDER:
|
rt300@28
|
469 thinnedLogEvent(lEvent(evtType,sliderVal , 0.0 , sliderID));
|
rt300@28
|
470 break;
|
rt300@29
|
471 // non thinned data
|
rt300@4
|
472 case SAVE_PRESET:
|
rt300@5
|
473 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
|
rt300@4
|
474 // Code
|
rt300@4
|
475 break;
|
rt300@4
|
476 case SAVE_DESET:
|
rt300@5
|
477 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
|
rt300@4
|
478 break;
|
rt300@28
|
479
|
rt300@5
|
480 case SCROLL_STOPPED:
|
rt300@5
|
481 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
|
rt300@22
|
482 cout << "SCROLL STOPPED EVENT\n";
|
rt300@22
|
483 break;
|
rt300@22
|
484 case SNAPPED_TO_PRESET:
|
rt300@22
|
485 theEvents.push_back(lEvent(evtType,centre.x,centre.y,sliderID));
|
rt300@22
|
486 cout << "SCROLL STOPPED EVENT\n";
|
rt300@4
|
487 break;
|
rt300@25
|
488 case SWAP_VIEW:
|
rt300@25
|
489 theEvents.push_back(lEvent(evtType,0.0 , 0.0 , sliderID)); // slider ID is which view
|
rt300@25
|
490 break;
|
rt300@4
|
491 default:
|
rt300@25
|
492 // default is just an event type with no values
|
rt300@25
|
493 theEvents.push_back(lEvent(evtType));
|
rt300@4
|
494 break;
|
rt300@1
|
495 }
|
rt300@27
|
496 if(theEvents.size() > nextUploadQty && !logUploadInProgress){
|
rt300@27
|
497 //try to upload asynchronously
|
rt300@27
|
498 uploadEventLog(true);
|
rt300@7
|
499 }
|
rt300@9
|
500 //sessionTime = (ofGetSystemTime() - sessionStartTime);
|
rt300@28
|
501 totalInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime); // milliseconds
|
rt300@28
|
502
|
rt300@1
|
503 }
|
rt300@29
|
504 //--------------------------------------------------------------------
|
rt300@29
|
505 // called from newUser
|
rt300@29
|
506 void EventLogger::deleteLogs(){
|
rt300@29
|
507 // the
|
rt300@29
|
508 theEvents.clear();
|
rt300@29
|
509 string fname = ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME;
|
rt300@29
|
510 ofFile logFile(fname,ofFile::WriteOnly);
|
rt300@29
|
511 logFile << "";
|
rt300@29
|
512 logFile.close();
|
rt300@29
|
513 }
|
rt300@8
|
514 //---------------------------------------------------------------------------
|
rt300@7
|
515
|
rt300@7
|
516 void EventLogger::exitAndSave(){
|
rt300@25
|
517
|
rt300@25
|
518 if(!consentGiven){
|
rt300@25
|
519 logEvent(CONSENT_DENIED);
|
rt300@25
|
520 Json::Value jlogs = logsToJson();
|
rt300@25
|
521 // try to upload TODO (no - might hang and prevent exit???)
|
rt300@27
|
522 uploadEventLog(true);
|
rt300@25
|
523 return;
|
rt300@25
|
524 }
|
rt300@25
|
525 logEvent(APP_EXITED);
|
rt300@25
|
526 savedInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime);
|
rt300@7
|
527 // save user details
|
rt300@8
|
528 string fname = ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME;
|
rt300@8
|
529
|
rt300@31
|
530 // try to upload
|
rt300@31
|
531 // do it sync because event list needs to be cleared to prevent saving on device
|
rt300@27
|
532 uploadEventLog(false);
|
rt300@7
|
533
|
rt300@8
|
534 // write to file
|
rt300@25
|
535 // json without the logs that were uploaded!
|
rt300@25
|
536 Json::Value jlogs = logsToJson();
|
rt300@8
|
537 ofFile logFile(fname,ofFile::WriteOnly);
|
rt300@8
|
538 logFile << jlogs;
|
rt300@29
|
539 logFile.close();
|
rt300@8
|
540
|
rt300@8
|
541 }
|
rt300@8
|
542 //---------------------------------------------------------------------------
|
rt300@8
|
543
|
rt300@8
|
544 Json::Value EventLogger::logsToJson(){
|
rt300@8
|
545 // put all logged events into Json formatted string
|
rt300@8
|
546 Json::Value root;
|
rt300@8
|
547
|
rt300@8
|
548 vector<lEvent>::iterator eventIter;
|
rt300@8
|
549
|
rt300@22
|
550 root["programVersion"] = PROGRAM_VERSION;
|
rt300@8
|
551 root["userName"] = userName;
|
rt300@8
|
552 root["deviceID"] = deviceID;
|
rt300@25
|
553 root["uploadNumber"] = nextUploadNumber;
|
rt300@22
|
554 root["iOSdeviceType"] = iOSdeviceType;
|
rt300@25
|
555 root["savedInteractionTime"] = savedInteractionTime;
|
rt300@22
|
556 root["questionnaireCompleted"] = questionnaireCompleted;
|
rt300@22
|
557 root["questionnaireUploaded"] = questionnaireUploaded;
|
rt300@30
|
558 root["questionnaire"] = questionnaireToJson();
|
rt300@30
|
559
|
rt300@8
|
560
|
rt300@8
|
561 int i = 0;
|
rt300@8
|
562 for(eventIter = theEvents.begin(); eventIter < theEvents.end(); eventIter++){
|
rt300@8
|
563 root["events"][i] = (*eventIter).eventToJson();
|
rt300@8
|
564 i++;
|
rt300@7
|
565 }
|
rt300@25
|
566 root["numEventsHere"] = i;
|
rt300@8
|
567 return root;
|
rt300@8
|
568 }
|
rt300@22
|
569
|
rt300@8
|
570 //---------------------------------------------------------------------------
|
rt300@22
|
571
|
rt300@22
|
572 Json::Value EventLogger::questionnaireToJson(){
|
rt300@22
|
573 // put all answers into Json formatted string
|
rt300@22
|
574 Json::Value root;
|
rt300@22
|
575
|
rt300@22
|
576 vector<int>::iterator aIter;
|
rt300@22
|
577
|
rt300@22
|
578 Json::Value questionnaire;
|
rt300@22
|
579
|
rt300@22
|
580 int i = 0;
|
rt300@22
|
581 for(aIter = questionnaireAnswers.begin(); aIter < questionnaireAnswers.end(); aIter++){
|
rt300@22
|
582 questionnaire[i] = (*aIter);
|
rt300@22
|
583 i++;
|
rt300@22
|
584 }
|
rt300@22
|
585
|
rt300@22
|
586 root["qAnswers"] = questionnaire;
|
rt300@28
|
587 root["comments"] = questionnaireComments;
|
rt300@22
|
588 root["userName"] = userName;
|
rt300@22
|
589 root["deviceID"] = deviceID;
|
rt300@22
|
590 root["iOSdeviceType"] = iOSdeviceType;
|
rt300@22
|
591 root["programVersion"] = PROGRAM_VERSION;
|
rt300@22
|
592
|
rt300@22
|
593 return root;
|
rt300@22
|
594 }
|
rt300@30
|
595
|
rt300@8
|
596 //---------------------------------------------------------------------------
|
rt300@30
|
597 void EventLogger::printAll(){
|
rt300@30
|
598 cout << "-----------------ALL LOGGED EVENTS----------------- \n";
|
rt300@30
|
599 vector<lEvent>::iterator evIter;
|
rt300@30
|
600 cout << logsToJson() << "\n";
|
rt300@30
|
601 cout << "---------------------QUESTIONNAIRE---------------- \n";
|
rt300@30
|
602 cout << questionnaireToJson() << "\n";
|
rt300@30
|
603 };
|
rt300@8
|
604 //---------------------------------------------------------------------------
|
rt300@32
|
605
|
rt300@32
|
606 void EventLogger::saveSessionToFile(){
|
rt300@32
|
607 string fname = ofxiPhoneGetDocumentsDirectory() + userName + '_' + EVENT_LOG_FILENAME;
|
rt300@32
|
608
|
rt300@32
|
609 // write to file
|
rt300@32
|
610 // json without the logs that were uploaded!
|
rt300@32
|
611 Json::Value jlogs = logsToJson();
|
rt300@32
|
612 ofFile logFile(fname,ofFile::WriteOnly);
|
rt300@32
|
613 logFile << jlogs;
|
rt300@32
|
614 logFile.close();
|
rt300@32
|
615
|
rt300@32
|
616 }
|
rt300@33
|
617 //----------------------------------------------------------------------------
|
rt300@33
|
618 // TODO this path thing
|
rt300@33
|
619 vector<TwoVector> EventLogger::getRecentPath(int numEvents){
|
rt300@33
|
620 vector<TwoVector> thePath;
|
rt300@33
|
621
|
rt300@33
|
622 TwoVector lastScrollPos;
|
rt300@33
|
623 vector<lEvent>::iterator eventIndex;
|
rt300@33
|
624 eventIndex = theEvents.end();
|
rt300@33
|
625 int numScrolls = 0;
|
rt300@33
|
626 while(numScrolls < numEvents){
|
rt300@33
|
627 // go back check for scrolls, check for end of array etc.
|
rt300@33
|
628 thePath.push_back(lastScrollPos);
|
rt300@33
|
629 numScrolls++;
|
rt300@33
|
630 }
|
rt300@33
|
631 return thePath;
|
rt300@33
|
632 } |