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