rt300@0: // rt300@0: // eventLogger.mm rt300@0: // oscSenderExample rt300@0: // rt300@0: // Created by Robert Tubb on 05/11/2012. rt300@0: // rt300@0: // rt300@4: rt300@8: //--------------------------------------------------------------------------- rt300@0: #include "eventLogger.h" rt300@33: #include "grid.h" rt300@33: rt300@33: extern Grid theGridView; rt300@1: rt300@1: EventLogger eventLogger; rt300@32: extern PresetManager presetManager; rt300@33: //--------------------------------------------------------------------------- rt300@33: void lEvent::draw(){ rt300@35: rt300@33: double size = 2.0; rt300@33: TwoVector pos = theGridView.coordToPixel(TwoVector(val1,val2)); // euch -rely on grid view? rt300@33: rt300@33: if(eventType == SCROLL){ rt300@33: ofSetColor(255,123,56); rt300@33: ofRect(pos.x, pos.y,size,size); rt300@33: }else if(eventType == EVALUATION_POINT){ rt300@33: size = sliderID/150.0; // slider id is the hover time. sorry sorry. rt300@33: if(size > 40) size = 40; rt300@33: ofSetColor(25,123,216); rt300@33: ofNoFill(); rt300@33: ofEllipse(pos.x, pos.y, size, size); rt300@33: rt300@33: } rt300@33: } rt300@29: rt300@8: //--------------------------------------------------------------------------- rt300@1: EventLogger::EventLogger(){ rt300@8: rt300@31: consentGiven = true; // unless told otherwise firstAppOpen rt300@8: loggingEnabled = true; rt300@22: serverConnectionOK = false; rt300@22: questionnaireCompleted = false; rt300@22: questionnaireUploaded = false; rt300@27: logUploadInProgress = false; rt300@22: ofxiPhoneDeviceType iOSdeviceType = ofxiPhoneGetDeviceType(); rt300@22: cout << "Device: " << iOSdeviceType << '\n'; rt300@22: rt300@25: nextUploadQty = UPLOAD_CHUNK_SIZE; // amount of data uploaded is always more than UPLOAD_CHUNK_SIZE events rt300@42: serverComms = [[ServerComms alloc] init]; rt300@27: rt300@8: } rt300@42: //--------------------------------------------------------------------------- rt300@42: void EventLogger::startLoadAll(){ rt300@4: rt300@42: loadExistingLogFile(ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME); rt300@42: rt300@42: rt300@8: sessionStartTime = ofGetSystemTime(); rt300@42: testConnection(); rt300@9: rt300@42: // if we have a back log of events upload them rt300@42: if(theEvents.size() > nextUploadQty && ! logUploadInProgress){ rt300@42: //try to upload rt300@42: uploadEventLog(true); rt300@42: } rt300@42: rt300@42: if(questionnaireCompleted && !questionnaireUploaded){ rt300@42: uploadQuestionnaire(); rt300@42: } rt300@42: rt300@42: //TODO if questionnaire wasn't reached but still opened then set the timer to something sensible rt300@42: rt300@42: //timer.setInteractionTime(savedInteractionTime); rt300@42: //timer.setOrderFromPrevious(interfaceOrder); rt300@42: rt300@42: // for now sod it, start from scratch rt300@42: if(!questionnaireCompleted){ rt300@42: cout<<"Questionnaire NOT completed - first APP open called\n"; rt300@42: firstEverAppOpen(); rt300@42: } rt300@7: rt300@31: } rt300@31: //--------------------------------------------------------------------------- rt300@38: void EventLogger::testConnection(){ rt300@31: Json::Value root; rt300@31: root["x"] = "y"; rt300@31: cout << "testConnection\n"; rt300@31: sendToServer("testConnection", root, true); rt300@31: rt300@30: } rt300@30: //--------------------------------------------------------------------------- rt300@30: // this reads the persistent log file , checks if we've used the app before and rt300@30: // if we've answered questionnaire or not rt300@39: rt300@42: // should just store stuff. the start up state should be handled by testAPp rt300@42: rt300@42: void EventLogger::loadExistingLogFile(const string &jsonFile){ rt300@30: Json::Value root; rt300@30: Json::Reader reader; rt300@30: rt300@30: ///////////// rt300@30: // read file rt300@30: rt300@30: ifstream theFile(jsonFile.c_str()); rt300@30: stringstream fileText; rt300@30: string line; rt300@30: if(!theFile){ rt300@42: cout<<"No event log file found - first APP open called\n"; rt300@30: firstEverAppOpen(); rt300@30: return; rt300@30: }else{ rt300@30: while(theFile){ rt300@30: theFile >> line; rt300@30: // cout << line; // lots!!!! rt300@30: fileText << line; rt300@30: } rt300@30: theFile.close(); rt300@30: } rt300@42: rt300@30: cout << "size of log JSON string:" << fileText.str().length() << "BYTES \n"; rt300@30: rt300@30: bool parsingSuccessful = reader.parse( fileText.str(), root ); rt300@30: rt300@30: if ( !parsingSuccessful ) rt300@30: { rt300@30: // report to the user the failure and their locations in the document. rt300@30: std::cout << "Failed to parse event log JSON: \n" rt300@30: << reader.getFormattedErrorMessages(); rt300@30: return; rt300@30: } rt300@30: rt300@30: ///////////////// rt300@30: // now put user deets into variables rt300@30: rt300@30: userName = root["userName"].asString(); rt300@30: deviceID = root["deviceID"].asLargestInt(); rt300@30: nextUploadNumber = root["uploadNumber"].asInt(); rt300@30: savedInteractionTime = root["savedInteractionTime"].asLargestInt(); rt300@30: questionnaireCompleted = root["questionnaireCompleted"].asBool(); rt300@30: questionnaireUploaded = root["questionnaireUploaded"].asBool(); rt300@30: interfaceOrder = root["interfaceOrder"].asInt(); rt300@30: rt300@29: rt300@30: // check for unuploaded evts rt300@30: const Json::Value jlogs = root["events"]; rt300@29: rt300@30: for ( int index = 0; index < jlogs.size(); ++index ) theEvents.push_back(lEvent(jlogs[index])); rt300@42: rt300@30: rt300@30: ////////////// rt300@30: rt300@30: if(questionnaireCompleted && !questionnaireUploaded){ rt300@30: // then read it in and upload it rt300@30: Json::Value JQ = root["questionnaire"]; rt300@30: Json::Value JArray = JQ["qAnswers"]; rt300@30: if(JArray.size() < 2){ rt300@30: cout << "Error - status of questionnaire is wierd\n"; rt300@30: } rt300@30: for ( unsigned int i = 0; i < JArray.size(); i++ ) rt300@30: { rt300@30: questionnaireAnswers.push_back(JArray[i].asInt()); rt300@30: } rt300@30: questionnaireComments = JQ["comments"].toStyledString(); rt300@42: //uploadQuestionnaire(); rt300@30: } rt300@42: rt300@30: cout << "Total interaction time: " << savedInteractionTime << '\n'; rt300@42: rt300@8: } rt300@29: rt300@8: //--------------------------------------------------------------------------- rt300@30: rt300@30: void EventLogger::firstEverAppOpen(){ rt300@42: rt300@30: nextUploadNumber = 0; rt300@30: deviceID = ofGetSystemTimeMicros(); rt300@30: savedInteractionTime = 0; rt300@30: questionnaireCompleted = false; rt300@30: questionnaireUploaded = false; rt300@30: rt300@30: } rt300@30: rt300@30: rt300@30: //--------------------------------------------------------------------------- rt300@28: void EventLogger::questionnaireAnswersObtained(vector answers, const char* userComments){ rt300@22: rt300@22: questionnaireCompleted = true; rt300@22: questionnaireAnswers = answers; rt300@28: questionnaireComments = userComments; rt300@22: uploadQuestionnaire(); rt300@31: logEvent(QUESTIONNAIRE_COMPLETED); rt300@22: rt300@22: } rt300@22: //--------------------------------------------------------------------------- rt300@27: void EventLogger::uploadQuestionnaire(){ rt300@22: // show indicator rt300@22: cout << "^^^^^^^^ UPLOADING QUESTIONNAIRE ^^^^^^^^ \n"; rt300@31: cout << questionnaireToJson() << "\n"; rt300@27: sendToServer("questionnaire", questionnaireToJson(), true); rt300@27: rt300@22: } rt300@22: //--------------------------------------------------------------------------- rt300@27: bool EventLogger::sendToServer(string functionName, Json::Value jsonData, bool async = false){ rt300@22: rt300@22: Json::FastWriter writer; rt300@22: string jsontext = writer.write( jsonData ); rt300@22: rt300@27: // remove newline rt300@22: if (!jsontext.empty() && jsontext[jsontext.length()-1] == '\n') { rt300@22: jsontext.erase(jsontext.length()-1); rt300@22: } rt300@30: ostringstream jd; rt300@30: jd << jsontext; rt300@30: NSString *theData = [NSString stringWithUTF8String:jd.str().c_str()]; rt300@30: NSString *theType = [NSString stringWithUTF8String:functionName.c_str()]; rt300@22: rt300@30: if(async){ rt300@30: [serverComms doPostRequest:theType withData:theData]; rt300@30: }else{ rt300@30: bool success = [serverComms doSyncPostRequest:theType withData:theData]; rt300@30: return success; rt300@30: } rt300@29: rt300@27: } rt300@27: //----------------------------- rt300@30: void EventLogger::questionnaireOK(){ rt300@30: questionnaireUploaded = true; rt300@36: questionnaireComments = ""; rt300@30: } rt300@30: //----------------------------- rt300@30: void EventLogger::eventlogOK(){ rt300@33: // COMMENT THIS IF UPLAODING FROM IPAD TO XCODE rt300@40: rt300@40: // it's a bad idea to do this in another thread... rt300@32: theEvents.clear(); rt300@30: cout << "EVENT LOG UPLOAD SUCCESS\n"; rt300@30: nextUploadNumber++; rt300@30: logUploadInProgress = false; rt300@30: } rt300@30: //----------------------------- rt300@30: void EventLogger::testConnectionOK(){ rt300@30: cout << "^^^^^^^^ server connection OK ^^^^^^^^ \n"; rt300@30: serverConnectionOK = true; rt300@30: } rt300@30: //----------------------------- rt300@30: void EventLogger::questionnaireNotOK(){ rt300@32: cout << "XXXXX questionnaire NOT OK XXXXXXX \n"; rt300@30: questionnaireUploaded = false; rt300@30: } rt300@30: //----------------------------- rt300@30: void EventLogger::eventlogNotOK(){ rt300@30: // try later rt300@32: cout << "XXXXX event log NOT OK XXXXXXX \n"; rt300@30: nextUploadQty += UPLOAD_CHUNK_SIZE; rt300@30: logUploadInProgress = false; rt300@30: } rt300@30: //----------------------------- rt300@30: void EventLogger::testConnectionNotOK(){ rt300@32: cout << "XXXXX server connection NOT OK XXXXXXX \n"; rt300@30: serverConnectionOK = false; rt300@30: // alert? rt300@30: rt300@30: } rt300@27: rt300@27: rt300@9: //--------------------------------------------------------------------------- rt300@9: rt300@27: bool EventLogger::uploadEventLog(bool async){ rt300@38: rt300@22: // show indicator rt300@27: logUploadInProgress = true; rt300@25: cout << "^^^^^^^^ ATTEMPTING TO UPLOAD " << theEvents.size() << " EVENTS ^^^^^^^^ .\n"; rt300@27: if(!async){ rt300@27: bool success = sendToServer("eventlog", logsToJson(), async); rt300@27: if(!success){ rt300@29: // try later : NOPE has maximum size rt300@27: nextUploadQty += UPLOAD_CHUNK_SIZE; rt300@27: }else{ rt300@27: rt300@27: // if success - clear memory rt300@31: // IF UPLAODING FROM IPAD TO XCODE COMMENT OUT rt300@32: theEvents.clear(); rt300@27: cout << "UPLOAD SUCCESS\n"; rt300@27: nextUploadNumber++; rt300@27: } rt300@27: logUploadInProgress = false; rt300@27: return success; rt300@9: }else{ rt300@27: sendToServer("eventlog", logsToJson(), async); rt300@9: } rt300@1: } rt300@8: //---------------------------------------------------------------------------- rt300@38: bool eventWasInRegion(deque::iterator eiter, TwoVector regionTopLeft, TwoVector regionBottomRight){ rt300@33: if( ((*eiter).val1 > regionTopLeft.x ) && ((*eiter).val2 > regionTopLeft.y ) && ((*eiter).val1 < regionBottomRight.x ) && ((*eiter).val2 < regionBottomRight.y )){ rt300@33: return true; rt300@33: }else{ rt300@33: return false; rt300@33: } rt300@33: rt300@33: } rt300@33: //---------------------------------------------------------------------------- rt300@38: void EventLogger::clearTrail(){ rt300@38: eventsToDraw.clear(); rt300@38: } rt300@38: //---------------------------------------------------------------------------- rt300@44: vector EventLogger::getEvaluationPointsInRange(const TwoVector min, const TwoVector max){ rt300@44: vector results; rt300@44: if(eventsToDraw.size() <= 0){ rt300@44: return results; rt300@44: rt300@44: } rt300@44: rt300@44: deque::iterator eiter; rt300@44: deque::iterator preveiter; rt300@44: for(eiter = --eventsToDraw.end(); eiter >= eventsToDraw.begin(); eiter--){ rt300@44: if((*eiter).eventType == EVALUATION_POINT){ rt300@44: if( ((*eiter).val1 > min.x ) && ((*eiter).val2 > min.y ) && ((*eiter).val1 < max.x ) && ((*eiter).val2 < max.y )){ rt300@44: results.push_back(&(*eiter)); rt300@44: } rt300@44: } rt300@44: } rt300@44: return results; rt300@44: } rt300@44: //---------------------------------------------------------------------------- rt300@33: void EventLogger::drawTrail(const TwoVector min, const TwoVector max){ rt300@38: if(eventsToDraw.size() <= 0){ rt300@38: return; rt300@38: rt300@38: } rt300@38: deque::iterator eiter; rt300@38: deque::iterator preveiter; rt300@33: int i = 0; rt300@33: rt300@33: rt300@38: preveiter = --eventsToDraw.end(); rt300@38: TwoVector start = TwoVector(ofGetWidth()*0.5,ofGetHeight()*0.5); rt300@38: TwoVector end = theGridView.coordToPixel(TwoVector((*preveiter).val1,(*preveiter).val2)); rt300@38: ofSetColor(255,255,255,96); rt300@38: ofLine(start.x,start.y, end.x, end.y); rt300@33: rt300@38: for(eiter = --eventsToDraw.end(); eiter >= eventsToDraw.begin(); eiter--){ rt300@27: rt300@33: //cout << i << '\n'; rt300@44: if( (*eiter).eventType == SCROLL rt300@44: || (*eiter).eventType == EVALUATION_POINT rt300@44: || (*eiter).eventType == RANDOMISE rt300@44: || (*eiter).eventType == CHANGE_SLIDER) rt300@44: { rt300@33: i++; rt300@33: if(i > SCROLL_TRAIL_LENGTH){ rt300@33: return; rt300@33: } rt300@33: if(eventWasInRegion(eiter, min, max) || eventWasInRegion(preveiter, min, max)){ rt300@33: // draw a line between prev and this rt300@38: if(eiter != eventsToDraw.begin()){ rt300@33: TwoVector start = theGridView.coordToPixel(TwoVector((*preveiter).val1,(*preveiter).val2)); rt300@33: TwoVector end = theGridView.coordToPixel(TwoVector((*eiter).val1,(*eiter).val2)); rt300@33: ofSetColor(255,255,255,96); rt300@33: ofLine(start.x,start.y, end.x, end.y); rt300@33: rt300@33: } rt300@33: rt300@33: } rt300@33: preveiter = eiter; rt300@33: } rt300@33: if( (*eiter).eventType == EVALUATION_POINT){ rt300@33: if( ((*eiter).val1 > min.x ) && ((*eiter).val2 > min.y ) && ((*eiter).val1 < max.x ) && ((*eiter).val2 < max.y )){ rt300@33: // draw it rt300@33: (*eiter).draw(); rt300@33: } rt300@33: } rt300@33: } rt300@33: rt300@33: } rt300@8: rt300@28: //--------------------------------------------------------------------------- rt300@28: // only called when doing supervised tests rt300@27: void EventLogger::newUser(){ rt300@32: // store old stuff rt300@32: rt300@32: saveSessionToFile(); rt300@32: presetManager.saveSessionToFile(userName); rt300@27: cout<<"setup new user\n"; rt300@29: deleteLogs(); rt300@27: nextUploadNumber = 0; rt300@27: deviceID = ofGetSystemTimeMicros(); rt300@27: savedInteractionTime = 0; rt300@28: totalInteractionTime = 0; rt300@28: sessionStartTime = ofGetSystemTime(); rt300@27: questionnaireCompleted = false; rt300@29: questionnaireUploaded = false; rt300@29: rt300@29: ((testApp *)ofGetAppPtr())->showIntro(); rt300@27: rt300@27: } rt300@8: //--------------------------------------------------------------------------- rt300@8: // called from alertView OK in iViewController rt300@8: void EventLogger::setUsername(const char *u){ rt300@8: userName = u; rt300@29: rt300@33: } rt300@33: //--------------------------------------------------------------------------- rt300@33: void EventLogger::thinnedScrollEvent(lEvent newEvent){ rt300@33: static lEvent previousEvent(EMPTY_EVENT); // initialised as whatever rt300@33: static int eventCounter = 0; rt300@33: rt300@33: // if first event then log it. it won't be, but still. rt300@33: if(theEvents.size() == 0){ rt300@33: theEvents.push_back(newEvent); rt300@33: previousEvent = newEvent; rt300@33: return; rt300@33: } rt300@41: rt300@33: // 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: int gap = newEvent.eventTime - previousEvent.eventTime; rt300@33: if(gap > 300){ rt300@39: // log previous event as a evaluation point rt300@38: lEvent evalEvt(EVALUATION_POINT, previousEvent.val1, previousEvent.val2, gap); rt300@38: theEvents.push_back(evalEvt); rt300@38: eventsToDraw.push_back(evalEvt); rt300@38: if(eventsToDraw.size() > SCROLL_TRAIL_LENGTH){ rt300@38: eventsToDraw.pop_front(); rt300@38: } rt300@33: // and now new event as scroll rt300@33: theEvents.push_back(newEvent); rt300@38: eventsToDraw.push_back(newEvent); rt300@38: if(eventsToDraw.size() > SCROLL_TRAIL_LENGTH){ rt300@38: eventsToDraw.pop_front(); rt300@38: } rt300@33: eventCounter = 0; rt300@33: rt300@33: }else if(eventCounter >= EVENT_THIN_FACTOR){ // otherwise only record every Nth event rt300@33: theEvents.push_back(newEvent); rt300@38: eventsToDraw.push_back(newEvent); rt300@38: if(eventsToDraw.size() > SCROLL_TRAIL_LENGTH){ rt300@38: eventsToDraw.pop_front(); rt300@38: } rt300@33: eventCounter = 0; rt300@33: rt300@33: } rt300@33: eventCounter++; rt300@33: previousEvent = newEvent; rt300@33: rt300@39: }//--------------------------------------------------------------------------- rt300@39: void EventLogger::thinnedSliderEvent(lEvent newEvent){ rt300@33: static lEvent previousEvent(EMPTY_EVENT); // initialised as whatever. hopefully won't log rt300@28: static int eventCounter = 0; rt300@28: rt300@41: // if first event then log it. rt300@28: if(theEvents.size() == 0){ rt300@28: theEvents.push_back(newEvent); rt300@28: previousEvent = newEvent; rt300@28: return; rt300@28: } rt300@41: rt300@39: // if previous event is more than 300ms ago log both of them rt300@28: int gap = newEvent.eventTime - previousEvent.eventTime; rt300@39: if(gap > 300){ rt300@39: // we need the PREV grid coord though, which is lost!! rt300@39: TwoVector c = theGridView.getCoord(); rt300@39: lEvent evalEvt(EVALUATION_POINT, c.x, c.y, gap); rt300@39: theEvents.push_back(evalEvt); rt300@39: eventsToDraw.push_back(evalEvt); rt300@39: if(eventsToDraw.size() > SCROLL_TRAIL_LENGTH){ rt300@39: eventsToDraw.pop_front(); rt300@39: } rt300@39: // if prev slider event not logged, log it rt300@39: if(theEvents.back().eventTime != previousEvent.eventTime){ rt300@39: theEvents.push_back(previousEvent); rt300@39: } rt300@39: rt300@39: // also log new slider evt rt300@39: theEvents.push_back(newEvent); rt300@39: eventCounter = 0; rt300@39: rt300@39: }else if(eventCounter >= EVENT_THIN_FACTOR){ // otherwise only record every Nth event rt300@39: theEvents.push_back(newEvent); rt300@39: eventCounter = 0; rt300@39: rt300@39: } rt300@39: eventCounter++; rt300@39: previousEvent = newEvent; rt300@39: } rt300@39: //--------------------------------------------------------------------------- rt300@39: void EventLogger::thinnedZoomEvent(lEvent newEvent){ rt300@39: static lEvent previousEvent(EMPTY_EVENT); // initialised as whatever. hopefully won't log rt300@39: static int eventCounter = 0; rt300@39: rt300@39: // if first event then log it. it won't be, but still. rt300@39: if(theEvents.size() == 0){ rt300@39: theEvents.push_back(newEvent); rt300@39: previousEvent = newEvent; rt300@39: return; rt300@39: } rt300@39: rt300@39: int gap = newEvent.eventTime - previousEvent.eventTime; rt300@39: if(gap > 400){ rt300@28: // if prev event not logged, log it rt300@31: if(theEvents.back().eventTime != previousEvent.eventTime){ rt300@28: theEvents.push_back(previousEvent); rt300@28: } rt300@28: theEvents.push_back(newEvent); rt300@39: eventCounter = 0; rt300@7: rt300@39: }else if(eventCounter >= EVENT_THIN_FACTOR){ rt300@28: theEvents.push_back(newEvent); rt300@28: eventCounter = 0; rt300@28: rt300@28: } rt300@28: eventCounter++; rt300@28: previousEvent = newEvent; rt300@7: } rt300@39: //----------------------------------------------------------------------------- rt300@39: void EventLogger::thinnedSnapEvent(lEvent nextEvent){ rt300@39: static lEvent previousEvent(EMPTY_EVENT); rt300@39: if(nextEvent.val1 != previousEvent.val1 || nextEvent.val2 != previousEvent.val2){ rt300@39: theEvents.push_back(nextEvent); rt300@39: cout << "Snapped EVENT\n"; rt300@39: } rt300@39: previousEvent = nextEvent; rt300@39: } rt300@8: //--------------------------------------------------------------------------- rt300@5: void EventLogger::logEvent(const leventType& evtType,const TwoVector& centre, const double& scale, const int& sliderID, const double& sliderVal){ rt300@33: rt300@1: rt300@1: // scroll has 2 double coords rt300@1: // zoom has 1 double scale rt300@1: // save preset has 2 coords rt300@1: // switch view has view type rt300@9: // slider change has int slider index and 1 float value rt300@1: rt300@4: // get time for key index rt300@4: rt300@5: // thinFactor rt300@5: if(!loggingEnabled) return; rt300@4: switch ( evtType ) { rt300@28: // data thinning here rt300@28: case SCROLL: rt300@33: thinnedScrollEvent(lEvent(evtType,centre.x,centre.y)); rt300@33: //theEvents.push_back(lEvent(evtType,centre.x,centre.y)); rt300@28: break; rt300@28: case ZOOM: rt300@39: thinnedZoomEvent(lEvent(evtType,scale)); rt300@28: break; rt300@28: case CHANGE_SLIDER: rt300@39: thinnedSliderEvent(lEvent(evtType,sliderVal , 0.0 , sliderID)); rt300@39: break; rt300@39: case SNAPPED_TO_PRESET: rt300@39: thinnedSnapEvent(lEvent(evtType,centre.x,centre.y,sliderID)); rt300@39: rt300@28: break; rt300@29: // non thinned data rt300@4: case SAVE_PRESET: rt300@5: theEvents.push_back(lEvent(evtType,centre.x,centre.y)); rt300@4: // Code rt300@4: break; rt300@4: case SAVE_DESET: rt300@5: theEvents.push_back(lEvent(evtType,centre.x,centre.y)); rt300@4: break; rt300@28: rt300@5: case SCROLL_STOPPED: rt300@5: theEvents.push_back(lEvent(evtType,centre.x,centre.y)); rt300@22: break; rt300@39: rt300@25: case SWAP_VIEW: rt300@25: theEvents.push_back(lEvent(evtType,0.0 , 0.0 , sliderID)); // slider ID is which view rt300@25: break; rt300@44: // these cases are move events that need to be put in events to draw rt300@44: case RANDOMISE: rt300@44: theEvents.push_back(lEvent(evtType,centre.x,centre.y)); rt300@44: eventsToDraw.push_back(lEvent(evtType,centre.x,centre.y)); rt300@44: if(eventsToDraw.size() > SCROLL_TRAIL_LENGTH){ rt300@44: eventsToDraw.pop_front(); rt300@44: } rt300@44: break; rt300@4: default: rt300@25: // default is just an event type with no values rt300@25: theEvents.push_back(lEvent(evtType)); rt300@4: break; rt300@1: } rt300@27: if(theEvents.size() > nextUploadQty && !logUploadInProgress){ rt300@27: //try to upload asynchronously rt300@27: uploadEventLog(true); rt300@7: } rt300@9: //sessionTime = (ofGetSystemTime() - sessionStartTime); rt300@28: totalInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime); // milliseconds rt300@28: rt300@1: } rt300@29: //-------------------------------------------------------------------- rt300@29: // called from newUser rt300@29: void EventLogger::deleteLogs(){ rt300@29: // the rt300@29: theEvents.clear(); rt300@29: string fname = ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME; rt300@29: ofFile logFile(fname,ofFile::WriteOnly); rt300@29: logFile << ""; rt300@29: logFile.close(); rt300@29: } rt300@8: //--------------------------------------------------------------------------- rt300@7: rt300@7: void EventLogger::exitAndSave(){ rt300@25: rt300@25: if(!consentGiven){ rt300@25: logEvent(CONSENT_DENIED); rt300@25: Json::Value jlogs = logsToJson(); rt300@25: // try to upload TODO (no - might hang and prevent exit???) rt300@27: uploadEventLog(true); rt300@25: return; rt300@25: } rt300@25: logEvent(APP_EXITED); rt300@25: savedInteractionTime = savedInteractionTime + (ofGetSystemTime() - sessionStartTime); rt300@7: // save user details rt300@8: string fname = ofxiPhoneGetDocumentsDirectory() + EVENT_LOG_FILENAME; rt300@8: rt300@31: // try to upload rt300@31: // do it sync because event list needs to be cleared to prevent saving on device rt300@27: uploadEventLog(false); rt300@7: rt300@8: // write to file rt300@25: // json without the logs that were uploaded! rt300@25: Json::Value jlogs = logsToJson(); rt300@8: ofFile logFile(fname,ofFile::WriteOnly); rt300@8: logFile << jlogs; rt300@29: logFile.close(); rt300@8: rt300@8: } rt300@8: //--------------------------------------------------------------------------- rt300@8: rt300@8: Json::Value EventLogger::logsToJson(){ rt300@8: // put all logged events into Json formatted string rt300@8: Json::Value root; rt300@8: rt300@8: vector::iterator eventIter; rt300@8: rt300@22: root["programVersion"] = PROGRAM_VERSION; rt300@8: root["userName"] = userName; rt300@8: root["deviceID"] = deviceID; rt300@25: root["uploadNumber"] = nextUploadNumber; rt300@22: root["iOSdeviceType"] = iOSdeviceType; rt300@25: root["savedInteractionTime"] = savedInteractionTime; rt300@22: root["questionnaireCompleted"] = questionnaireCompleted; rt300@22: root["questionnaireUploaded"] = questionnaireUploaded; rt300@35: if(!questionnaireUploaded) root["questionnaire"] = questionnaireToJson(); rt300@35: rt300@8: rt300@8: int i = 0; rt300@8: for(eventIter = theEvents.begin(); eventIter < theEvents.end(); eventIter++){ rt300@8: root["events"][i] = (*eventIter).eventToJson(); rt300@8: i++; rt300@7: } rt300@25: root["numEventsHere"] = i; rt300@8: return root; rt300@8: } rt300@22: rt300@8: //--------------------------------------------------------------------------- rt300@22: rt300@22: Json::Value EventLogger::questionnaireToJson(){ rt300@22: // put all answers into Json formatted string rt300@22: Json::Value root; rt300@22: rt300@22: vector::iterator aIter; rt300@22: rt300@22: Json::Value questionnaire; rt300@22: rt300@22: int i = 0; rt300@22: for(aIter = questionnaireAnswers.begin(); aIter < questionnaireAnswers.end(); aIter++){ rt300@22: questionnaire[i] = (*aIter); rt300@22: i++; rt300@22: } rt300@22: rt300@22: root["qAnswers"] = questionnaire; rt300@28: root["comments"] = questionnaireComments; rt300@22: root["userName"] = userName; rt300@22: root["deviceID"] = deviceID; rt300@22: root["iOSdeviceType"] = iOSdeviceType; rt300@22: root["programVersion"] = PROGRAM_VERSION; rt300@22: rt300@22: return root; rt300@22: } rt300@30: rt300@8: //--------------------------------------------------------------------------- rt300@30: void EventLogger::printAll(){ rt300@30: cout << "-----------------ALL LOGGED EVENTS----------------- \n"; rt300@30: vector::iterator evIter; rt300@30: cout << logsToJson() << "\n"; rt300@30: cout << "---------------------QUESTIONNAIRE---------------- \n"; rt300@30: cout << questionnaireToJson() << "\n"; rt300@30: }; rt300@8: //--------------------------------------------------------------------------- rt300@32: rt300@32: void EventLogger::saveSessionToFile(){ rt300@32: string fname = ofxiPhoneGetDocumentsDirectory() + userName + '_' + EVENT_LOG_FILENAME; rt300@32: rt300@32: // write to file rt300@32: // json without the logs that were uploaded! rt300@32: Json::Value jlogs = logsToJson(); rt300@32: ofFile logFile(fname,ofFile::WriteOnly); rt300@32: logFile << jlogs; rt300@32: logFile.close(); rt300@32: rt300@32: } rt300@33: //---------------------------------------------------------------------------- rt300@33: // TODO this path thing rt300@33: vector EventLogger::getRecentPath(int numEvents){ rt300@33: vector thePath; rt300@33: rt300@33: TwoVector lastScrollPos; rt300@33: vector::iterator eventIndex; rt300@33: eventIndex = theEvents.end(); rt300@33: int numScrolls = 0; rt300@33: while(numScrolls < numEvents){ rt300@33: // go back check for scrolls, check for end of array etc. rt300@33: thePath.push_back(lastScrollPos); rt300@33: numScrolls++; rt300@33: } rt300@33: return thePath; rt300@33: }