using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Threading; using System.Web; using System.Web.Services; using CPE.App.Web.Code; using CPE.App.Web.Models; /* Tyler Allen - 08/22/2016 Change notes: I have commented out the section that was executing the thread within the application pool process The new method calls a console application with the session variables This will run outside the application pool */ namespace CPE.App.Web.Static.services.engagement { /// /// Summary description for engagement /// [WebService(Namespace = "http://cpeengagement.com/static/services/engagement/service/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ToolboxItem(false)] // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. // [System.Web.Script.Services.ScriptService] public class service : BaseWebService { private string notifyFilePath { get { return ConfigurationManager.AppSettings["NotifyFilePath"]; } } [WebMethod] public Setting GetMeetingSettings(int sco_id) { Extensions.LogServiceCall("[service][GetMeetingSettings]", string.Format("Parameters: sco_id = {0}", sco_id)); var result = new Setting { Interval = 15, Text = "Respond", Duration = 1 }; var meetingSetting = Database.MeetingSettings.SingleOrDefault(ms => ms.MeetingKey == sco_id); if(meetingSetting != null) { result.Interval = meetingSetting.Interval.HasValue ? meetingSetting.Interval.Value : 15; result.Text = meetingSetting.Text; result.Duration = meetingSetting.Duration.HasValue ? meetingSetting.Duration.Value : 1; } return result; } [WebMethod] public ServiceStatus SetMeetingSettings(int sco_id, int interval, string text, int duration) { Extensions.LogServiceCall("SetMeetingSettings", string.Format("Parameters: sco_id = {0} interval = {1} text = {2} duration = {3}", sco_id, interval, text, duration)); var result = new ServiceStatus { Success = true, Message = "" }; try { var context = Database; var existing = context.Meetings.SingleOrDefault(m => m.SCO_ID == sco_id); if(existing != null) { if(existing.MeetingSetting == null) { var meetingSetting = new MeetingSetting { MeetingKey = sco_id, Interval = interval, Text = text, Duration = duration }; context.MeetingSettings.InsertOnSubmit(meetingSetting); } else { existing.MeetingSetting.Interval = interval; existing.MeetingSetting.Text = text; existing.MeetingSetting.Duration = duration; } } else { var meeting = new Meeting { SCO_ID = sco_id, MeetingKey = sco_id }; var meetingSetting = new MeetingSetting(); meetingSetting.Interval = interval; meetingSetting.Text = text; meetingSetting.Duration = duration; meetingSetting.MeetingKey = sco_id; context.Meetings.InsertOnSubmit(meeting); context.MeetingSettings.InsertOnSubmit(meetingSetting); } context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("SetMeetingSettings", ex); result.Success = false; result.Message = ex.Message; } return result; } [WebMethod] public SessionStatusClass SessionStatus(int sco_id) { Extensions.LogServiceCall("[service][SessionStatus]", string.Format("Parameters: sco_id = {0}", sco_id)); var result = new SessionStatusClass { MeetingSessionKey = -1, OwnerPrincipalId = -1, Status = "none" }; try { var context = Database; var existingSession = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue) .OrderByDescending(ms => ms.StartDate) .FirstOrDefault(); if(existingSession != null) { result.MeetingSessionKey = existingSession.MeetingSessionKey; result.OwnerPrincipalId = existingSession.OwnerPrincipal_ID; var isPaused = existingSession.MeetingSessionPauses.Count(me => !me.EndDate.HasValue) > 0; if(existingSession.EndDate.HasValue) { result.Status = "complete"; } else if(!existingSession.EndDate.HasValue && existingSession.StartDate < DateTime.UtcNow && !isPaused) { result.Status = "active"; } else if(isPaused) { result.Status = "pause"; } } } catch (Exception ex) { Extensions.LogServiceError("SessionStatus", ex); result.OwnerPrincipalId = -1; result.Status = "error"; } return result; } [WebMethod] public int StartSession(int sco_id, string name, int owner_principal_id) { Extensions.LogServiceCall("StartSession", string.Format("Parameters: sco_id = {0} name= {1} owner_principal_id = {2}", sco_id, name, owner_principal_id)); var result = -1; try { var context = Database; var existingSession = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue) .OrderByDescending(ms => ms.StartDate) .FirstOrDefault(); if(existingSession == null) { //create new session var meeting = context.Meetings.SingleOrDefault(m => m.SCO_ID == sco_id); if(meeting == null) { meeting = new Meeting { SCO_ID = sco_id, MeetingKey = sco_id }; context.Meetings.InsertOnSubmit(meeting); var meetingSettings = new MeetingSetting { Interval = 15, MeetingKey = sco_id, Text = "Respond" }; context.MeetingSettings.InsertOnSubmit(meetingSettings); var meetingDetails = new MeetingDetail { MeetingKey = sco_id, CourseCode = "", Instructor = "", Location = "", TopicID = "", TopicName = "" }; context.MeetingDetails.InsertOnSubmit(meetingDetails); } var session = new MeetingSession { SCO_ID = sco_id, Name = name, StartDate = DateTime.UtcNow, //start OwnerPrincipal_ID = owner_principal_id }; context.MeetingSessions.InsertOnSubmit(session); context.SubmitChanges(); result = session.MeetingSessionKey; //check for multiple sessions var existingSessionCount = context.MeetingSessions.Count(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue); if(existingSessionCount > 1) { //there should only be one session var existingSessions = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue) .OrderBy(ms => ms.MeetingSessionKey); var sessionToKeep = existingSessions.First(); var sessionsToDelete = existingSessions.Where(ms => ms.MeetingSessionKey != sessionToKeep.MeetingSessionKey); foreach (var meetingSession in sessionsToDelete) { if(meetingSession.MeetingParticipantSessions.Any() || meetingSession.MeetingSessionHeartbeats.Any()) { //session has data, do not delete it } else { context.MeetingSessions.DeleteOnSubmit(meetingSession); } } try { context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("StartSession", ex); } } } else { //session already exists, this should not happen! //string existingSessionKeys = string.Join(",", context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue).Select(ms => ms.MeetingSessionKey).ToArray()); //throw new ArgumentException(String.Format("Trying to start a session which already exists. sco_id = {0}, meetingSessionKeys = {1}", sco_id, existingSessionKeys)); result = existingSession.MeetingSessionKey; } } catch (Exception ex) { Extensions.LogServiceError("StartSession", ex); result = -1; } return result; } private void startNotify(string args) { var startinfo = new ProcessStartInfo { FileName = $"{notifyFilePath}", CreateNoWindow = true, UseShellExecute = false, WindowStyle = ProcessWindowStyle.Hidden, Arguments = args }; var process = Process.Start(startinfo); process.WaitForExit(0); } [WebMethod] public int StopSession(int sessionKey, DateTime end) { Extensions.LogServiceCall("[service.asmx][StopSession]", string.Format("Parameters: sessionKey = {0} end = {1}", sessionKey, end.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; var session = Database.MeetingSessions.Single(ms => ms.MeetingSessionKey == sessionKey); try { session.EndDate = DateTime.UtcNow; //end var participants = Database.MeetingParticipantSessions.Where(m => m.MeetingSessionKey == session.MeetingSessionKey); foreach (var participantSession in participants) { foreach (var participantTracking in participantSession.ParticipantTrackings.Where(t => !t.EndDate.HasValue)) { participantTracking.EndDate = session.EndDate; } } adobe.GetAdobeTransactions(session, Database); // Tyler Allen - 08/27/2016 // This is redundant and will cause deadlocks //Database.SubmitChanges(); result = session.MeetingSessionKey; CleanupSessionEngagements(session.MeetingSessionKey); } catch (Exception ex) { Extensions.LogServiceError("[service.asmx][StopSession]", ex); result = -1; } /* This is the new modifications created by Tyler Allen [08/22/2016] */ // TODO: Depricated for new Notify application // Launch a non-blocking thread to wait for 30 minutes for adobe results then email certificates // var waitForResultsThread = new Thread(() => StopSessionResultsThread(session)); // waitForResultsThread.Start(); // -u = unattended // -s = stop session try { Extensions.LogServiceCall("[service.asmx][StopSession][CPE.App.Notify.exe]", $"Processing CPE.App.Notify.exe [{notifyFilePath} -u -s {session.MeetingSessionKey}]"); //Process.Start($"{notifyFilePath} -u -s {session.MeetingSessionKey}"); startNotify($"-u -s {session.MeetingSessionKey}"); Extensions.LogServiceCall("[service.asmx][StopSession][CPE.App.Notify.exe]", $"Started CPE.App.Notify.exe [{notifyFilePath} -u -s {session.MeetingSessionKey}]"); } catch (Exception exception) { Extensions.LogServiceError("[service.asmx][StopSession][CPE.App.Notify.exe]", exception); } /* This is the new modifications created by Tyler Allen [08/22/2016] */ return sessionKey; } [WebMethod] public int PauseSession(int sessionKey, DateTime start) { Extensions.LogServiceCall("PauseSession", string.Format("Parameters: sessionKey = {0} start= {1}", sessionKey, start.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; try { var context = Database; var sessionEvent = new MeetingSessionPause { MeetingSessionKey = sessionKey, StartDate = DateTime.UtcNow //start }; context.MeetingSessionPauses.InsertOnSubmit(sessionEvent); context.SubmitChanges(); result = sessionEvent.MesstingSessionPauseKey; } catch (Exception ex) { Extensions.LogServiceError("PauseSession", ex); result = -1; } return result; } [WebMethod] public int GetPauseKey(int sco_id) { Extensions.LogServiceCall("GetPauseKey", string.Format("Parameters: sco_id = {0}", sco_id)); var result = -1; try { var context = Database; var existingSession = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue) .OrderByDescending(ms => ms.StartDate) .FirstOrDefault(); if(existingSession != null) { var pause = existingSession.MeetingSessionPauses.Where(mse => !mse.EndDate.HasValue) .OrderByDescending( mse => mse.StartDate) .First(); result = pause.MesstingSessionPauseKey; } ; } catch (Exception ex) { Extensions.LogServiceError("GetPauseKey", ex); result = -1; } return result; } [WebMethod] public int ResumeSession(int eventKey, DateTime end) { Extensions.LogServiceCall("ResumeSession", string.Format("Parameters: eventKey = {0} end = {1}", eventKey, end.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; try { var context = Database; var sessionEvent = context.MeetingSessionPauses.Single(se => se.MesstingSessionPauseKey == eventKey); sessionEvent.EndDate = DateTime.UtcNow; //end; context.SubmitChanges(); result = sessionEvent.MesstingSessionPauseKey; } catch (Exception ex) { Extensions.LogServiceError("ResumeSession", ex); result = -1; } return result; } [WebMethod] public ServiceStatus LogEngagement(int meetingSessionKey) { Extensions.LogServiceCall("LogEngagement", string.Format("Parameters: meetingSessionKey = {0}", meetingSessionKey)); var result = new ServiceStatus { Success = true, Message = "" }; try { var context = Database; var record = new MeetingSessionHeartbeat { MeetingSessionKey = meetingSessionKey, Timestamp = DateTime.UtcNow }; context.MeetingSessionHeartbeats.InsertOnSubmit(record); context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("LogEngagement", ex); result.Success = false; result.Message = ex.Message; } return result; } [WebMethod] public ServiceStatus TrackUser(int meetingSessionKey, int principal_id, string firstname, string lastname, string email, byte type) { Extensions.LogServiceCall("TrackUser", string.Format("Parameters: meetingSessionKey = {0} principal_id= {1} firstname = {2}, lastname = {3} email = {4} type = {5} DateTimeUtc = {6}", meetingSessionKey, principal_id, firstname, lastname, email, type, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = new ServiceStatus { Success = true, Message = "" }; try { if(principal_id > 0) { var context = Database; //var meeting = context.Meetings.SingleOrDefault(m => m.SCO_ID == sco_id); //JM - 9/27/2012 changed session to add a check for session not ended //var session = // context.MeetingSessions.SingleOrDefault(ms => ms.MeetingSessionKey == meetingSessionKey && !ms.EndDate.HasValue); //JM - 11/20/2012 changed to allow for end dates existing for recordings var session = getActiveSessionByKey(meetingSessionKey); if(session != null) { var user = context.Participants.SingleOrDefault(p => p.Principal_ID == principal_id); if(user == null) { //create new user user = new Participant { Principal_ID = principal_id, FirstName = firstname, LastName = lastname, Email = email }; context.Participants.InsertOnSubmit(user); } //else //{ // user.FirstName = firstname; // user.LastName = lastname; // user.Email = email; //} //JM 9/27/2012 - changed to grab first participant session found. Weird issue where some users were getting multiple sesions var existingMeetingParticipantSession = session.MeetingParticipantSessions.OrderBy(mps => mps.MeetingParticipantSessionKey) .FirstOrDefault(mps => mps.ParticipantKey == principal_id); if(existingMeetingParticipantSession == null) { //create new session for user existingMeetingParticipantSession = new MeetingParticipantSession { MeetingSessionKey = session.MeetingSessionKey, ParticipantKey = principal_id, PlaySound = true, Created = DateTime.UtcNow }; context.MeetingParticipantSessions.InsertOnSubmit(existingMeetingParticipantSession); context.SubmitChanges(); //JM-12/27/2012 Change to check for multiple meeting participant sessions CheckParticipantSessions(session, principal_id); //make sure tracking gets assigned right key existingMeetingParticipantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principal_id); } var lastParticipantTracking = context.ParticipantTrackings.Where( pt => pt.MeetingParticipantSessionKey == existingMeetingParticipantSession.MeetingParticipantSessionKey && pt.Principal_ID == principal_id) .OrderByDescending(p => p.StartDate) .FirstOrDefault(); if(type == 0) { // Start if(lastParticipantTracking != null && !lastParticipantTracking.EndDate.HasValue) { var lastEngagement = lastParticipantTracking.MeetingParticipantSession.ParticipantEngagements.OrderByDescending(e => e.DisplayTime) .FirstOrDefault(); if(lastEngagement == null) lastParticipantTracking.EndDate = lastParticipantTracking.StartDate; else { lastParticipantTracking.EndDate = lastEngagement.ResponseTime.HasValue ? lastEngagement.ResponseTime.Value : lastEngagement.DisplayTime; if(lastParticipantTracking.EndDate < lastParticipantTracking.StartDate) lastParticipantTracking.EndDate = lastParticipantTracking.StartDate; } } var pTrack = new ParticipantTracking { MeetingParticipantSessionKey = existingMeetingParticipantSession.MeetingParticipantSessionKey, Principal_ID = principal_id, StartDate = DateTime.UtcNow //timestamp }; context.ParticipantTrackings.InsertOnSubmit(pTrack); } else { // End if(lastParticipantTracking == null) throw new ArgumentException(string.Format("Exiting user does not have an entrance time MeetingSessionKey = {0}, principalId = {1}", meetingSessionKey, principal_id)); lastParticipantTracking.EndDate = DateTime.UtcNow; } context.SubmitChanges(); CheckParticipantSessions(session, principal_id); } else { result.Success = false; result.Message = "MeetingSessionKey: " + meetingSessionKey + " is not valid"; throw new ArgumentException(result.Message); } } else { result.Success = false; result.Message = "Principal ID: " + principal_id + " is not valid"; throw new ArgumentException(result.Message); } } catch (Exception ex) { Extensions.LogServiceError("TrackUser", ex); result.Success = false; result.Message = ex.Message; } return result; } [WebMethod] public int TrackEngagementReceived(int scoId, int principalId, double timeDelta) { Extensions.LogServiceCall("TrackEngagementReceived", string.Format("Parameters: scoId = {0} principalId = {1} timeDelta = {2} DateTimeUtc = {3}", scoId, principalId, timeDelta, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; var context = Database; try { if(principalId > 0) { //var session = context.MeetingSessions.OrderByDescending(ms => ms.StartDate).FirstOrDefault(m => m.SCO_ID == scoId & !m.EndDate.HasValue); //JM - 11/20/2012 changed to allow for session end dates existing for recordings var session = getActiveSessionBySco(scoId); if(session != null) { CheckParticipantSessions(session, principalId); var participantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) . FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); if(participantSession == null) { participantSession = new MeetingParticipantSession { MeetingSessionKey = session.MeetingSessionKey, ParticipantKey = principalId, PlaySound = true, Created = DateTime.UtcNow }; context.MeetingParticipantSessions.InsertOnSubmit(participantSession); context.SubmitChanges(); //JM-12/27/2012 Change to check for multiple meeting participant sessions CheckParticipantSessions(session, principalId); participantSession = context.MeetingParticipantSessions.OrderBy( p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); //Check Participant Tracking CheckParticipantTracking(participantSession, principalId); } //else //{ // //Check Participant Tracking // CheckParticipantTracking(participantSession, principalId); //} var now = DateTime.UtcNow; var engagementVerification = new ParticipantEngagementVerification { MeetingParticipantSessionKey = participantSession.MeetingParticipantSessionKey, Principal_ID = principalId, DisplayTime = now, PreviousAlertDelta = timeDelta }; context.ParticipantEngagementVerifications.InsertOnSubmit(engagementVerification); context.SubmitChanges(); result = engagementVerification.TrackingKey; } else { throw new ArgumentException(string.Format("No session exists for sco = {0}, principal_id = {1}", scoId, principalId)); } } else { throw new ArgumentException("Principal ID: " + principalId + " is not valid"); } } catch (Exception ex) { Extensions.LogServiceError("TrackEngagementReceived", ex); result = -1; } return result; } [WebMethod] public int TrackEngagementDisplay(int scoId, int principalId) { Extensions.LogServiceCall("TrackEngagementDisplay", string.Format("Parameters: scoId = {0} principalId = {1} DateTimeUtc = {2}", scoId, principalId, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; try { if(principalId > 0) { var context = Database; //var session = context.MeetingSessions.OrderByDescending(ms => ms.StartDate).FirstOrDefault(m => m.SCO_ID == scoId & !m.EndDate.HasValue); //JM - 11/20/2012 changed to allow for session end dates existing for recordings var session = getActiveSessionBySco(scoId); if(session != null) { CheckParticipantSessions(session, principalId); var participantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); if(participantSession == null) { participantSession = new MeetingParticipantSession { MeetingSessionKey = session.MeetingSessionKey, ParticipantKey = principalId, PlaySound = true, Created = DateTime.UtcNow }; context.MeetingParticipantSessions.InsertOnSubmit(participantSession); context.SubmitChanges(); //JM-12/27/2012 Change to check for multiple meeting participant sessions CheckParticipantSessions(session, principalId); //make sure tracking gets assigned right key participantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); //Check Participant Tracking CheckParticipantTracking(participantSession, principalId); } else { //Check Participant Tracking CheckParticipantTracking(participantSession, principalId); } //var validEngagement = false; ////Validate the engangement request to make sure it was sent by the host within the last 30 seconds //var now = DateTime.UtcNow; //var hostPoll = session.MeetingSessionHeartbeats.OrderByDescending(h => h.Timestamp).FirstOrDefault(); //if (hostPoll != null) //{ // var timeDelta = Math.Abs((now - hostPoll.Timestamp).TotalSeconds); // validEngagement = timeDelta <= 30.0; //} //if (validEngagement) //{ // var engagement = new ParticipantEngagement() // { // MeetingParticipantSessionKey = // participantSession.MeetingParticipantSessionKey, // Principal_ID = principalId, // DisplayTime = now // }; // context.ParticipantEngagements.InsertOnSubmit(engagement); // context.SubmitChanges(); // result = engagement.ParticipantEngagementKey; //} var lastHeartbeat = session.MeetingSessionHeartbeats.OrderBy(h => h.Timestamp) .Last(); var lastEngagement = participantSession.ParticipantEngagements.Where(t => t.Principal_ID == principalId) .OrderBy(t => t.DisplayTime) .LastOrDefault(); if(lastEngagement == null || lastEngagement.DisplayTime < lastHeartbeat.Timestamp) { var now = DateTime.UtcNow; var engagement = new ParticipantEngagement { MeetingParticipantSessionKey = participantSession.MeetingParticipantSessionKey, Principal_ID = principalId, DisplayTime = now }; context.ParticipantEngagements.InsertOnSubmit(engagement); context.SubmitChanges(); result = engagement.ParticipantEngagementKey; } else result = lastEngagement.ParticipantEngagementKey; } else { throw new ArgumentException(string.Format("No session exists for sco = {0}, principal_id = {1}", scoId, principalId)); } } else { throw new ArgumentException("Principal ID: " + principalId + " is not valid"); } } catch (Exception ex) { Extensions.LogServiceError("TrackEngagementDisplay", ex); result = -99; } return result; } [WebMethod] public int TrackEngagementResponse(int scoId, int principalId) //int engagementKey { Extensions.LogServiceCall("TrackEngagementResponse", string.Format("Parameters: scoId = {0} principalId = {1} DateTimeUtc = {2}", scoId, principalId, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); var result = -1; try { if(principalId > 0) { var context = Database; var now = DateTime.UtcNow; //JM - 9-21-2012 changing to ignore key - will require updating pod //var engagement = context.ParticipantEngagements.Single(pe => pe.ParticipantEngagementKey == engagementKey); //var session = // Database.MeetingSessions.OrderByDescending(ms => ms.StartDate).FirstOrDefault(ms => ms.SCO_ID == scoId && !ms.EndDate.HasValue); //JM - 11/20/2012 changed to allow for session end dates existing for recordings var session = getActiveSessionBySco(scoId); if(session != null) { CheckParticipantSessions(session, principalId); //JM - 9-27-2012 Changed to grab first session. Fix for weird issue where some users were getting mutiple user sessions in a meeting. var participantSession = context.MeetingParticipantSessions.Where(mps => mps.MeetingSessionKey == session.MeetingSessionKey && mps.ParticipantKey == principalId) .OrderBy(mps => mps.MeetingParticipantSessionKey) .FirstOrDefault(); //var participantSession = // context.MeetingParticipantSessions.Single( // mps => // mps.MeetingSessionKey == session.MeetingSessionKey && mps.ParticipantKey == principalId); if(participantSession == null) { //create participant session participantSession = new MeetingParticipantSession { MeetingSessionKey = session.MeetingSessionKey, ParticipantKey = principalId, PlaySound = true, Created = DateTime.UtcNow }; context.MeetingParticipantSessions.InsertOnSubmit(participantSession); context.SubmitChanges(); //JM-12/27/2012 Change to check for multiple meeting participant sessions CheckParticipantSessions(session, principalId); participantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); //Check Participant Tracking CheckParticipantTracking(participantSession, principalId); } else { //Check Participant Tracking CheckParticipantTracking(participantSession, principalId); } //find last engagement without a response within 90 seconds var participantEngagement = context.ParticipantEngagements.OrderByDescending(pe => pe.DisplayTime) . FirstOrDefault( pe => pe.MeetingParticipantSessionKey == participantSession.MeetingParticipantSessionKey && (Math.Abs((now - pe.DisplayTime).TotalSeconds) <= 90) && !pe.ResponseTime.HasValue); if(participantEngagement != null) { //found it participantEngagement.ResponseTime = now; } //else //{ // //not found, create new engagement // participantEngagement = new ParticipantEngagement() // { // MeetingParticipantSessionKey = // participantSession.MeetingParticipantSessionKey, // Principal_ID = principalId, // DisplayTime = now, // ResponseTime = now // }; // context.ParticipantEngagements.InsertOnSubmit(participantEngagement); //} context.SubmitChanges(); result = 1; //participantEngagement.ParticipantEngagementKey; } else { throw new ArgumentException(string.Format("No session exists for sco = {0}, principal_id = {1}", scoId, principalId)); } } else { throw new ArgumentException("Principal ID: " + principalId + " is not valid"); } } catch (Exception ex) { Extensions.LogServiceError("TrackEngagementResponse", ex); result = -1; } return result; } [WebMethod] public Detail GetMeetingDetails(int sco_id) { Extensions.LogServiceCall("[service][GetMeetingDetails]", string.Format("Parameters: sco_id = {0}", sco_id)); var result = new Detail { CourseCode = "", Instructor = "", Location = "", TopicID = "", Topic = "" }; var meetingDetail = Database.MeetingDetails.SingleOrDefault(ms => ms.MeetingKey == sco_id); if(meetingDetail != null) { result.CourseCode = meetingDetail.CourseCode; result.Instructor = meetingDetail.Instructor; result.Location = meetingDetail.Location; result.TopicID = meetingDetail.TopicID; result.Topic = meetingDetail.TopicName; } return result; } [WebMethod] public ServiceStatus SetMeetingDetails(int sco_id, string courseCode, string instructor, string location, string topicID, string topic) { Extensions.LogServiceCall("SetMeetingDetails", string.Format("Parameters: sco_id = {0}, courseCode = {1} instructor = {2} location = {3} topicId = {4} topic = {5}", sco_id, courseCode, instructor, location, topicID, topic)); var result = new ServiceStatus { Success = true, Message = "" }; try { var context = Database; var existing = context.Meetings.SingleOrDefault(m => m.SCO_ID == sco_id); if(existing != null) { if(existing.MeetingDetail == null) { var meetingDetail = new MeetingDetail { MeetingKey = sco_id, CourseCode = courseCode, Instructor = instructor, Location = location, TopicID = topicID, TopicName = topic }; context.MeetingDetails.InsertOnSubmit(meetingDetail); } else { existing.MeetingDetail.CourseCode = courseCode; existing.MeetingDetail.Instructor = instructor; existing.MeetingDetail.Location = location; existing.MeetingDetail.TopicID = topicID; existing.MeetingDetail.TopicName = topic; } } else { var meeting = new Meeting { SCO_ID = sco_id, MeetingKey = sco_id }; var meetingDetail = new MeetingDetail(); meetingDetail.CourseCode = courseCode; meetingDetail.Instructor = instructor; meetingDetail.Location = location; meetingDetail.TopicID = topicID; meetingDetail.TopicName = topic; meetingDetail.MeetingKey = sco_id; context.Meetings.InsertOnSubmit(meeting); context.MeetingDetails.InsertOnSubmit(meetingDetail); } context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("SetMeetingDetails", ex); result.Success = false; result.Message = ex.Message; } return result; } [WebMethod] public long GetTotalSessionTime(int sco_id) { Extensions.LogServiceCall("GetTotalSessionTime", string.Format("Parameters: sco_id = {0}", sco_id)); long result = -1; try { result = 0; var context = Database; var session = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue) .OrderByDescending(ms => ms.StartDate) .FirstOrDefault(); if(session != null) { if(session.EndDate.HasValue) { result = session.EndDate.Value.Ticks - session.StartDate.Ticks; } else { result = DateTime.UtcNow.Ticks - session.StartDate.Ticks; } var completedPauses = session.MeetingSessionPauses.Where(msp => msp.EndDate.HasValue); result = completedPauses.Aggregate(result, (current, meetingSessionPause) => current - (meetingSessionPause.EndDate.Value.Ticks - meetingSessionPause.StartDate.Ticks)); var incompletePause = session.MeetingSessionPauses.Where(msp => !msp.EndDate.HasValue) .OrderByDescending(msp => msp.StartDate) .FirstOrDefault(); if(incompletePause != null) { result -= DateTime.UtcNow.Ticks - incompletePause.StartDate.Ticks; } } result = result/TimeSpan.TicksPerMillisecond; } catch (Exception ex) { Extensions.LogServiceError("GetTotalSessionTime", ex); result = -1; } return result; } [WebMethod] public int ActiveSessionCount(int sco_id) { Extensions.LogServiceCall("ActiveSessionCount", string.Format("Parameters: sco_id = {0}", sco_id)); var result = 1; try { var context = Database; result = context.MeetingSessions.Count(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue); } catch (Exception ex) { Extensions.LogServiceError("ActiveSessionCount", ex); result = -1; } return result; } [WebMethod] public int EndAllActiveSessions(int sco_id) { Extensions.LogServiceCall("EndAllActiveSessions", string.Format("Parameters: sco_id = {0}", sco_id)); var result = 1; try { var now = DateTime.UtcNow; var context = Database; var activeSessions = context.MeetingSessions.Where(ms => ms.SCO_ID == sco_id && !ms.EndDate.HasValue); foreach (var meetingSession in activeSessions) { meetingSession.EndDate = now; } context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("EndAllActveSessions", ex); result = -1; } return result; } [WebMethod] public int RecordHeartbeatTick(int meetingSessionKey, int principal_id, double previousInterval) { Extensions.LogServiceCall("RecordHeartbeatTick", string.Format("Parameters: meetingSessionKey= {0}, principal_id = {1}, previousInterval = {2}", meetingSessionKey, principal_id, previousInterval)); var result = -1; try { var context = Database; var tickRecord = new MeetingSessionHeartbeatTick { MeetingSessionKey = meetingSessionKey, Timestamp = DateTime.UtcNow, Principal_ID = principal_id, PreviousInterval = previousInterval }; context.MeetingSessionHeartbeatTicks.InsertOnSubmit(tickRecord); context.SubmitChanges(); result = 1; } catch (Exception ex) { Extensions.LogServiceError("RecordHeartbeatTick", ex); result = -1; } return result; } [WebMethod] public bool RecordParticipantLog(int scoId, int principalId, string details) { Extensions.LogServiceCall("RecordParticipantLog", string.Format("Parameters: sco_id = {0} principal_id = {1} details = {2} DateTimeUtc = {3}", scoId, principalId, details, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); try { var context = Database; var log = new ParticipantLog { Sco_ID = scoId, Principal_ID = principalId, Details = HttpUtility.HtmlDecode(details.Replace("_cr_", "\r\n")), Created = DateTime.UtcNow }; context.ParticipantLogs.InsertOnSubmit(log); context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("RecordParticipantLog", ex); } return true; } [WebMethod] public bool RecordHostMessage(int scoId, int principalId, string message) { Extensions.LogServiceCall("RecordHostMessage", string.Format("Parameters: sco_id = {0} principal_id = {1} message = {2} DateTimeUtc = {3}", scoId, principalId, message, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); try { var context = Database; var log = new HostMessageLog { SCO_ID = scoId, Principal_ID = principalId, Message = message }; context.HostMessageLogs.InsertOnSubmit(log); context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("RecordHostMessage", ex); } return true; } [WebMethod] public bool RecordParticipantMessage(int scoId, int principalId, string message) { Extensions.LogServiceCall("RecordParticipantMessage", string.Format("Parameters: sco_id = {0} principal_id = {1} details = {2} DateTimeUtc = {3}", scoId, principalId, message, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"))); try { var context = Database; var log = new ParticipantMessageLog { SCO_ID = scoId, Principal_ID = principalId, Message = message }; context.ParticipantMessageLogs.InsertOnSubmit(log); context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("RecordParticipantMessage", ex); } return true; } [WebMethod] public ServiceStatus ArchiveSessionEnd(int scoId, int principalId, string firstname, string lastname, string email) { Extensions.LogServiceCall("[service.asmx][ArchiveSessionEnd]", string.Format("Parameters: scoId = {0} principalId = {1} firstname = {2} lastname = {3} email = {4}", scoId, principalId, firstname, lastname, email)); var result = new ServiceStatus { Success = true, Message = "" }; MeetingParticipantSession existingMeetingParticipantSession = null; try { if(principalId > 0) { var context = Database; //var meeting = context.Meetings.SingleOrDefault(m => m.SCO_ID == sco_id); //JM - 9/27/2012 changed session to add a check for session not ended var session = GetRecordingSession(scoId, context); if(session != null) { var user = context.Participants.SingleOrDefault(p => p.Principal_ID == principalId); if(user == null) { //create new user user = new Participant { Principal_ID = principalId, FirstName = firstname, LastName = lastname, Email = email }; context.Participants.InsertOnSubmit(user); } //JM 9/27/2012 - changed to grab first participant session found. Weird issue where some users were getting multiple sesions existingMeetingParticipantSession = session.MeetingParticipantSessions.OrderBy(mps => mps.MeetingParticipantSessionKey) .FirstOrDefault(mps => mps.ParticipantKey == principalId); if(existingMeetingParticipantSession == null) { //create new session for user existingMeetingParticipantSession = new MeetingParticipantSession { MeetingSessionKey = session.MeetingSessionKey, ParticipantKey = principalId, PlaySound = true, Created = DateTime.UtcNow }; context.MeetingParticipantSessions.InsertOnSubmit(existingMeetingParticipantSession); context.SubmitChanges(); //JM-12/27/2012 Change to check for multiple meeting participant sessions CheckParticipantSessions(session, principalId); //make sure tracking gets assigned right key existingMeetingParticipantSession = context.MeetingParticipantSessions.OrderBy(p => p.MeetingParticipantSessionKey) .FirstOrDefault( p => p.MeetingSessionKey == session.MeetingSessionKey & p.ParticipantKey == principalId); } var existingParticipantTracking = context.ParticipantTrackings.OrderByDescending(pt => pt.StartDate) .FirstOrDefault( pt => pt.MeetingParticipantSessionKey == existingMeetingParticipantSession.MeetingParticipantSessionKey && pt.Principal_ID == principalId); if(existingParticipantTracking != null) { if(!existingParticipantTracking.EndDate.HasValue) { //add exit time existingParticipantTracking.EndDate = DateTime.UtcNow; } } else { throw new ArgumentException(string.Format("Exiting user does not have an entrance time scoId = {0}, principalId = {1}, existingMeetingParticipantSession.MeetingParticipantSessionKey = {2}", scoId, principalId, existingMeetingParticipantSession.MeetingParticipantSessionKey)); } adobe.GetAdobeTransactions(session, Database); // Tyler Allen - 08/27/2016 // This is redundant and will cause deadlocks //Database.SubmitChanges(); CheckParticipantSessions(session, principalId); } else { result.Success = false; result.Message = "Archive scoId: " + scoId + " is not a valid session."; throw new ArgumentException(result.Message); } } else { result.Success = false; result.Message = "Principal ID: " + principalId + " is not valid"; throw new ArgumentException(result.Message); } } catch (Exception ex) { Extensions.LogServiceError("[service.asmx][ArchiveSessionEnd]", ex); result.Success = false; result.Message = ex.Message; } finally { CloseArchiveSession(scoId); } /* This is the new modifications created by Tyler Allen [08/22/2016] */ // TODO: Depricated for new Notify application // Launch a non-blocking thread to wait for 30 minutes for adobe results then email certificates // System.Threading.Thread waitForResultsThread = new System.Threading.Thread(() => ArchiveSessionEndResultsThread(existingMeetingParticipantSession)); // waitForResultsThread.Start(); // -u = unattended // -a = archive session try { Extensions.LogServiceCall("[service.asmx][ArchiveSessionEnd][Processing]", $"{notifyFilePath} -u -a {existingMeetingParticipantSession.MeetingParticipantSessionKey}"); //Process.Start($"{notifyFilePath} -u -a {existingMeetingParticipantSession.MeetingParticipantSessionKey}"); startNotify($"-u -a {existingMeetingParticipantSession.MeetingParticipantSessionKey}"); Extensions.LogServiceCall("[service.asmx][ArchiveSessionEnd][Started]", $"{notifyFilePath} -u -a {existingMeetingParticipantSession.MeetingParticipantSessionKey}"); } catch (Exception exception) { Extensions.LogServiceError("[service.asmx][ArchiveSessionEnd][CPE.App.Notify.exe]", exception); //Extensions.LogServiceCall("[service.asmx][ArchiveSessionEnd]", $"There was an error processing CPE.App.Notify.exe [{notifyFilePath} -u -a {existingMeetingParticipantSession.MeetingParticipantSessionKey}] ({exception.Message}"); } /* This is the new modifications created by Tyler Allen [08/22/2016] */ return result; } [WebMethod] public bool CloseOpenUserSessions(int meetingSessionKey) { Extensions.LogServiceCall("CloseOpenUserSessions", string.Format("Parameters: meetingSessionKey = {0}", meetingSessionKey)); var result = false; try { var session = Database.MeetingSessions.Single(ms => ms.MeetingSessionKey == meetingSessionKey); var participants = Database.MeetingParticipantSessions.Where(m => m.MeetingSessionKey == session.MeetingSessionKey); foreach (var participantSession in participants) foreach (var participantTracking in participantSession.ParticipantTrackings.Where(t => !t.EndDate.HasValue)) { // Tyler changed to get EndDate from the last engagement request. //participantTracking.EndDate = session.EndDate; var lastEngagement = participantTracking.MeetingParticipantSession.ParticipantEngagements.OrderByDescending(e => e.DisplayTime) .FirstOrDefault(); if(lastEngagement == null) participantTracking.EndDate = participantTracking.StartDate; else participantTracking.EndDate = lastEngagement.ResponseTime.HasValue ? lastEngagement.ResponseTime.Value : lastEngagement.DisplayTime; } Database.SubmitChanges(); CleanupRebroadcastEngagements(meetingSessionKey); result = true; } catch (Exception ex) { Extensions.LogServiceError("CloseOpenUserSessions", ex); result = false; } return result; } private MeetingSession GetRecordingSession(int scoId, CPEWebDataContext context) { var now = DateTime.UtcNow; var twelveHoursAgo = now.AddHours(-12); var session = context.MeetingSessions.Where(ms => ms.SCO_ID == scoId && ms.StartDate >= twelveHoursAgo && ms.StartDate < now && ms.EndDate.HasValue) .OrderByDescending(ms => ms.EndDate) .FirstOrDefault() ?? getLastSession(scoId); return session; } //static Dictionary> Tasks = new Dictionary>(); private void CloseArchiveSession(int scoId) { var context = Database; var session = GetRecordingSession(scoId, context); if(session != null) { //session.EndDate = endTime; var participantSessions = context.MeetingParticipantSessions.Where(m => m.MeetingSessionKey == session.MeetingSessionKey); foreach (var participantSession in participantSessions) { var trackingsWithoutExit = context.ParticipantTrackings.Where( t => t.MeetingParticipantSessionKey == participantSession.MeetingParticipantSessionKey && !t.EndDate.HasValue) .OrderByDescending(t => t.StartDate) .FirstOrDefault(); //TODO: Try to catch logouts due to power failure //look for las poll displayed and compare to logout time? if(trackingsWithoutExit != null) { // Tyler changed to get EndDate from the last engagement request. //trackingsWithoutExit.EndDate = session.EndDate; var lastEngagement = trackingsWithoutExit.MeetingParticipantSession.ParticipantEngagements.OrderByDescending(e => e.DisplayTime) .FirstOrDefault(); if(lastEngagement == null) trackingsWithoutExit.EndDate = trackingsWithoutExit.StartDate; else trackingsWithoutExit.EndDate = lastEngagement.ResponseTime.HasValue ? lastEngagement.ResponseTime.Value : lastEngagement.DisplayTime; } //foreach ( // var participantTracking in // participantSession.ParticipantTrackings.Where(t => !t.EndDate.HasValue)) // participantTracking.EndDate = session.EndDate; } context.SubmitChanges(); CleanupRebroadcastEngagements(session.MeetingSessionKey); //CleanupSessionEngagements(session.MeetingSessionKey); } //string taskId = Guid.NewGuid().ToString(); //Tasks[taskId] = Task.Factory.StartNew(() => RunTask(scoId)); //return taskId; } //private bool RunTask(int scoId) //{ // var result = false; // //wait 10 seconds so all user logouts are tracked // const int secondsToSleep = 2; // Thread.Sleep(secondsToSleep * 1000); // try // { // var context = Database; // var session = getLastSession(scoId); // if(session!= null) // { // //session.EndDate = endTime; // var participants = context.MeetingParticipantSessions.Where(m => m.MeetingSessionKey == session.MeetingSessionKey); // foreach (var participantSession in participants) // foreach (var participantTracking in participantSession.ParticipantTrackings.Where(t => !t.EndDate.HasValue)) // participantTracking.EndDate = session.EndDate; // context.SubmitChanges(); // //CleanupSessionEngagements(session.MeetingSessionKey); // result = true; // } // } // catch (Exception ex) // { // Extensions.LogServiceError("CloseArchiveSession", ex); // result = false; // } // return result; //} [WebMethod] public bool CleanupSession(int sessionKey) { Extensions.LogServiceCall("CleanupSession", string.Format("Parameters: sessionKey = {0}", sessionKey)); CleanupSessionEngagements(sessionKey); return true; } private void CleanupSessionEngagements(int sessionKey) { try { var context = Database; Database.ArchiveSessionEngagements(sessionKey); var session = context.MeetingSessions.Single(ms => ms.MeetingSessionKey == sessionKey); //filter out engagements not sent by host var engagementsToKeep = new List(); var heartbeats = session.MeetingSessionHeartbeats.Select(h => h.Timestamp) .ToList(); foreach (var heartbeat in heartbeats) { foreach (var ps in session.MeetingParticipantSessions) { var validEngagements = context.ParticipantEngagements.Where( pe => Math.Abs((pe.DisplayTime - heartbeat).TotalSeconds) < 30.0) .Select(pe => pe.ParticipantEngagementKey) .Distinct(); engagementsToKeep.AddRange(validEngagements); } } engagementsToKeep = engagementsToKeep.Distinct() .ToList(); var tooLarge = engagementsToKeep.Count >= 2000; var meetingParticipantEngagementKeys = session.MeetingParticipantSessions.Select(mps => mps.MeetingParticipantSessionKey) .ToList(); var allParticipantEngagements = context.ParticipantEngagements.Where( pe => meetingParticipantEngagementKeys.Contains(pe.MeetingParticipantSessionKey)); if(tooLarge) { var allEngagementKeys = allParticipantEngagements.Select(pe => pe.ParticipantEngagementKey) .ToList(); var engagementsToDelete = allEngagementKeys.Except(engagementsToKeep); var query = string.Format( "DELETE FROM dbo.ParticipantEngagements WHERE ParticipantEngagementKey IN({0})", string.Join(",", engagementsToDelete)); context.ExecuteCommand(query); } else { var engagementsToDelete = allParticipantEngagements.Where(pe => !engagementsToKeep.Contains(pe.ParticipantEngagementKey)); context.ParticipantEngagements.DeleteAllOnSubmit(engagementsToDelete); context.SubmitChanges(); } //filter out duplicates var duplicateKeys = new List(); foreach (var mps in session.MeetingParticipantSessions) { var duplicateEngagements = context.GetDuplicateEngagements(mps.MeetingParticipantSessionKey) .ToList(); if(duplicateEngagements.Any()) { var keysProcessed = new List(); var keysToKeep = new List(); foreach (var getDuplicateEngagementsResult in duplicateEngagements) { if(!keysProcessed.Contains(getDuplicateEngagementsResult.ParticipantEngagementKey)) { var currentTime = getDuplicateEngagementsResult.DisplayTime; var currentDuplicates = duplicateEngagements.Where( de => Math.Abs((de.DisplayTime - currentTime).TotalSeconds) <= 30.0); var currentDuplicatesWithResponse = currentDuplicates.Where(cd => cd.ResponseTime.HasValue); var currentDuplicatesWithoutResponse = currentDuplicates.Where(cd => !cd.ResponseTime.HasValue); var dupKeyToKeep = currentDuplicatesWithResponse.Any() ? currentDuplicatesWithResponse.First() .ParticipantEngagementKey : currentDuplicatesWithoutResponse.First() .ParticipantEngagementKey; keysToKeep.Add(dupKeyToKeep); keysProcessed.AddRange(currentDuplicates.Select(cd => cd.ParticipantEngagementKey)); } } duplicateKeys.AddRange(duplicateEngagements.Where(de => !keysToKeep.Contains(de.ParticipantEngagementKey)) .Select(de => de.ParticipantEngagementKey)); } } if(duplicateKeys.Any()) { var distinctKeysToDelete = duplicateKeys.Distinct(); var duplicates = context.ParticipantEngagements.Where(pe => distinctKeysToDelete.Contains(pe.ParticipantEngagementKey)); context.ParticipantEngagements.DeleteAllOnSubmit(duplicates); } context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("CleanupSessionEngagements", ex); Console.WriteLine(ex.Message); } } [WebMethod] public bool CleanupRebroadcast(int sessionKey) { Extensions.LogServiceCall("CleanupRebroadcast", string.Format("Parameters: sessionKey = {0}", sessionKey)); CleanupRebroadcastEngagements(sessionKey); return true; } private void CleanupRebroadcastEngagements(int sessionKey) { try { var context = Database; var meetingParticipantSessionKeys = context.MeetingParticipantSessions.Where(mps => mps.MeetingSessionKey == sessionKey) .Select(mps => mps.MeetingParticipantSessionKey) .ToArray(); var archived = context.ParticipantEngagementsArchives.Any( pa => meetingParticipantSessionKeys.Contains(pa.MeetingParticipantSessionKey)); if(!archived) Database.ArchiveSessionEngagements(sessionKey); var session = context.MeetingSessions.Single(ms => ms.MeetingSessionKey == sessionKey); //filter out duplicates var duplicateKeys = new List(); foreach (var mps in session.MeetingParticipantSessions) { var duplicateEngagements = context.GetDuplicateEngagements(mps.MeetingParticipantSessionKey) .ToList(); if(duplicateEngagements.Any()) { var keysProcessed = new List(); var keysToKeep = new List(); foreach (var getDuplicateEngagementsResult in duplicateEngagements) { if(!keysProcessed.Contains(getDuplicateEngagementsResult.ParticipantEngagementKey)) { var currentTime = getDuplicateEngagementsResult.DisplayTime; var currentDuplicates = duplicateEngagements.Where( de => Math.Abs((de.DisplayTime - currentTime).TotalSeconds) <= 30.0); var currentDuplicatesWithResponse = currentDuplicates.Where(cd => cd.ResponseTime.HasValue); var currentDuplicatesWithoutResponse = currentDuplicates.Where(cd => !cd.ResponseTime.HasValue); var dupKeyToKeep = currentDuplicatesWithResponse.Any() ? currentDuplicatesWithResponse.First() .ParticipantEngagementKey : currentDuplicatesWithoutResponse.First() .ParticipantEngagementKey; keysToKeep.Add(dupKeyToKeep); keysProcessed.AddRange(currentDuplicates.Select(cd => cd.ParticipantEngagementKey)); } } duplicateKeys.AddRange(duplicateEngagements.Where(de => !keysToKeep.Contains(de.ParticipantEngagementKey)) .Select(de => de.ParticipantEngagementKey)); } } if(duplicateKeys.Any()) { var distinctKeysToDelete = duplicateKeys.Distinct(); var duplicates = context.ParticipantEngagements.Where(pe => distinctKeysToDelete.Contains(pe.ParticipantEngagementKey)); context.ParticipantEngagements.DeleteAllOnSubmit(duplicates); } context.SubmitChanges(); } catch (Exception ex) { Extensions.LogServiceError("CleanupRebroadcastEngagements", ex); Console.WriteLine(ex.Message); } } private void CheckParticipantSessions(MeetingSession session, int principal_id) { try { var context = Database; var participantSessionCount = session.MeetingParticipantSessions.Count(mps => mps.ParticipantKey == principal_id); if(participantSessionCount > 1) { var participantSessions = session.MeetingParticipantSessions.Where(mps => mps.ParticipantKey == principal_id) .OrderBy( mps => mps.MeetingParticipantSessionKey); var psToKeep = participantSessions.First(); var psessionsToDelete = participantSessions.Where( ps => ps.MeetingParticipantSessionKey != psToKeep.MeetingParticipantSessionKey); foreach (var meetingParticipantSession in psessionsToDelete) { context.MeetingParticipantSessions.DeleteOnSubmit(meetingParticipantSession); } context.SubmitChanges(); } } catch (Exception ex) { Extensions.LogServiceError("CheckParticipanSessions", ex); } } private MeetingSession getActiveSessionBySco(int sco_id) { var now = DateTime.UtcNow; var session = Database.MeetingSessions.FirstOrDefault( ms => ms.SCO_ID == sco_id && ms.StartDate <= now && (!ms.EndDate.HasValue || ms.EndDate.Value >= now)); return session; } private MeetingSession getActiveSessionByKey(int meetingSessionKey) { var now = DateTime.UtcNow; var session = Database.MeetingSessions.SingleOrDefault(ms => ms.MeetingSessionKey == meetingSessionKey && ms.StartDate <= now); // Tyler Allen - 09/01/2016 // Just send a session no matter what // && (!ms.EndDate.HasValue || ms.EndDate.Value >= now)); return session; } private MeetingSession getLastSession(int sco_id) { var now = DateTime.UtcNow; var session = Database.MeetingSessions.Where( ms => ms.SCO_ID == sco_id && ms.StartDate <= now && ms.EndDate.HasValue && ms.EndDate.Value <= now) .OrderByDescending(ms => ms.StartDate) .FirstOrDefault(); return session; } private void CheckParticipantTracking(MeetingParticipantSession participantSession, int principalId) { //Extensions.LogServiceCall("CheckParticipantTracking", String.Format("Parameters: participantSession.MeetingParticipantSessionKey = {0} principal_id = {1}", participantSession.MeetingParticipantSessionKey, principalId)); var context = Database; var tracking = context.ParticipantTrackings.Where(t => t.Principal_ID == principalId && t.MeetingParticipantSessionKey == participantSession.MeetingParticipantSessionKey && !t.EndDate.HasValue) .OrderByDescending(t => t.StartDate) .FirstOrDefault(); if(tracking == null) { tracking = new ParticipantTracking { MeetingParticipantSessionKey = participantSession.MeetingParticipantSessionKey, Principal_ID = principalId, StartDate = DateTime.UtcNow }; context.ParticipantTrackings.InsertOnSubmit(tracking); context.SubmitChanges(); } } } public class SessionStatusClass { public int MeetingSessionKey { get; set; } public string Status { get; set; } public int OwnerPrincipalId { get; set; } } public class ServiceStatus { public bool Success { get; set; } public string Message { get; set; } } public class Setting { public int Interval { get; set; } public string Text { get; set; } public int Duration { get; set; } } public class Detail { public string CourseCode { get; set; } public string Instructor { get; set; } public string Location { get; set; } public string TopicID { get; set; } public string Topic { get; set; } } }