Ejemplo n.º 1
0
        private async Task <SubmitResult <RosterEntry> > SaveSigninInternal(MemberSignIn value, SubmitResult <RosterEntry> result)
        {
            if (result.Errors.Count == 0)
            {
                using (var db = dbFactory())
                {
                    MemberSignIn signin;
                    if (value.Id == 0)
                    {
                        signin = value;
                        db.SignIns.Add(value);
                    }
                    else
                    {
                        signin = db.SignIns.Single(f => f.Id == value.Id);
                    }

                    if (signin.TimeOut != value.TimeOut)
                    {
                        signin.TimeOut = value.TimeOut;
                    }
                    if (signin.Miles != value.Miles)
                    {
                        signin.Miles = value.Miles;
                    }
                    await AssignInternal(signin, signin.EventId, db, config);

                    result.Data = new[] { signin }.AsQueryable().Select(proj).Single();
                }
            }
            return(result);
        }
Ejemplo n.º 2
0
 public async Task <SubmitResult <RosterEntry> > Put(MemberSignIn value)
 {
     log.InfoFormat("User {0} updating signin for member {1} ({2}) on event {3}. New timeout = {4}",
                    User.Identity.Name,
                    value.MemberId,
                    value.Name,
                    value.EventId,
                    value.TimeOut);
     return(await SaveSigninInternal(value, new SubmitResult <RosterEntry>()));
 }
Ejemplo n.º 3
0
        private static MemberSignIn SplitSignin(int?eventId, MemberSignIn outer, MemberSignIn inner, IMissionLineDbContext db, IConfigSource config)
        {
            MemberSignIn front = new MemberSignIn
            {
                EventId  = eventId,
                isMember = outer.isMember,
                MemberId = outer.MemberId,
                Name     = outer.Name,
                TimeIn   = outer.TimeIn,
                TimeOut  = inner.TimeIn
            };

            db.SignIns.Add(front);
            outer.TimeIn = inner.TimeOut ?? DateTimeOffset.UtcNow.ToOrgTime(config);
            return(front);
        }
        public void Assign(string movingIn, string movingOut, string[] existingIns, string[] expectedOuts)
        {
            string memberId = "the member";

            var context     = TestContext.GetDefault <TestContext>();
            var membersMock = new Mock <IMemberSource>(MockBehavior.Strict);
            var controller  = new RosterController(() => context.DBMock.Object, context.ConfigMock.Object, membersMock.Object, new ConsoleLogger());

            int eventId = 52;

            context.Events.Add(new SarEvent {
                Id = eventId, Opened = "3:00".TimeToDate().Value, Name = "Test Event"
            });
            for (int i = 0; i < existingIns.Length; i += 2)
            {
                context.SignIns.Add(new MemberSignIn {
                    Id = i, MemberId = memberId, TimeIn = existingIns[i].TimeToDate().Value, TimeOut = existingIns[i + 1].TimeToDate(), EventId = eventId
                });
            }

            var target = new MemberSignIn {
                Id = 54, MemberId = memberId, EventId = null, TimeIn = movingIn.TimeToDate().Value, TimeOut = movingOut.TimeToDate()
            };

            context.SignIns.Add(target);

            var apiResult = Task.Run(() => controller.Assign(target.Id, eventId)).Result;

            Assert.AreEqual(0, apiResult.Errors.Count, "expect no errors: " + string.Join("\n", apiResult.Errors.Select(f => f.Text)));
            Assert.AreEqual(0, context.SignIns.Count(f => f.EventId == null), "no leftovers");
            var results = context.SignIns.OrderBy(f => f.TimeIn).ToList();

            Assert.AreEqual(expectedOuts.Length / 2, results.Count, "expected results");
            for (int i = 0; i < results.Count; i++)
            {
                Assert.AreEqual(expectedOuts[2 * i][0] == '*', results[i] == target, "target is in the right place");
                Assert.AreEqual(expectedOuts[2 * i].Trim('*').TimeToDate(), results[i].TimeIn, "expected time in " + i.ToString());
                Assert.AreEqual(expectedOuts[2 * i + 1].TimeToDate(), results[i].TimeOut, "expected time out " + i.ToString());
                Assert.AreEqual(eventId, results[i].EventId, "is assigned to event " + i.ToString());
                Assert.AreEqual(memberId, results[i].MemberId, "correct memberid " + i.ToString());
            }
        }
Ejemplo n.º 5
0
        public async Task <SubmitResult <RosterEntry> > Post(MemberSignIn value)
        {
            var result = new SubmitResult <RosterEntry>();

            log.InfoFormat("User {0} adding signin for member {1} on event {2}", User.Identity.Name, value.MemberId, value.EventId);

            var memberIdClaim = ((ClaimsPrincipal)User).FindFirst("memberId");

            if (memberIdClaim == null || memberIdClaim.Value != value.MemberId)
            {
                result.Errors.Add(new SubmitError("memberId", "Currently only supports signing in yourself."));
            }
            else
            {
                var nameClaim = ((ClaimsPrincipal)User).FindFirst("name");
                value.Name     = nameClaim.Value;
                value.isMember = true;
            }

            return(await SaveSigninInternal(value, result));
        }
Ejemplo n.º 6
0
        public async Task <SubmitResult <EventEntry> > Merge(int fromId, int intoId)
        {
            var           result        = new SubmitResult <EventEntry>();
            List <Action> notifications = new List <Action>();
            var           hub           = this.config.GetPushHub <CallsHub>();

            using (var db = dbFactory())
            {
                var from = await db.Events.Include(f => f.SignIns).SingleOrDefaultAsync(f => f.Id == fromId);

                var into = await db.Events.Include(f => f.SignIns).SingleOrDefaultAsync(f => f.Id == intoId);

                var fromSignins = from.SignIns.ToList();
                var intoSignins = into.SignIns.ToList();

                foreach (var call in from.Calls)
                {
                    call.EventId = intoId;
                }

                var          allSignins = from.SignIns.Concat(into.SignIns).OrderBy(f => f.MemberId).ThenBy(f => f.TimeIn).ToArray();
                MemberSignIn lastSignin = null;
                for (int i = 0; i < allSignins.Length; i++)
                {
                    var thisSignin = allSignins[i];
                    if (lastSignin != null && lastSignin.MemberId == thisSignin.MemberId)
                    {
                        if (lastSignin.TimeOut == null || thisSignin.TimeIn <= lastSignin.TimeOut.Value)
                        {
                            if (thisSignin.TimeOut == null || lastSignin.TimeOut == null || thisSignin.TimeOut > lastSignin.TimeOut)
                            {
                                lastSignin.TimeOut = thisSignin.TimeOut;
                            }
                            var milesSum = lastSignin.Miles ?? 0 + thisSignin.Miles ?? 0;
                            lastSignin.Miles = (lastSignin.Miles.HasValue || thisSignin.Miles.HasValue) ? milesSum : (int?)null;
                            if (thisSignin.EventId == intoId)
                            {
                                into.SignIns.Remove(thisSignin);
                            }
                            continue;
                        }
                    }
                    if (thisSignin.EventId != intoId)
                    {
                        into.SignIns.Add(thisSignin);
                    }
                    lastSignin = thisSignin;
                }

                if (string.IsNullOrWhiteSpace(into.OutgoingText))
                {
                    into.OutgoingText = from.OutgoingText;
                }
                if (string.IsNullOrWhiteSpace(into.OutgoingUrl))
                {
                    into.OutgoingUrl = from.OutgoingUrl;
                }
                if (string.IsNullOrWhiteSpace(into.DirectionsText))
                {
                    into.DirectionsText = from.DirectionsText;
                }
                if (string.IsNullOrWhiteSpace(into.DirectionsUrl))
                {
                    into.DirectionsUrl = from.DirectionsUrl;
                }
                if (into.Closed == null)
                {
                    into.Closed = from.Closed;
                }

                db.Events.Remove(from);

                await db.SaveChangesAsync();

                result.Data = compiledProj(into);
                hub.removedEvent(from.Id);
                hub.updatedEvent(result.Data);
            }
            return(new SubmitResult <EventEntry>());
        }
Ejemplo n.º 7
0
        public void Signout()
        {
            var context = VoiceTestContext.GetDefault <VoiceTestContext>();
            var signin  = new MemberSignIn
            {
                Id       = 24,
                isMember = true,
                MemberId = context.Member.Id,
                Name     = context.Member.Name,
                TimeIn   = DateTime.Now.AddHours(-7.5)
            };

            context.SignIns.Add(signin);

            string expectedAnswer = string.Format(Speeches.PressOneTemplate,
                                                  string.Format(Speeches.PromptSignOutTemplate, string.Format(Speeches.AsMemberTemplate, context.Member.Name)))
                                    + string.Format(Speeches.PromptRecordMessageTemplate, 3)
                                    + string.Format(Speeches.PromptChangeResponder, 8)
                                    + string.Format(Speeches.PromptAdminMenu, 9);


            var result  = AnswerCallCheckResult(context, expectedAnswer);
            var element = result.Root.FirstNode as XElement;

            Assert.AreEqual("Gather", element.Name.LocalName, "expected answer gather step");
            var url = element.Attribute("action").Value;


            DateTimeOffset markerA = TimeUtils.GetLocalDateTime(context.ConfigMock.Object);

            result = Task.Run(() => context.DoApiCall(url, digits: "1")).Result.ToXDocument();

            // Should mark the member signed out in the database
            Assert.IsTrue(signin.TimeOut.HasValue &&
                          signin.TimeOut.Value <= TimeUtils.GetLocalDateTime(context.ConfigMock.Object) &&
                          signin.TimeOut.Value >= markerA, "default time out in range");
            var defaultTimeout = signin.TimeOut.Value;

            element = result.Root.FirstNode as XElement;
            Assert.AreEqual("Gather", element.Name.LocalName, "expected gather step 1");
            url = element.Attribute("action").Value;

            // Should ask for more information
            Assert.AreEqual("SetTimeOut", context.GetActionMethod(url).Name, "next prompt is set time out");

            // The call should be tracking the caller as signed out
            Assert.False(url.Contains(VoiceController.QueryFields.SignedInKey + "=1"), "action does not have signed in query value");

            // Update the time out for an hour from now.
            result = Task.Run(() => context.DoApiCall(url, "60")).Result.ToXDocument();
            Assert.AreEqual(
                string.Format(Speeches.SignedOutTemplate, context.Member.Name, TimeUtils.GetMiltaryTimeVoiceText(signin.TimeOut.Value)),
                result.Descendants("Say").First().Value,
                "report updated time out");
            Assert.AreEqual(defaultTimeout.AddMinutes(60), signin.TimeOut.Value, "updated time out");

            element = result.Root.FirstNode as XElement;
            Assert.AreEqual("Gather", element.Name.LocalName, "expected gather step 2");
            url = element.Attribute("action").Value;

            Assert.AreEqual("SetMiles", context.GetActionMethod(url).Name, "next prompt is set miles");

            // The caller updates their miles
            result = Task.Run(() => context.DoApiCall(url, "120")).Result.ToXDocument();
            Assert.AreEqual(Speeches.MilesUpdated, result.Descendants("Say").First().Value, "report updated miles");
            Assert.AreEqual(120, signin.Miles.Value, "updated miles");
        }
Ejemplo n.º 8
0
        internal static async Task <SubmitResult> AssignInternal(MemberSignIn signin, int?eventId, IMissionLineDbContext db, IConfigSource config)
        {
            var result        = new SubmitResult();
            var notifications = new List <Action>();
            var hub           = config.GetPushHub <CallsHub>();

            var exposedSignin = await db.SignIns
                                .Where(f => f.MemberId == signin.MemberId && f.EventId == signin.EventId && f.Id != signin.Id)
                                .OrderByDescending(f => f.TimeIn)
                                .FirstOrDefaultAsync();

            if (exposedSignin != null)
            {
                notifications.Add(() => hub.updatedRoster(compiledProj(exposedSignin), true));
            }

            var            others           = db.SignIns.Where(f => f.EventId == eventId && f.MemberId == signin.MemberId && f.Id != signin.Id).OrderBy(f => f.TimeIn).ToList();
            DateTimeOffset effectiveTimeOut = signin.TimeOut ?? DateTimeOffset.MaxValue;
            bool           rosterisLatest   = true;

            foreach (var other in others)
            {
                var otherTimeOut = other.TimeOut ?? DateTimeOffset.MaxValue;
                // R: [---------->
                // O:      [----------]

                // R: [---------->
                // O:       [-------->

                // R: [----------]
                // O:         [----->

                // R: [--------------------]
                // O:       [-------]

                // R: [---------]
                // O:     [--->

                // R: [------]
                // O:    [------]

                // R: [---]
                // O:       [----]

                // R:      [-----]
                // O:   [-----]

                // R:        [---]
                // O: [---]



                // Trim signins that overlap our time in.
                if (signin.TimeIn <= other.TimeIn)
                {
                    if (effectiveTimeOut >= other.TimeIn && effectiveTimeOut <= otherTimeOut)
                    {
                        // roster is before the other one. It is not the latest
                        rosterisLatest = false;
                        notifications.Add(() => hub.updatedRoster(compiledProj(other), true));
                        signin.TimeOut = other.TimeIn;
                    }
                    else if (effectiveTimeOut > otherTimeOut)
                    {
                        // split roster around other.
                        // roster is the most recent, other and front are not.
                        var front = SplitSignin(eventId, signin, other, db, config);
                        notifications.Add(() => hub.updatedRoster(compiledProj(front), false));
                        notifications.Add(() => hub.updatedRoster(compiledProj(other), false));
                    }
                }
                else
                {
                    if (otherTimeOut > signin.TimeIn && otherTimeOut <= effectiveTimeOut)
                    {
                        // other overlaps on the early side
                        other.TimeOut = signin.TimeIn;
                        notifications.Add(() => hub.updatedRoster(compiledProj(other), false));
                    }
                    else if (otherTimeOut > effectiveTimeOut)
                    {
                        //split other around roster
                        var front = SplitSignin(eventId, other, signin, db, config);
                        notifications.Add(() => hub.updatedRoster(compiledProj(front), false));
                        notifications.Add(() => hub.updatedRoster(compiledProj(other), true));
                        rosterisLatest = false;
                    }
                    else if (otherTimeOut < signin.TimeIn)
                    {
                        // other notification is not the latest.
                        notifications.Add(() => hub.updatedRoster(compiledProj(other), false));
                    }
                }
            }

            signin.EventId = eventId;
            notifications.Add(() => hub.updatedRoster(compiledProj(signin), rosterisLatest));
            db.SaveChanges();
            foreach (var notify in notifications)
            {
                notify();
            }

            return(result);
        }
Ejemplo n.º 9
0
        public async Task <TwilioResponse> DoSignInOut(TwilioRequest request)
        {
            var response = new TwilioResponse();

            using (var db = dbFactory())
            {
                var signin = await db.SignIns.Where(f => f.MemberId == this.session.MemberId).OrderByDescending(f => f.TimeIn).FirstOrDefaultAsync();

                var call = await db.Calls.SingleAsync(f => f.CallId == request.CallSid);

                DateTimeOffset time    = TimeUtils.GetLocalDateTime(this.config);
                var            sayDate = TimeUtils.GetMiltaryTimeVoiceText(time);

                if (signin == null || signin.TimeOut.HasValue)
                {
                    if (this.session.IsSignedIn)
                    {
                        throw new InvalidOperationException("Tried to sign out when not signed in");
                    }

                    signin = new MemberSignIn
                    {
                        MemberId = this.session.MemberId,
                        isMember = true,
                        Name     = this.session.MemberName,
                        TimeIn   = time,
                        EventId  = (this.CurrentEvents.Count == 1) ? this.CurrentEvents[0].Id : this.session.EventId,
                    };

                    db.SignIns.Add(signin);
                    call.Actions.Add(new CallAction {
                        Call = call, CallId = call.Id, Time = signin.TimeIn, Action = "Signed in " + signin.Name
                    });
                    await db.SaveChangesAsync();

                    this.session.IsSignedIn = true;

                    if (this.CurrentEvents.Count == 0)
                    {
                        await RosterController.AssignInternal(signin, null, db, this.config);

                        BeginMenu(response);
                        response.SayVoice(Speeches.SignedInUnassignedTemplate, this.session.MemberName, sayDate);
                        await EndMenu(response);
                    }
                    else if (this.CurrentEvents.Count == 1)
                    {
                        await RosterController.AssignInternal(signin, this.CurrentEvents[0].Id, db, this.config);

                        BeginMenu(response);
                        response.SayVoice(Speeches.SignedInTemplate, this.CurrentEvents[0].Name, this.session.MemberName, sayDate);
                        await EndMenu(response);
                    }
                    else
                    {
                        await RosterController.AssignInternal(signin, null, db, this.config);

                        BuildSetEventMenu(response, string.Format(Speeches.SignedInUnassignedTemplate, this.session.MemberName, sayDate), Url.Content("~/api/voice/SetSigninEvent"));
                    }
                }
                else
                {
                    signin.TimeOut = time;
                    call.Actions.Add(new CallAction {
                        Call = call, CallId = call.Id, Time = time, Action = "Signed out " + this.session.MemberName
                    });
                    this.session.IsSignedIn = false;
                    await db.SaveChangesAsync();

                    this.config.GetPushHub <CallsHub>().updatedRoster(RosterController.GetRosterEntry(signin.Id, db), true);

                    // add prompt for timeout beyond right now
                    response.BeginGather(new { timeout = 10, action = GetAction("SetTimeOut") });
                    response.SayVoice(Speeches.SignedOutTemplate, this.session.MemberName, sayDate);
                    response.SayVoice(Speeches.TimeoutPrompt);
                    response.EndGather();
                }
            }

            return(LogResponse(response));
        }