annotate eventLogger.mm @ 28:e2c62db1e265

Started timer stuff
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Mon, 18 Feb 2013 11:45:05 +0000
parents ae4d2c3ce5e0
children fabb3a5cdfc9
rev   line source
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@28 11 #include "timer.h"
rt300@1 12
rt300@1 13 EventLogger eventLogger;
rt300@28 14 extern Timer timer;
rt300@8 15 //---------------------------------------------------------------------------
rt300@1 16 EventLogger::EventLogger(){
rt300@14 17 //QuestionnaireViewController * questionnaireViewController;
rt300@8 18
rt300@25 19 consentGiven = true; // unless told otherwise by introView
rt300@8 20 loggingEnabled = true;
rt300@22 21 serverConnectionOK = false;
rt300@22 22 questionnaireCompleted = false;
rt300@22 23 questionnaireUploaded = false;
rt300@27 24 currentHTTPRequestID = -1;
rt300@27 25 logUploadInProgress = false;
rt300@22 26 ofxiPhoneDeviceType iOSdeviceType = ofxiPhoneGetDeviceType();
rt300@22 27 cout << "Device: " << iOSdeviceType << '\n';
rt300@22 28
rt300@25 29 nextUploadQty = UPLOAD_CHUNK_SIZE; // amount of data uploaded is always more than UPLOAD_CHUNK_SIZE events
rt300@27 30
rt300@8 31 }
rt300@8 32 //---------------------------------------------------------------------------
rt300@24 33 // draw() - show path of last N scroll events - can be scrubbed along?
rt300@24 34 //
rt300@24 35 //---------------------------------------------------------------------------
rt300@8 36 void EventLogger::init(){
rt300@4 37
rt300@8 38 readJsonToLog(ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME);
rt300@8 39 sessionStartTime = ofGetSystemTime();
rt300@9 40
rt300@9 41 testConnection();
rt300@7 42
rt300@25 43 logEvent(APP_STARTED);
rt300@8 44 }
rt300@8 45 //---------------------------------------------------------------------------
rt300@28 46 void EventLogger::questionnaireAnswersObtained(vector<int> answers, const char* userComments){
rt300@22 47
rt300@22 48 questionnaireCompleted = true;
rt300@22 49 questionnaireAnswers = answers;
rt300@28 50 questionnaireComments = userComments;
rt300@22 51 uploadQuestionnaire();
rt300@22 52
rt300@22 53 }
rt300@22 54 //---------------------------------------------------------------------------
rt300@27 55 void EventLogger::uploadQuestionnaire(){
rt300@22 56 // show indicator
rt300@22 57 cout << "^^^^^^^^ UPLOADING QUESTIONNAIRE ^^^^^^^^ \n";
rt300@22 58
rt300@27 59 sendToServer("questionnaire", questionnaireToJson(), true);
rt300@27 60
rt300@22 61 }
rt300@22 62 //---------------------------------------------------------------------------
rt300@27 63 bool EventLogger::sendToServer(string functionName, Json::Value jsonData, bool async = false){
rt300@22 64 bool sent;
rt300@22 65 string request;
rt300@22 66
rt300@22 67 if(functionName == "testConnection"){
rt300@22 68 request = "http://127.0.0.1:8080/testservice/testConnection?jsontext=";
rt300@22 69 }else if(functionName == "questionnaire"){
rt300@22 70 request = "http://127.0.0.1:8080/testservice/questionnaire?jsontext=";
rt300@22 71 }else if(functionName == "eventlog"){
rt300@22 72 request = "http://127.0.0.1:8080/testservice/eventlog?jsontext=";
rt300@22 73 }
rt300@22 74 Json::FastWriter writer;
rt300@22 75 string jsontext = writer.write( jsonData );
rt300@22 76
rt300@27 77 // remove newline
rt300@22 78 if (!jsontext.empty() && jsontext[jsontext.length()-1] == '\n') {
rt300@22 79 jsontext.erase(jsontext.length()-1);
rt300@22 80 }
rt300@22 81
rt300@22 82 request.append(jsontext);
rt300@27 83
rt300@27 84 if(!async){
rt300@27 85 ofURLFileLoader fileLoader;
rt300@27 86 ofHttpResponse resp;
rt300@27 87 resp = fileLoader.get(request);
rt300@22 88
rt300@27 89 cout << "HTTP STATUS " << resp.status << "\n";
rt300@27 90 cout << "HTTP ERROR " << resp.error << "\n";
rt300@27 91 cout << "HTTP DATA " << resp.data << "\n"; // ofBuffer
rt300@27 92
rt300@27 93 stringstream response;
rt300@27 94 response << resp.data;
rt300@27 95
rt300@27 96 if (resp.status == 200){
rt300@27 97 if(response.str() == "OK"){
rt300@27 98
rt300@27 99 sent = true;
rt300@27 100 }else{
rt300@27 101 // not ok
rt300@27 102 // problem serverside
rt300@27 103 sent = false;
rt300@27 104 }
rt300@27 105 }else{
rt300@27 106
rt300@27 107 sent = false;
rt300@27 108 // SHOW AN ALERT TO USER?
rt300@27 109 }
rt300@27 110
rt300@27 111 return sent;
rt300@27 112 }else{ // async
rt300@27 113 ofURLFileLoader fileLoader;
rt300@27 114 currentHTTPRequestID = ofLoadURLAsync(request, functionName);
rt300@27 115 ofRegisterURLNotification(this);
rt300@27 116
rt300@27 117 return true; // ???
rt300@27 118 }
rt300@27 119 }
rt300@27 120 //-----------------------------
rt300@27 121 void EventLogger::urlResponse(ofHttpResponse & response){
rt300@27 122 cout << "gotHTTPRequestStatus\n";
rt300@27 123 cout << "HTTP REQUEST NAME " << response.request.name << "\n";
rt300@27 124 cout << "HTTP STATUS " << response.status << "\n";
rt300@27 125 cout << "HTTP ERROR " << response.error << "\n";
rt300@27 126 cout << "HTTP DATA " << response.data << "\n"; // ofBuffer
rt300@9 127
rt300@27 128 bool sent;
rt300@27 129 stringstream respStr;
rt300@27 130 respStr << response.data;
rt300@22 131
rt300@27 132 if (response.status == 200){
rt300@27 133 if(respStr.str() == "OK"){
rt300@22 134
rt300@22 135 sent = true;
rt300@22 136 }else{
rt300@22 137 // not ok
rt300@22 138 // problem serverside
rt300@22 139 sent = false;
rt300@22 140 }
rt300@9 141 }else{
rt300@22 142
rt300@22 143 sent = false;
rt300@9 144 // SHOW AN ALERT TO USER?
rt300@9 145 }
rt300@27 146
rt300@27 147 // now do request specific stuff
rt300@27 148 if(response.request.name == "eventlog"){
rt300@27 149 if(!sent){
rt300@27 150 // try later
rt300@27 151 nextUploadQty += UPLOAD_CHUNK_SIZE;
rt300@27 152 }else{
rt300@27 153
rt300@27 154 // if success - clear memory
rt300@27 155 theEvents.clear();
rt300@27 156 cout << "UPLOAD SUCCESS\n";
rt300@27 157 nextUploadNumber++;
rt300@27 158 }
rt300@27 159 logUploadInProgress = false;
rt300@27 160 }else if(response.request.name == "questionnaire"){
rt300@27 161 if(sent){
rt300@27 162 questionnaireUploaded = true;
rt300@27 163 }else{
rt300@27 164 questionnaireUploaded = false; // will try next time... when?
rt300@27 165 }
rt300@27 166 }else if(response.request.name == "testConnection"){
rt300@27 167
rt300@27 168 if (sent){
rt300@27 169 cout << "^^^^^^^^ server connection OK ^^^^^^^^ \n";
rt300@27 170 serverConnectionOK = true;
rt300@27 171 }else{
rt300@28 172 serverConnectionOK = false;
rt300@27 173 cout << "server connection ERROR \n";
rt300@27 174 }
rt300@27 175 }
rt300@27 176
rt300@22 177 }
rt300@22 178 //---------------------------------------------------------------------------
rt300@22 179 bool EventLogger::testConnection(){
rt300@22 180 Json::Value root;
rt300@22 181 root["test"] = "test";
rt300@22 182
rt300@22 183
rt300@27 184 sendToServer("testConnection", root, true);
rt300@27 185
rt300@9 186 }
rt300@9 187 //---------------------------------------------------------------------------
rt300@28 188 // this reads the persistent log file , checks if we've used the app before and
rt300@28 189 // if we've answered questionnaire or not
rt300@8 190 void EventLogger::readJsonToLog(const string &jsonFile){
rt300@8 191 Json::Value root;
rt300@8 192 Json::Reader reader;
rt300@5 193
rt300@5 194
rt300@8 195 ifstream theFile(jsonFile.c_str());
rt300@8 196 stringstream fileText;
rt300@8 197 string line;
rt300@8 198 if(!theFile){
rt300@8 199
rt300@8 200 firstEverAppOpen();
rt300@8 201
rt300@8 202 return;
rt300@8 203 }else{
rt300@8 204 while(theFile){
rt300@8 205 theFile >> line;
rt300@9 206 // cout << line; // lots!!!!
rt300@8 207 fileText << line;
rt300@8 208 }
rt300@8 209 theFile.close();
rt300@8 210 }
rt300@8 211
rt300@9 212 cout << "size of log JSON string:" << fileText.str().length() << "BYTES \n";
rt300@9 213
rt300@8 214 bool parsingSuccessful = reader.parse( fileText.str(), root );
rt300@8 215
rt300@8 216 if ( !parsingSuccessful )
rt300@8 217 {
rt300@8 218 // report to the user the failure and their locations in the document.
rt300@22 219 std::cout << "Failed to parse event log JSON: \n"
rt300@8 220 << reader.getFormattedErrorMessages();
rt300@8 221 return;
rt300@8 222 }
rt300@8 223
rt300@8 224 // now put user deets into variables
rt300@8 225 userName = root["userName"].asString();
rt300@8 226 deviceID = root["deviceID"].asLargestInt();
rt300@25 227 nextUploadNumber = root["uploadNumber"].asInt();
rt300@25 228 savedInteractionTime = root["savedInteractionTime"].asLargestInt();
rt300@22 229 questionnaireCompleted = root["questionnaireCompleted"].asBool();
rt300@22 230 questionnaireUploaded = root["questionnaireUploaded"].asBool();
rt300@28 231 interfaceOrder = root["interfaceOrder"].asInt();
rt300@22 232
rt300@22 233 if(questionnaireCompleted && !questionnaireUploaded){
rt300@22 234 // then read it in and upload it
rt300@28 235 Json::Value JQ = root["questionnaire"];
rt300@28 236 Json::Value JArray = JQ["qAnswers"];
rt300@22 237 if(JArray.size() < 2){
rt300@22 238 cout << "Error - status of questionnaire is wierd\n";
rt300@22 239 }
rt300@22 240 for ( unsigned int i = 0; i < JArray.size(); i++ )
rt300@22 241 {
rt300@28 242 questionnaireAnswers.push_back(JArray[i].asInt());
rt300@22 243 }
rt300@28 244 questionnaireComments = JQ["comments"].toStyledString();
rt300@22 245 uploadQuestionnaire();
rt300@22 246 }
rt300@8 247
rt300@8 248 // check for unuploaded evts
rt300@8 249 const Json::Value jlogs = root["events"];
rt300@8 250
rt300@8 251 for ( int index = 0; index < jlogs.size(); ++index ) theEvents.push_back(lEvent(jlogs[index]));
rt300@27 252 if(theEvents.size() > nextUploadQty && ! logUploadInProgress){
rt300@8 253 //try to upload
rt300@27 254 uploadEventLog(true);
rt300@8 255 }
rt300@8 256 // TODO if the total interaction time is greater than a certain amount && no questions answered - questionnaire time!
rt300@25 257 cout << "Total interaction time: " << savedInteractionTime << '\n';
rt300@28 258
rt300@28 259 timer.setInteractionTime(savedInteractionTime);
rt300@28 260 timer.setOrderFromPrevious(interfaceOrder);
rt300@8 261
rt300@9 262 }
rt300@9 263
rt300@9 264
rt300@9 265 //---------------------------------------------------------------------------
rt300@9 266
rt300@27 267 bool EventLogger::uploadEventLog(bool async){
rt300@28 268
rt300@22 269 // show indicator
rt300@27 270 logUploadInProgress = true;
rt300@25 271 cout << "^^^^^^^^ ATTEMPTING TO UPLOAD " << theEvents.size() << " EVENTS ^^^^^^^^ .\n";
rt300@27 272 if(!async){
rt300@27 273 bool success = sendToServer("eventlog", logsToJson(), async);
rt300@27 274 if(!success){
rt300@27 275 // try later
rt300@27 276 nextUploadQty += UPLOAD_CHUNK_SIZE;
rt300@27 277 }else{
rt300@27 278
rt300@27 279 // if success - clear memory
rt300@27 280 theEvents.clear();
rt300@27 281 cout << "UPLOAD SUCCESS\n";
rt300@27 282 nextUploadNumber++;
rt300@27 283 }
rt300@27 284 logUploadInProgress = false;
rt300@27 285 return success;
rt300@9 286 }else{
rt300@27 287 sendToServer("eventlog", logsToJson(), async);
rt300@9 288 }
rt300@1 289 }
rt300@8 290 //----------------------------------------------------------------------------
rt300@27 291
rt300@27 292 //----------------------------------------------------------------------------
rt300@9 293 //void EventLogger::deleteLogFile(){
rt300@8 294
rt300@8 295 //---------------------------------------------------------------------------
rt300@8 296
rt300@8 297 void EventLogger::firstEverAppOpen(){
rt300@25 298 cout<<"no event log file - first APP open\n";
rt300@25 299 nextUploadNumber = 0;
rt300@8 300 deviceID = ofGetSystemTimeMicros();
rt300@25 301 savedInteractionTime = 0;
rt300@22 302 questionnaireCompleted = false;
rt300@22 303 questionnaireUploaded = false;
rt300@8 304
rt300@24 305 ((testApp *)ofGetAppPtr())->showIntro();
rt300@25 306 consentGiven = false;
rt300@8 307
rt300@8 308 }
rt300@28 309 //---------------------------------------------------------------------------
rt300@28 310 // only called when doing supervised tests
rt300@27 311 void EventLogger::newUser(){
rt300@27 312 cout<<"setup new user\n";
rt300@27 313 nextUploadNumber = 0;
rt300@27 314 deviceID = ofGetSystemTimeMicros();
rt300@27 315 savedInteractionTime = 0;
rt300@28 316 totalInteractionTime = 0;
rt300@28 317 sessionStartTime = ofGetSystemTime();
rt300@27 318 questionnaireCompleted = false;
rt300@27 319 questionnaireUploaded = false;
rt300@27 320 consentGiven = true; // other wise we wouldn't be doing this
rt300@27 321 ((testApp *)ofGetAppPtr())->introHidden(true); // hacky
rt300@27 322
rt300@27 323 }
rt300@8 324 //---------------------------------------------------------------------------
rt300@8 325 // called from alertView OK in iViewController
rt300@8 326 void EventLogger::setUsername(const char *u){
rt300@8 327 userName = u;
rt300@28 328 // start interaction clock
rt300@28 329 timer.startInteractionClock();
rt300@28 330 }
rt300@28 331 //---------------------------------------------------------------------------
rt300@28 332 void EventLogger::thinnedLogEvent(lEvent newEvent){
rt300@28 333 static lEvent previousEvent(APP_STARTED); // initialised as whatever. hopefully won't log
rt300@28 334 static int eventCounter = 0;
rt300@28 335
rt300@28 336 // if first event then log it. it won't be, but still.
rt300@28 337 if(theEvents.size() == 0){
rt300@28 338 theEvents.push_back(newEvent);
rt300@28 339 previousEvent = newEvent;
rt300@28 340 return;
rt300@28 341 }
rt300@28 342
rt300@28 343 // if previous event is more than 300ms ago, or event type has changed, log both of them
rt300@28 344 int gap = newEvent.eventTime - previousEvent.eventTime;
rt300@28 345 if(gap > 300 || newEvent.eventType != previousEvent.eventType){
rt300@28 346 // if prev event not logged, log it
rt300@28 347 if((*theEvents.end()).eventTime != previousEvent.eventTime){
rt300@28 348 theEvents.push_back(previousEvent);
rt300@28 349 }
rt300@28 350 theEvents.push_back(newEvent);
rt300@7 351
rt300@28 352 }
rt300@28 353
rt300@28 354 // otherwise only record every Nth event
rt300@28 355 if(eventCounter >= EVENT_THIN_FACTOR){
rt300@28 356 theEvents.push_back(newEvent);
rt300@28 357 eventCounter = 0;
rt300@28 358
rt300@28 359 }
rt300@28 360 eventCounter++;
rt300@28 361 previousEvent = newEvent;
rt300@7 362 }
rt300@8 363 //---------------------------------------------------------------------------
rt300@8 364 // log zoom event
rt300@5 365 void EventLogger::logEvent(const leventType& evtType,const TwoVector& centre, const double& scale, const int& sliderID, const double& sliderVal){
rt300@3 366 //cout << "log: " << evtType << "\n";
rt300@1 367
rt300@1 368 // scroll has 2 double coords
rt300@1 369 // zoom has 1 double scale
rt300@1 370 // save preset has 2 coords
rt300@1 371 // switch view has view type
rt300@9 372 // slider change has int slider index and 1 float value
rt300@1 373
rt300@4 374 // get time for key index
rt300@4 375
rt300@5 376 // thinFactor
rt300@5 377 if(!loggingEnabled) return;
rt300@4 378 switch ( evtType ) {
rt300@28 379 // data thinning here
rt300@28 380 case SCROLL:
rt300@28 381 thinnedLogEvent(lEvent(evtType,centre.x,centre.y));
rt300@28 382 break;
rt300@28 383 case ZOOM:
rt300@28 384 thinnedLogEvent(lEvent(evtType,scale));
rt300@28 385 break;
rt300@28 386 case CHANGE_SLIDER:
rt300@28 387 thinnedLogEvent(lEvent(evtType,sliderVal , 0.0 , sliderID));
rt300@28 388 break;
rt300@28 389
rt300@4 390 case SAVE_PRESET:
rt300@5 391 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
rt300@4 392 // Code
rt300@4 393 break;
rt300@4 394 case SAVE_DESET:
rt300@5 395 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
rt300@4 396 break;
rt300@28 397
rt300@5 398 case SCROLL_STOPPED:
rt300@5 399 theEvents.push_back(lEvent(evtType,centre.x,centre.y));
rt300@22 400 cout << "SCROLL STOPPED EVENT\n";
rt300@22 401 break;
rt300@22 402 case SNAPPED_TO_PRESET:
rt300@22 403 theEvents.push_back(lEvent(evtType,centre.x,centre.y,sliderID));
rt300@22 404 cout << "SCROLL STOPPED EVENT\n";
rt300@4 405 break;
rt300@25 406 case SWAP_VIEW:
rt300@25 407 theEvents.push_back(lEvent(evtType,0.0 , 0.0 , sliderID)); // slider ID is which view
rt300@25 408 break;
rt300@4 409 default:
rt300@25 410 // default is just an event type with no values
rt300@25 411 theEvents.push_back(lEvent(evtType));
rt300@4 412 break;
rt300@1 413 }
rt300@27 414 if(theEvents.size() > nextUploadQty && !logUploadInProgress){
rt300@27 415 //try to upload asynchronously
rt300@27 416 uploadEventLog(true);
rt300@7 417 }
rt300@9 418 //sessionTime = (ofGetSystemTime() - sessionStartTime);
rt300@28 419 totalInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime); // milliseconds
rt300@28 420
rt300@1 421 }
rt300@8 422
rt300@8 423 //---------------------------------------------------------------------------
rt300@7 424
rt300@7 425 void EventLogger::exitAndSave(){
rt300@25 426
rt300@25 427 if(!consentGiven){
rt300@25 428 logEvent(CONSENT_DENIED);
rt300@25 429 Json::Value jlogs = logsToJson();
rt300@25 430 // try to upload TODO (no - might hang and prevent exit???)
rt300@27 431 uploadEventLog(true);
rt300@25 432 return;
rt300@25 433 }
rt300@25 434 logEvent(APP_EXITED);
rt300@25 435 savedInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime);
rt300@7 436 // save user details
rt300@8 437 string fname = ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME;
rt300@8 438
rt300@25 439 // try to upload TODO (no - might hang and prevent exit???)
rt300@27 440 // do it async because event list needs to be cleared to prevent saving on device
rt300@27 441 uploadEventLog(false);
rt300@7 442
rt300@8 443 // write to file
rt300@25 444 // json without the logs that were uploaded!
rt300@25 445 Json::Value jlogs = logsToJson();
rt300@8 446 ofFile logFile(fname,ofFile::WriteOnly);
rt300@8 447 logFile << jlogs;
rt300@8 448
rt300@8 449 }
rt300@8 450 //---------------------------------------------------------------------------
rt300@8 451
rt300@8 452 Json::Value EventLogger::logsToJson(){
rt300@8 453 // put all logged events into Json formatted string
rt300@8 454 Json::Value root;
rt300@8 455
rt300@8 456 vector<lEvent>::iterator eventIter;
rt300@8 457
rt300@22 458 root["programVersion"] = PROGRAM_VERSION;
rt300@8 459 root["userName"] = userName;
rt300@8 460 root["deviceID"] = deviceID;
rt300@25 461 root["uploadNumber"] = nextUploadNumber;
rt300@22 462 root["iOSdeviceType"] = iOSdeviceType;
rt300@25 463 root["savedInteractionTime"] = savedInteractionTime;
rt300@22 464 root["questionnaireCompleted"] = questionnaireCompleted;
rt300@22 465 root["questionnaireUploaded"] = questionnaireUploaded;
rt300@22 466 if(questionnaireCompleted && !questionnaireUploaded){
rt300@28 467 root["questionnaire"] = questionnaireToJson();
rt300@22 468 }
rt300@8 469
rt300@8 470 int i = 0;
rt300@8 471 for(eventIter = theEvents.begin(); eventIter < theEvents.end(); eventIter++){
rt300@8 472 root["events"][i] = (*eventIter).eventToJson();
rt300@8 473 i++;
rt300@7 474 }
rt300@25 475 root["numEventsHere"] = i;
rt300@8 476 return root;
rt300@8 477 }
rt300@22 478
rt300@8 479 //---------------------------------------------------------------------------
rt300@22 480
rt300@22 481 Json::Value EventLogger::questionnaireToJson(){
rt300@22 482 // put all answers into Json formatted string
rt300@22 483 Json::Value root;
rt300@22 484
rt300@22 485 vector<int>::iterator aIter;
rt300@22 486
rt300@22 487 Json::Value questionnaire;
rt300@22 488
rt300@22 489 int i = 0;
rt300@22 490 for(aIter = questionnaireAnswers.begin(); aIter < questionnaireAnswers.end(); aIter++){
rt300@22 491 questionnaire[i] = (*aIter);
rt300@22 492 i++;
rt300@22 493 }
rt300@28 494 cout << questionnaireComments << "\n";
rt300@22 495
rt300@22 496 root["qAnswers"] = questionnaire;
rt300@28 497 root["comments"] = questionnaireComments;
rt300@22 498 root["userName"] = userName;
rt300@22 499 root["deviceID"] = deviceID;
rt300@22 500 root["iOSdeviceType"] = iOSdeviceType;
rt300@22 501 root["programVersion"] = PROGRAM_VERSION;
rt300@22 502
rt300@22 503 return root;
rt300@22 504 }
rt300@8 505 //---------------------------------------------------------------------------
rt300@8 506 //---------------------------------------------------------------------------
rt300@22 507 //---------------------------------------------------------------------------