private CollectorRecord CreateRecordList(string macAddress, string websiteName, int sessionCount, int recordCount) { var now = DateTime.Now; var record = new CollectorRecord() { DeviceId = macAddress, DeviceName = macAddress, UserId = macAddress, RecordList = new List <SiteLookupRecord>() }; int sessionBreak = recordCount / sessionCount; int runningSessionCount = 1; for (int i = 1; i <= recordCount; i++) { record.RecordList.Add(new SiteLookupRecord() { WebsiteName = websiteName, Timestamp = now.ToString("s"), Duration = 0 }); if (i % sessionBreak == 0 && runningSessionCount < sessionCount) { now += TimeSpan.FromMinutes(10.0); runningSessionCount++; } else { now += TimeSpan.FromMinutes(2.0); } } return(record); }
const int SessionThreshold = 300; // sessions elapse after 5 minutes public static List <WebSession> ProcessRecords(SiteMapRepository siteMapRepository, List <WebSession> dbSessions, CollectorRecord collectorRecord) { // working sessions list var sessions = new List <WebSession>(); // set of WebSession references that have been modified or added var resultSessions = new List <WebSession>(); var recordList = collectorRecord.RecordList.OrderBy(r => r.Timestamp).ToList(); // terminate all sessions that aren't "in progress" var firstRecord = recordList.First(); var timestamp = Convert.ToDateTime(firstRecord.Timestamp); if (dbSessions != null && dbSessions.Count() > 0) { foreach (var s in dbSessions) { var sessionStart = Convert.ToDateTime(s.Start); if (s.InProgress == true && timestamp > sessionStart + TimeSpan.FromSeconds(SessionThreshold)) { // session is no longer in progress - mark it for update s.InProgress = false; resultSessions.Add(s); } else { // add it to the working session list sessions.Add(s); } } } foreach (var record in recordList) { // get the sitemap for this site and skip if the site is suppressed var siteMap = siteMapRepository.GetSiteMapping(record.WebsiteName); if (siteMap == null) { continue; } // find an in-progress session var session = sessions.LastOrDefault(s => s.Device.DeviceId == collectorRecord.DeviceId && s.Site == siteMap.Site && s.InProgress == true); if (session == null) { var newSession = new WebSession() { UserId = collectorRecord.UserId, DeviceId = collectorRecord.DeviceId, Device = new Device() { DeviceId = collectorRecord.DeviceId, Hostname = collectorRecord.DeviceName, UserId = collectorRecord.UserId }, Site = siteMap.Site, Category = siteMap.Category, Start = record.Timestamp, Duration = record.Duration, InProgress = true }; sessions.Add(newSession); resultSessions.Add(newSession); } else { var recordTimestamp = Convert.ToDateTime(record.Timestamp); var sessionStart = Convert.ToDateTime(session.Start); var sessionCurrent = sessionStart + TimeSpan.FromSeconds(session.Duration); // is this a new session? if (recordTimestamp > sessionCurrent + TimeSpan.FromSeconds(SessionThreshold)) { // terminate the current session and add it to the result session list session.InProgress = false; if (!resultSessions.Contains(session)) { resultSessions.Add(session); } // create a new session var newSession = new WebSession() { UserId = collectorRecord.UserId, DeviceId = collectorRecord.DeviceId, Device = new Device() { DeviceId = collectorRecord.DeviceId, Hostname = collectorRecord.DeviceName, UserId = collectorRecord.UserId }, Site = siteMap.Site, Category = siteMap.Category, Start = record.Timestamp, Duration = record.Duration, InProgress = true }; sessions.Add(newSession); resultSessions.Add(newSession); } else { // extend the session int duration = 0; if (recordTimestamp < sessionStart) { // handle the case where the current record somehow predates the existing session session.Start = record.Timestamp; sessionStart = recordTimestamp; duration = session.Duration > record.Duration ? session.Duration : record.Duration; } else { // normal processing - just calculate the delta between the new record timestamp and the session start, and add the duration // also ensure that the resulting duration isn't smaller than the existing session duration var delta = (int)(recordTimestamp - sessionStart).TotalSeconds + record.Duration; duration = delta > session.Duration ? delta : session.Duration; } session.Duration = duration; // add it to the result session list if not there already if (!resultSessions.Contains(session)) { resultSessions.Add(session); } } } } return(resultSessions); }
// POST colapi/collector public ServiceResponse Post(HttpRequestMessage req, JToken value) { //BUGBUG - need to do something about CSRF try { var controlMessage = ControlMessage.Normal; var obj = value as JObject; var record = new CollectorRecord() { DeviceId = (string)obj[CollectorFields.DeviceId], DeviceName = (string)obj[CollectorFields.DeviceName], UserId = CollectorRepository.UserId, State = RecordState.New, RecordList = new List <SiteLookupRecord>() }; // reject messages from a null device ID or UserID if (string.IsNullOrEmpty(record.DeviceId) || string.IsNullOrEmpty(record.UserId)) { TraceLog.TraceError(string.Format("Rejecting invalid record: {0}", obj.ToString())); return(new ServiceResponse() { ControlMessage = controlMessage, RecordsProcessed = -1 }); } // store device software version and timestamp (if this isn't a new device) var device = UserDataRepository.Devices.FirstOrDefault(d => d.DeviceId == record.DeviceId); if (device != null) { // this device is marked for deletion if (device.Enabled == null) { TraceLog.TraceInfo(string.Format("Deleting suspended device {0} for user {1}", device.Name, device.UserId)); UserDataRepository.DeleteDevice(device); return(new ServiceResponse() { ControlMessage = ControlMessage.DisableDevice, RecordsProcessed = 0 }); } // tell the client to suspend collection if the device was disabled by the user if (!device.Enabled.Value) { controlMessage = ControlMessage.SuspendCollection; } if (obj[CollectorFields.SoftwareVersion] != null) { device.SoftwareVersion = (string)obj[CollectorFields.SoftwareVersion]; } device.Timestamp = DateTime.UtcNow; UserDataRepository.SaveChanges(); } var array = obj[CollectorFields.Records] as JArray; if (array != null && array.Count > 0) { // extract each object out of the array and create a SiteLookupRecord for each foreach (JObject r in array) { int duration = r["Duration"] != null ? (int)r["Duration"] : 0; record.RecordList.Add(new SiteLookupRecord() { WebsiteName = (string)r["WebsiteName"], Timestamp = ((DateTime)r["Timestamp"]).ToString("s"), Duration = duration, }); } // add all records at once CollectorRepository.AddRecord(record); TraceLog.TraceInfo(string.Format("Added {0} records for user {1}", record.RecordList.Count, CollectorRepository.UserId)); } var response = new ServiceResponse() { RecordsProcessed = array != null ? array.Count : 0, ControlMessage = controlMessage }; return(response); } catch (Exception ex) { TraceLog.TraceException("Collector Post failed", ex); throw; } }