Ejemplo n.º 1
0
        //    protected VoteHelper VoteHelperLocal
        //    {
        //      get { return _voteHelper ?? (_voteHelper = new VoteHelper(true)); }
        //    }

        #region IBallotModel Members

        public object SwitchToBallotAndGetInfo(int ballotId, bool refresh)
        {
            SetAsCurrentBallot(ballotId);

            var ballot = GetCurrentBallot(refresh);

            if (ballot == null)
            {
                return(new { });
            }

            SessionKey.CurrentLocationGuid.SetInSession(ballot.LocationGuid);

            var allVotes = new VoteCacher(Db).AllForThisElection;

            return(new
            {
                BallotInfo = new
                {
                    Ballot = BallotInfoForJs(ballot, allVotes),
                    Votes = CurrentVotesForJs(ballot, allVotes),
                    NumNeeded = UserSession.CurrentElection.NumberToElect
                },
                Location = ContextItems.LocationModel.LocationInfoForJson(UserSession.CurrentLocation)
            });
        }
Ejemplo n.º 2
0
        public JsonResult GetAll()
        {
            var currentElection = UserSession.CurrentElection;

            if (currentElection == null)
            {
                return(new
                {
                    Error = "Election not selected"
                }.AsJsonResult());
            }

            var isSingleNameElection = currentElection.IsSingleNameElection;
            var votes = new VoteCacher(Db).AllForThisElection;

            return(new
            {
                people = new PersonCacher(Db).AllForThisElection.Select(p => new
                {
                    Id = p.C_RowId,
                    //p.PersonGuid,
                    Name = p.C_FullName,
                    p.Area,
                    p.Email,
                    p.Phone,
                    V = (p.CanReceiveVotes.AsBoolean() ? "1" : "0") + (p.CanVote.AsBoolean() ? "1" : "0"),
                    IRG = p.IneligibleReasonGuid,
                    NumVotes = isSingleNameElection
            ? votes.Where(v => v.PersonGuid == p.PersonGuid).Sum(v => v.SingleNameElectionCount).AsInt()
            : votes.Count(v => v.PersonGuid == p.PersonGuid)
                }),
                lastVid = votes.Any() ? votes.Max(v => v.C_RowId) : 0
            }.AsJsonResult());
        }
Ejemplo n.º 3
0
        public override object CurrentBallotsInfoList(bool refresh = false)
        {
            if (refresh)
            {
                new ElectionAnalyzerNormal().RefreshBallotStatuses(); // identical for single name elections
                Db.SaveChanges();
            }

            var votes = new VoteCacher(Db).AllForThisElection;
            //      var ballotGuidsWithVotes = votes.Select(v => v.BallotGuid).Distinct().ToList();

            var ballots = new BallotCacher(Db).AllForThisElection;

            var ballotsForLocation = ballots
                                     .Where(b => b.LocationGuid == UserSession.CurrentLocationGuid)
                                     //        .Where(b => ballotGuidsWithVotes.Contains(b.BallotGuid))
                                     .OrderBy(b => b.ComputerCode)
                                     .ThenBy(b => b.BallotNumAtComputer)
                                     .ToList();

            var ballotGuids = ballotsForLocation.Select(b => b.BallotGuid).ToList();

            var totalCount = votes
                             .Where(v => ballotGuids.Contains(v.BallotGuid))
                             .Sum(vi => vi.SingleNameElectionCount).AsInt();

            return(new
            {
                Ballots = ballotsForLocation.ToList().Select(ballot => BallotInfoForJs(ballot, votes)),
                Total = totalCount
            });
        }
Ejemplo n.º 4
0
        public JsonResult DeleteVote(int vid)
        {
            if (UserSession.CurrentElectionStatus == ElectionTallyStatusEnum.Finalized)
            {
                return(new { Message = UserSession.FinalizedNoChangesMessage }.AsJsonResult());
            }

            VoteCacher voteCacher = new VoteCacher(Db);

            var vote = voteCacher.AllForThisElection.SingleOrDefault(v => v.C_RowId == vid);

            if (vote == null)
            {
                return(new { Message = "Not found" }.AsJsonResult());
            }

            var ballot       = CurrentRawBallot();
            var isSingleName = UserSession.CurrentElection.IsSingleNameElection;
            var location     = new LocationCacher(Db).AllForThisElection.Single(l => l.LocationGuid == ballot.LocationGuid);

            if (location.IsVirtual)
            {
                return(new { Message = "Cannot delete votes from an online ballot." }.AsJsonResult());
            }

            Db.Vote.Attach(vote);
            Db.Vote.Remove(vote);
            Db.SaveChanges();

            var allVotes = voteCacher.RemoveItemAndSaveCache(vote).AllForThisElection;

            var ballotStatusInfo = BallotAnalyzerLocal.UpdateBallotStatus(ballot, VoteInfosFor(ballot, allVotes), false);

            UpdateVotePositions(vote.BallotGuid, allVotes);

            var sum = _helper.BallotCount(location.LocationGuid, isSingleName, null, allVotes);

            new BallotCacher(Db).UpdateItemAndSaveCache(ballot);
            Db.SaveChanges();

            var ballotCounts = isSingleName ? new VoteCacher().AllForThisElection
                               .Where(v => v.BallotGuid == ballot.BallotGuid)
                               .Sum(v => v.SingleNameElectionCount) : 0;
            var ballotCountNames = isSingleName ? new VoteCacher().AllForThisElection
                                   .Count(v => v.BallotGuid == ballot.BallotGuid) : 0;

            return(new
            {
                Deleted = true,
                Votes = CurrentVotesForJs(GetCurrentBallot(), allVotes),
                BallotStatus = ballotStatusInfo.Status.Value,
                BallotStatusText = ballotStatusInfo.Status.DisplayText,
                ballotStatusInfo.SpoiledCount,
                LocationBallotsEntered = sum,
                BallotId = ballot.C_RowId,
                SingleBallotCount = ballotCounts,
                SingleBallotNames = ballotCountNames,
                VoteUpdates = GetVoteUpdatesOnDelete(vote.PersonGuid, voteCacher, isSingleName),
            }.AsJsonResult());
        }
Ejemplo n.º 5
0
        private object GetVoteUpdates(int lastVoteId, VoteCacher voteCacher, bool isSingleName, PersonCacher personCacher)
        {
            if (lastVoteId == 0)
            {
                // single name elections
                return(null);
            }

            // ignores vote and ballot status - count how many times the name has been written on ballots

            var peopleInRecentVotes = voteCacher
                                      .AllForThisElection
                                      .Where(v => v.C_RowId > lastVoteId && v.PersonGuid != null)
                                      .Select(v => v.PersonGuid)
                                      .Distinct();

            var counts = voteCacher
                         .AllForThisElection
                         .Where(v => peopleInRecentVotes.Contains(v.PersonGuid))
                         .GroupBy(v => v.PersonGuid)
                         .Join(personCacher.AllForThisElection, votes => votes.Key, p => p.PersonGuid, (votes, p) => new { votes, p })
                         .Select(g => new
            {
                Id    = g.p.C_RowId,
                Count = g.votes.Sum(v => isSingleName ? v.SingleNameElectionCount : 1)
            })
                         .ToList();

            return(counts);
        }
Ejemplo n.º 6
0
        public override JsonResult StartNewBallotJson()
        {
            if (UserSession.CurrentElectionStatus == ElectionTallyStatusEnum.Finalized)
            {
                return(new { Message = UserSession.FinalizedNoChangesMessage }.AsJsonResult());
            }
            var locationModel = new LocationModel();

            if (locationModel.HasMultiplePhysicalLocations && UserSession.CurrentLocation == null)
            {
                return(new { Message = "Must select your location first!" }.AsJsonResult());
            }
            if (UserSession.GetCurrentTeller(1).HasNoContent())
            {
                return(new { Message = "Must select \"Teller at Keyboard\" first!" }.AsJsonResult());
            }

            var ballotInfo = CreateAndRegisterBallot();

            var allVotes = new VoteCacher(Db).AllForThisElection;

            return(new
            {
                BallotInfo = new
                {
                    Ballot = BallotInfoForJs(ballotInfo, allVotes),
                    Votes = CurrentVotesForJs(ballotInfo, allVotes),
                    NumNeeded = UserSession.CurrentElection.NumberToElect
                },
                Ballots = CurrentBallotsInfoList()
            }.AsJsonResult());
        }
Ejemplo n.º 7
0
        public override object BallotInfoForJs(Ballot b, List <Vote> allVotes)
        {
            var ballotCounts = new VoteCacher().AllForThisElection
                               .Where(v => v.BallotGuid == b.BallotGuid)
                               .Sum(v => v.SingleNameElectionCount);

            return(new
            {
                Id = b.C_RowId,
                Code = b.C_BallotCode,
                b.ComputerCode,
                Count = ballotCounts
            });
        }
Ejemplo n.º 8
0
        /// <Summary>Current Ballot... could be null</Summary>
        public Ballot GetCurrentBallot(bool refresh = false)
        {
            var isSingleNameElection = UserSession.CurrentElection.IsSingleNameElection;
            var currentBallotId      = SessionKey.CurrentBallotId.FromSession(0);

            var ballotCacher = new BallotCacher(Db);

            var ballot = ballotCacher.GetById(currentBallotId);

            if (ballot == null && isSingleNameElection)
            {
                ballot = ballotCacher.GetByComputerCode();
            }

            if (ballot == null)
            {
                if (isSingleNameElection)
                {
                    // will create empty ballot for this computer... do we need it?
                    //        ballot = CreateAndRegisterBallot();
                }
            }
            else
            {
                if (refresh)
                {
                    Db.Ballot.Attach(ballot);
                    var voteCacher = new VoteCacher(Db);
                    var votes      = voteCacher.AllForThisElection;
                    var voteInfos  = VoteInfosFor(ballot, votes);

                    SortVotes(voteInfos.OrderBy(vi => vi.PositionOnBallot).Select(v => v.VoteId).ToList(), voteCacher);
                    voteInfos = VoteInfosFor(ballot, votes);

                    BallotAnalyzerLocal.UpdateBallotStatus(ballot, voteInfos, true);

                    ballotCacher.UpdateItemAndSaveCache(ballot);
                    Db.SaveChanges();
                }
            }

            if (ballot != null && ballot.C_RowId != currentBallotId)
            {
                SessionKey.CurrentBallotId.SetInSession(ballot.C_RowId);
            }

            return(ballot);
        }
Ejemplo n.º 9
0
        /// <Summary>Delete a ballot, but only if already empty</Summary>
        public JsonResult DeleteBallotJson()
        {
            Ballot ballot;

            try
            {
                ballot = CurrentRawBallot();
            }
            catch (Exception e)
            {
                if (e.Message == "Sequence contains no matching element")
                {
                    return(new
                    {
                        Deleted = false,
                        Message = "Ballot not found"
                    }.AsJsonResult());
                }
                throw;
            }
            var ballotGuid = ballot.BallotGuid;

            var hasVotes = new VoteCacher(Db).AllForThisElection.Any(v => v.BallotGuid == ballotGuid);

            if (hasVotes)
            {
                return(new
                {
                    Deleted = false,
                    Message = "Can only delete a ballot when it has no votes."
                }.AsJsonResult());
            }

            new BallotCacher(Db).RemoveItemAndSaveCache(ballot);

            Db.Ballot.Attach(ballot);
            Db.Ballot.Remove(ballot);
            Db.SaveChanges();

            return(new
            {
                Deleted = true,
                Ballots = CurrentBallotsInfoList(),
                Location = ContextItems.LocationModel.LocationInfoForJson(UserSession.CurrentLocation)
            }.AsJsonResult());
        }
Ejemplo n.º 10
0
        /// <summary>
        /// This works only for the person deleting the vote. Other tellers will not know that their count of votes for this person
        /// should be reduced. However, when another vote is made for this person, all tellers will eventually know about it.
        /// The counts returned are not guaranteed as one teller's computer may get out of sync with the real total. Refreshing the
        /// teller's page should fix it.
        /// </summary>
        /// <param name="personGuid"></param>
        /// <param name="voteCacher"></param>
        /// <param name="isSingleName"></param>
        /// <returns></returns>
        private object GetVoteUpdatesOnDelete(Guid?personGuid, VoteCacher voteCacher, bool isSingleName)
        {
            var counts = voteCacher.AllForThisElection.Where(v => v.PersonGuid == personGuid)
                         .GroupBy(v => v.PersonGuid)
                         .Select(g => new
            {
                PersonGuid = g.Key,
                Count      = g.Sum(v => isSingleName ? v.SingleNameElectionCount : 1).DefaultTo(0)
            })
                         .ToList();

            if (!counts.Any(v => v.PersonGuid == personGuid))
            {
                counts.Add(new { PersonGuid = personGuid, Count = 0 });
            }

            return(counts);
        }
Ejemplo n.º 11
0
        public object CurrentBallotInfo()
        {
            var ballot = GetCurrentBallot();

            if (ballot == null)
            {
                return(null);
            }

            var allVotes = new VoteCacher(Db).AllForThisElection;

            return(new
            {
                Ballot = BallotInfoForJs(ballot, allVotes),
                Votes = CurrentVotesForJs(ballot, allVotes),
                NumNeeded = UserSession.CurrentElection.NumberToElect
            });
        }
Ejemplo n.º 12
0
        private object GetVoteUpdates(int lastVoteId, VoteCacher voteCacher, bool isSingleName)
        {
            if (lastVoteId == 0)
            {
                // single name elections
                return(null);
            }
            var peopleInRecentVotes = voteCacher.AllForThisElection.Where(v => v.C_RowId > lastVoteId && v.PersonGuid != null).Select(v => v.PersonGuid).Distinct();
            var counts = voteCacher.AllForThisElection.Where(v => peopleInRecentVotes.Contains(v.PersonGuid))
                         .GroupBy(v => v.PersonGuid)
                         .Select(g => new
            {
                PersonGuid = g.Key,
                Count      = g.Sum(v => isSingleName ? v.SingleNameElectionCount : 1)
            })
                         .ToList();

            return(counts);
        }
Ejemplo n.º 13
0
        private void UpgradeOldData()
        {
            var Db           = GetNewDbContext();
            var personCacher = new PersonCacher(Db);
            var testInfo     = personCacher.AllForThisElection.Select(p => new { p.CombinedInfo }).FirstOrDefault();

            if (testInfo == null)
            {
                return;
            }

            if (testInfo.CombinedInfo.HasContent() && !testInfo.CombinedInfo.Contains("^"))
            {
                return;
            }

            // fix all data
            var voteCacher = new VoteCacher(Db);

            var people = personCacher.AllForThisElection;
            var votes  = voteCacher.AllForThisElection;

            var peopleModel = new PeopleModel();
            var saveNeeded  = false;

            foreach (var person in people)
            {
                AutoFix(person, votes, peopleModel, ref saveNeeded);
            }

            if (saveNeeded)
            {
                Db.SaveChanges();

                new LogHelper().Add("Updated person combined infos");
            }

            personCacher.DropThisCache();
            voteCacher.DropThisCache();
        }
Ejemplo n.º 14
0
        public bool SortVotes(List <int> ids, VoteCacher voteCacher)
        {
            var ballotGuid = CurrentRawBallot().BallotGuid;

            var allVotes = voteCacher.AllForThisElection;

            var votes = allVotes.Where(v => v.BallotGuid == ballotGuid);

            var position = 1;

            foreach (var vote in ids.Select(id => votes.SingleOrDefault(v => v.C_RowId == id)).Where(vote => vote != null))
            {
                Db.Vote.Attach(vote);
                vote.PositionOnBallot = position;
                position++;
            }
            Db.SaveChanges();

            voteCacher.ReplaceEntireCache(allVotes);

            return(true);
        }
Ejemplo n.º 15
0
        public JsonResult DetailsFor(int personId)
        {
            var person = PeopleInElection.SingleOrDefault(p => p.C_RowId == personId);

            if (person == null)
            {
                return(new
                {
                    Error = "Unknown person"
                }.AsJsonResult());
            }

            //var whoCanVote = CurrentElection.CanVote;
            //var whoCanReceiveVotes = CurrentElection.CanReceive;
            var voteCacher = new VoteCacher(Db);
            var votedFor   = voteCacher.AllForThisElection.Any(v => v.PersonGuid == person.PersonGuid);

            return(new
            {
                Person = PersonForEdit(person),
                CanDelete = person.VotingMethod == null && !votedFor
            }.AsJsonResult());
        }
Ejemplo n.º 16
0
        public override JsonResult StartNewBallotJson()
        {
            if (UserSession.CurrentElectionStatus == ElectionTallyStatusEnum.Finalized)
            {
                return(new { Message = UserSession.FinalizedNoChangesMessage }.AsJsonResult());
            }

            // for single name, only one ballot per computer
            var ballots = new BallotCacher(Db).AllForThisElection;

            if (ballots.Any(b => b.ComputerCode == UserSession.CurrentComputerCode))
            {
                return(new { Message = "Only one 'ballot' per computer in single-name elections." }.AsJsonResult());
            }

            var locationModel = new LocationModel();

            if (locationModel.HasMultiplePhysicalLocations && UserSession.CurrentLocation == null)
            {
                return(new { Message = "Must select your location first!" }.AsJsonResult());
            }
            if (UserSession.GetCurrentTeller(1).HasNoContent())
            {
                return(new { Message = "Must select \"Teller at Keyboard\" first!" }.AsJsonResult());
            }

            var ballotInfo = CreateAndRegisterBallot();

            var allVotes = new VoteCacher(Db).AllForThisElection;

            return(new
            {
                BallotInfo = BallotInfoForJs(ballotInfo, allVotes),
                Ballots = CurrentBallotsInfoList()
            }.AsJsonResult());
        }
Ejemplo n.º 17
0
        public bool CreateBallotForOnlineVoter(List <OnlineRawVote> poolList, out string errorMessage)
        {
            //{Id: 0, First:"", Last:"", OtherInfo:""}

            // double check
            var numberToElect = UserSession.CurrentElection.NumberToElect;

            if (poolList.Count != numberToElect)
            {
                errorMessage = $"Invalid number of votes ({poolList.Count}). Need {numberToElect}.";
                return(false);
            }

            var ballotCacher   = new BallotCacher(Db);
            var voteCacher     = new VoteCacher(Db);
            var locationHelper = new LocationModel(Db);
            var location       = locationHelper.GetOnlineLocation();

            // create ballot
            var ballot = new Ballot
            {
                BallotGuid          = Guid.NewGuid(),
                LocationGuid        = location.LocationGuid,
                ComputerCode        = ComputerModel.ComputerCodeForOnline,
                BallotNumAtComputer = 0, // maxNum + 1, // will reset later
                StatusCode          = BallotStatusEnum.Empty,
            };

            Db.Ballot.Add(ballot);
            Db.SaveChanges();

            ballotCacher.UpdateItemAndSaveCache(ballot);

            // add Votes
            var nextVoteNum = 0;

            foreach (var rawVote in poolList)
            {
                Vote vote;
                if (rawVote.Id > 0)
                {
                    var person = new PersonCacher(Db).AllForThisElection.FirstOrDefault(b => b.C_RowId == rawVote.Id);
                    if (person == null)
                    {
                        errorMessage = $"Error converting pool id {rawVote.Id} to person.";
                        return(false);
                    }

                    vote = new Vote
                    {
                        BallotGuid              = ballot.BallotGuid,
                        PositionOnBallot        = ++nextVoteNum,
                        StatusCode              = VoteStatusCode.Ok,
                        PersonGuid              = person.PersonGuid,
                        PersonCombinedInfo      = person.CombinedInfo,
                        SingleNameElectionCount = 1, // okay if set for normal election too
                        InvalidReasonGuid       = person.CanReceiveVotes.AsBoolean(true) ? null : person.IneligibleReasonGuid
                    };
                }
                else
                {
                    // "random" vote
                    vote = new Vote
                    {
                        BallotGuid              = ballot.BallotGuid,
                        PositionOnBallot        = ++nextVoteNum,
                        StatusCode              = VoteStatusCode.OnlineRaw,
                        SingleNameElectionCount = 1,
                        OnlineVoteRaw           = JsonConvert.SerializeObject(rawVote),
                    };

                    // attempt to match if it is exact...
                    var matched = new PersonCacher(Db).AllForThisElection
                                  // match on first and last name only
                                  .Where(p => p.FirstName.ToLower() == rawVote.First.ToLower() && p.LastName.ToLower() == rawVote.Last.ToLower())
                                  // don't match if our list has "otherInfo" for this person - there might be some special considerations
                                  .Where(p => p.OtherInfo.HasNoContent())
                                  .ToList();

                    if (matched.Count == 1)
                    {
                        // found one exact match
                        var person = matched[0];
                        vote.StatusCode         = VoteStatusCode.Ok;
                        vote.PersonGuid         = person.PersonGuid;
                        vote.PersonCombinedInfo = person.CombinedInfo;
                        vote.InvalidReasonGuid  = person.CanReceiveVotes.AsBoolean(true) ? null : person.IneligibleReasonGuid;
                    }
                }

                Db.Vote.Add(vote);

                Db.SaveChanges();

                voteCacher.UpdateItemAndSaveCache(vote);
            }

            var votes = voteCacher.AllForThisElection;

            BallotAnalyzerLocal.UpdateBallotStatus(ballot, VoteInfosFor(ballot, votes), true);

            ballotCacher.UpdateItemAndSaveCache(ballot);
            Db.SaveChanges();

            errorMessage = "";
            return(true);
        }
Ejemplo n.º 18
0
        public JsonResult SaveVote(int personId, int voteId, Guid?invalidReason, int lastVid, int count, bool verifying)
        {
            if (UserSession.CurrentElectionStatus == ElectionTallyStatusEnum.Finalized)
            {
                return(new { Message = UserSession.FinalizedNoChangesMessage }.AsJsonResult());
            }
            var locationModel = new LocationModel();

            if (locationModel.HasMultiplePhysicalLocations && UserSession.CurrentLocation == null)
            {
                return(new { Message = "Must select your location first!" }.AsJsonResult());
            }
            if (UserSession.GetCurrentTeller(1).HasNoContent())
            {
                return(new { Message = "Must select \"Teller at Keyboard\" first!" }.AsJsonResult());
            }

            var isSingleName = UserSession.CurrentElection.IsSingleNameElection;

            var ballot = GetCurrentBallot();

            if (ballot == null)
            {
                // don't have an active Ballot!
                return(new { Updated = false, Error = "Invalid ballot" }.AsJsonResult());
            }

            Db.Ballot.Attach(ballot);

            var voteCacher   = new VoteCacher(Db);
            var personCacher = new PersonCacher(Db);

            if (voteId != 0)
            {
                // update existing record

                // find info about the existing Vote
                var vote = voteCacher.AllForThisElection.SingleOrDefault(v => v.C_RowId == voteId);

                if (vote == null)
                {
                    // problem... client has a vote number, but we didn't find...
                    return(new { Updated = false, Error = "Invalid vote id" }.AsJsonResult());
                }
                if (vote.BallotGuid != ballot.BallotGuid)
                {
                    // problem... client is focused on a different ballot!
                    return(new { Updated = false, Error = "Invalid vote/ballot id" }.AsJsonResult());
                }

                Db.Vote.Attach(vote);

                vote.SingleNameElectionCount = count;

                var person1 = personCacher.AllForThisElection.SingleOrDefault(p => p.C_RowId == personId);
                vote.PersonCombinedInfo = person1?.CombinedInfo;

                if (UserSession.CurrentLocation.IsVirtual)
                {
                    // changing person on an online ballot

                    if (person1 == null)
                    {
                        vote.PersonGuid = null;
                    }
                    else
                    {
                        vote.PersonGuid = person1.PersonGuid;
                        invalidReason   = person1.IneligibleReasonGuid;
                    }

                    vote.StatusCode = invalidReason == null ? VoteStatusCode.Ok : VoteStatusCode.Spoiled;
                }

                DetermineInvalidReasonGuid(invalidReason, vote);

                vote.StatusCode =
                    VoteAnalyzer.DetermineStatus(new VoteInfo(vote, UserSession.CurrentElection, ballot,
                                                              UserSession.CurrentLocation, person1));

                Db.SaveChanges();

                var votes = voteCacher.UpdateItemAndSaveCache(vote).AllForThisElection;

                var ballotStatusInfo = BallotAnalyzerLocal.UpdateBallotStatus(ballot, VoteInfosFor(ballot, votes), true);
                var sum = _helper.BallotCount(ballot.LocationGuid, isSingleName, null, votes);

                ballot.Teller1 = UserSession.GetCurrentTeller(1);
                ballot.Teller2 = UserSession.GetCurrentTeller(2);

                new BallotCacher(Db).UpdateItemAndSaveCache(ballot);
                Db.SaveChanges();

                var ballotCounts = isSingleName ? new VoteCacher().AllForThisElection
                                   .Where(v => v.BallotGuid == ballot.BallotGuid)
                                   .Sum(v => v.SingleNameElectionCount) : 0;
                var ballotCountNames = isSingleName ? new VoteCacher().AllForThisElection
                                       .Count(v => v.BallotGuid == ballot.BallotGuid) : 0;

                return(new
                {
                    Updated = true,
                    BallotStatus = ballotStatusInfo.Status.Value,
                    BallotStatusText = ballotStatusInfo.Status.DisplayText,
                    ballotStatusInfo.SpoiledCount,
                    LocationBallotsEntered = sum,
                    BallotId = ballot.C_RowId,
                    SingleBallotCount = ballotCounts,
                    SingleBallotNames = ballotCountNames,
                    VoteUpdates = GetVoteUpdates(lastVid, voteCacher, isSingleName, personCacher),
                    LastVid = vote.C_RowId,
                    vote.InvalidReasonGuid,
                    Name = person1?.C_FullName,
                    person1?.Area,
                    vote = CurrentVotesForJs(GetCurrentBallot(), new List <Vote> {
                        vote
                    }).First()
                }.AsJsonResult());
            }

            // make a new Vote record
            var location = new LocationCacher(Db).AllForThisElection.Single(l => l.LocationGuid == ballot.LocationGuid);

            if (location.IsVirtual)
            {
                return(new { Updated = false, Error = "Cannot add votes to an online ballot" }.AsJsonResult());
            }

            var invalidReasonGuid = DetermineInvalidReasonGuid(invalidReason);

            var person = personCacher.AllForThisElection.SingleOrDefault(p => p.C_RowId == personId);

            var ok = person != null || (invalidReason != null && invalidReasonGuid != Guid.Empty);

            if (ok)
            {
                var nextVoteNum = 1 + voteCacher.AllForThisElection.Where(v => v.BallotGuid == ballot.BallotGuid)
                                  .OrderByDescending(v => v.PositionOnBallot)
                                  .Take(1)
                                  .Select(b => b.PositionOnBallot)
                                  .SingleOrDefault();

                var vote = new Vote
                {
                    BallotGuid              = ballot.BallotGuid,
                    PositionOnBallot        = nextVoteNum,
                    StatusCode              = VoteStatusCode.Ok,
                    SingleNameElectionCount = count
                };
                if (person != null)
                {
                    vote.PersonGuid         = person.PersonGuid;
                    vote.PersonCombinedInfo = person.CombinedInfo;
                    vote.InvalidReasonGuid  = person.CanReceiveVotes.AsBoolean(true) ? null : person.IneligibleReasonGuid;
                    //          VoteHelperLocal.IneligibleToReceiveVotes(person.IneligibleReasonGuid,
                    //            person.CanReceiveVotes);
                }
                vote.InvalidReasonGuid = invalidReasonGuid;
                Db.Vote.Add(vote);
                Db.SaveChanges();

                var votes = voteCacher.UpdateItemAndSaveCache(vote).AllForThisElection;

                var rawBallot        = CurrentRawBallot();
                var ballotStatusInfo = BallotAnalyzerLocal.UpdateBallotStatus(rawBallot, VoteInfosFor(rawBallot, votes), true);

                var sum = _helper.BallotCount(ballot.LocationGuid, isSingleName, null, votes);

                new BallotCacher(Db).UpdateItemAndSaveCache(rawBallot);
                Db.SaveChanges();

                var ballotCounts = isSingleName ? new VoteCacher().AllForThisElection
                                   .Where(v => v.BallotGuid == ballot.BallotGuid)
                                   .Sum(v => v.SingleNameElectionCount) : 0;
                var ballotCountNames = isSingleName ? new VoteCacher().AllForThisElection
                                       .Count(v => v.BallotGuid == ballot.BallotGuid) : 0;

                return(new
                {
                    Updated = true,
                    VoteId = vote.C_RowId,
                    pos = vote.PositionOnBallot,
                    BallotStatus = ballotStatusInfo.Status.Value,
                    BallotStatusText = ballotStatusInfo.Status.DisplayText,
                    ballotStatusInfo.SpoiledCount,
                    BallotId = ballot.C_RowId,
                    LocationBallotsEntered = sum,
                    SingleBallotCount = ballotCounts,
                    SingleBallotNames = ballotCountNames,
                    VoteUpdates = GetVoteUpdates(lastVid, voteCacher, isSingleName, personCacher),
                    LastVid = vote.C_RowId
                }.AsJsonResult());
            }

            // don't recognize person id
            return(new { Updated = false, Error = "Invalid person. Please try again." }.AsJsonResult());
        }