Ejemplo n.º 1
0
        public static async Task Run(
            [TimerTrigger("%TitoSyncSchedule%")]
            TimerInfo timer,
            ILogger log,
            [BindConferenceConfig] ConferenceConfig conference,
            [BindKeyDatesConfig] KeyDatesConfig keyDates,
            [BindTitoSyncConfig] TitoSyncConfig config
            )
        {
            if (keyDates.After(x => x.StopSyncingTitoFromDate, TimeSpan.FromMinutes(10)))
            {
                log.LogInformation("TitoSync sync date passed");
                return;
            }

            var ids        = new List <string>();
            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", $"token={config.ApiKey}");

            var(tickets, hasMoreItems, nextPage) = await GetTicketsAsync(httpClient, config, log);

            if (tickets != null && tickets.Any())
            {
                ids.AddRange(tickets.Select(o => o.Id));
                log.LogInformation("Retrieved {ticketsCount} tickets from Tito.", tickets.Count());
            }

            while (hasMoreItems)
            {
                (tickets, hasMoreItems, nextPage) = await GetTicketsAsync(httpClient, config, log, nextPage.Value);

                if (tickets != null && tickets.Any())
                {
                    log.LogInformation("Found more {ticketsCount} tickets from Tito.", tickets.Count());
                    ids.AddRange(tickets.Select(o => o.Id));
                }
            }

            var repo = await config.GetRepositoryAsync();

            var existingTickets = await repo.GetAllAsync(conference.ConferenceInstance);

            // Taking up to 100 records to meet Azure Storage Bulk Operation limit
            var newTickets = ids.Except(existingTickets.Select(x => x.TicketId).ToArray()).Distinct().Take(100).ToArray();

            log.LogInformation(
                "Found {existingCount} existing tickets and {currentCount} current tickets. Inserting {newCount} new tickets.",
                existingTickets.Count, ids.Count, newTickets.Count());
            await repo.CreateBatchAsync(newTickets.Select(o => new TitoTicket(conference.ConferenceInstance, o))
                                        .ToArray());
        }
Ejemplo n.º 2
0
        public static async Task Run(
            [TimerTrigger("%TitoSyncSchedule%")]
            TimerInfo timer,
            ILogger log,
            [BindConferenceConfig] ConferenceConfig conference,
            [BindKeyDatesConfig] KeyDatesConfig keyDates,
            [BindTitoSyncConfig] TitoSyncConfig titoSyncConfig
            )
        {
            if (keyDates.After(x => x.StopSyncingTitoFromDate, TimeSpan.FromMinutes(10)))
            {
                log.LogInformation("TitoSync sync date passed");
                return;
            }

            var ids  = new List <string>();
            var http = new HttpClient();

            http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", $"token={titoSyncConfig.ApiKey}");

            var(registrations, hasMoreItems, nextPage) = await GetRegistrationsAsync(http,
                                                                                     $"https://api.tito.io/v3/{titoSyncConfig.AccountId}/{titoSyncConfig.EventId}/registrations/");

            ids.AddRange(registrations.Select(o => o.Id));

            while (hasMoreItems)
            {
                (registrations, hasMoreItems, nextPage) = await GetRegistrationsAsync(http,
                                                                                      $"https://api.tito.io/v3/{titoSyncConfig.AccountId}/{titoSyncConfig.EventId}/registrations?page={nextPage}");

                ids.AddRange(registrations.Select(o => o.Id));
            }

            var repo = await titoSyncConfig.GetRepositoryAsync();

            var existingOrders = await repo.GetAllAsync(conference.ConferenceInstance);

            // Taking up to 100 records to meet Azure Storage Bulk Operation limit
            var newOrders = ids.Except(existingOrders.Select(x => x.OrderId).ToArray()).Distinct().Take(100).ToArray();

            log.LogInformation(
                "Found {existingCount} existing orders and {currentCount} current orders. Inserting {newCount} new orders.",
                existingOrders.Count, ids.Count, newOrders.Count());
            await repo.CreateBatchAsync(newOrders.Select(o => new TitoOrder(conference.ConferenceInstance, o))
                                        .ToArray());
        }
Ejemplo n.º 3
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
            HttpRequest req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindSubmissionsConfig]
            SubmissionsConfig submissions,
            [BindVotingConfig]
            VotingConfig voting,
            [BindTitoSyncConfig]
            TitoSyncConfig tito,
            [BindAppInsightsSyncConfig]
            AppInsightsSyncConfig appInsights)
        {
            // Get submissions
            var(submissionsRepo, submittersRepo) = await submissions.GetRepositoryAsync();

            var receivedSubmissions = await submissionsRepo.GetAllAsync(conference.ConferenceInstance);

            var presenters = await submittersRepo.GetAllAsync(conference.ConferenceInstance);

            // Get votes
            var votingRepo = await voting.GetRepositoryAsync();

            var votes = await votingRepo.GetAllAsync(conference.ConferenceInstance);

            // Get Tito ids
            var ebRepo = await tito.GetRepositoryAsync();

            var titoTickets = await ebRepo.GetAllAsync(conference.ConferenceInstance);

            var titoIds = titoTickets.Select(o => o.TicketId).ToArray();

            // Get AppInsights sessions
            var aiRepo = await appInsights.GetRepositoryAsync();

            var userSessions = await aiRepo.GetAllAsync(conference.ConferenceInstance);

            // Analyse votes
            var analysedVotes = votes.Select(v => new AnalysedVote(v, votes, titoIds, userSessions)).ToArray();

            // Get summary
            var sessions = receivedSubmissions.Select(x => x.GetSession())
                           .Select(s => new SessionWithVotes
            {
                Id         = s.Id.ToString(),
                Title      = s.Title,
                Abstract   = s.Abstract,
                Format     = s.Format,
                Level      = s.Level,
                Tags       = s.Tags,
                Presenters = GetPresentersFromSession(presenters, s, ps => ps.Select(p =>
                                                                                     new Presenter
                {
                    Id              = p.Id.ToString(),
                    Name            = p.Name,
                    Tagline         = p.Tagline,
                    Bio             = p.Bio,
                    ProfilePhotoUrl = p.ProfilePhotoUrl,
                    TwitterHandle   = p.TwitterHandle,
                    WebsiteUrl      = p.WebsiteUrl
                }).ToArray()),
                CreatedDate          = s.CreatedDate,
                ModifiedDate         = s.ModifiedDate,
                IsUnderrepresented   = GetPresentersFromSession(presenters, s, IsUnderrepresented),
                Pronoun              = CollapsePresenterField(presenters, s, p => p.DataFields["Your pronoun"]),
                JobRole              = CollapsePresenterField(presenters, s, p => p.DataFields["How would you identify your job role"]),
                SpeakingExperience   = CollapsePresenterField(presenters, s, p => p.DataFields["How much speaking experience do you have?"]),
                VoteSummary          = new VoteSummary(analysedVotes.Where(v => v.Vote.GetSessionIds().Contains(s.Id.ToString())).ToArray()),
                FirstPreferenceCount = analysedVotes.Count(v => v.Vote.GetSessionIds()[0] == s.Id.ToString()),
                Top3PreferenceCount  = analysedVotes.Count(v => new [] { v.Vote.GetSessionIds()[0], v.Vote.GetSessionIds()[1], v.Vote.GetSessionIds()[2] }.Contains(s.Id.ToString()))
            })
                           .OrderBy(s => s.Title)
                           .ToArray();

            var tagSummaries = sessions.SelectMany(s => s.Tags).Distinct().OrderBy(t => t)
                               .Select(tag => new TagSummary
            {
                Tag         = tag,
                VoteSummary = new VoteSummary(sessions.Where(s => s.Tags.Contains(tag)).SelectMany(s => analysedVotes.Where(v => v.Vote.SessionIds.Contains(s.Id))).ToArray())
            }).ToArray();

            var response = new GetVotesResponse
            {
                VoteSummary  = new VoteSummary(analysedVotes),
                Sessions     = sessions.OrderByDescending(s => s.VoteSummary.RawTotal).ToArray(),
                TagSummaries = tagSummaries,
                Votes        = analysedVotes,
                UserSessions = userSessions.Select(x => new UserSession {
                    UserId = x.UserId, VoteId = x.VoteId, StartTime = x.StartTime
                }).ToArray()
            };
            var settings = new JsonSerializerSettings {
                ContractResolver = new DefaultContractResolver()
            };

            return(new JsonResult(response, settings));
        }
Ejemplo n.º 4
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]
            HttpRequestMessage req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindSubmissionsConfig]
            SubmissionsConfig submissions,
            [BindVotingConfig]
            VotingConfig voting,
            [BindTitoSyncConfig]
            TitoSyncConfig tickets
            )
        {
            var vote = await req.Content.ReadAsAsync <VoteRequest>();

            var ip = req.GetIpAddress();

            // Within voting window, allowing for 5 minutes of clock drift
            if (keyDates.Before(x => x.VotingAvailableFromDate) || keyDates.After(x => x.VotingAvailableToDate, TimeSpan.FromMinutes(5)))
            {
                log.LogWarning("Attempt to access SubmitVote endpoint outside of allowed window of {start} -> {end}.", keyDates.VotingAvailableFromDate, keyDates.VotingAvailableToDate);
                return(new StatusCodeResult((int)HttpStatusCode.NotFound));
            }

            // Correct number of votes
            var numVotesSubmitted = vote.SessionIds?.Length ?? 0;

            if (numVotesSubmitted < conference.MinVotes || numVotesSubmitted > conference.MaxVotes)
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint with incorrect number of votes ({numVotes} rather than {minVotes} - {maxVotes}).", numVotesSubmitted, conference.MinVotes, conference.MaxVotes);
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            // Correct number of indices
            if (numVotesSubmitted != vote.Indices?.Length)
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint without matching indices ({numIndices} vs {numVotes}).", vote.Indices?.Length, numVotesSubmitted);
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            // Valid voting start time, allowing for 5 minutes of clock drift
            if (vote.VotingStartTime > keyDates.Now.AddMinutes(5) || vote.VotingStartTime < keyDates.VotingAvailableFromDate.AddMinutes(-5))
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint with invalid start time (got {submittedStartTime} instead of {votingStartTime} - {now}).", vote.VotingStartTime, keyDates.VotingAvailableFromDate, keyDates.Now);
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            if (voting.TicketNumberWhileVotingValue == TicketNumberWhileVoting.Required)
            {
                // Get tickets
                var ticketsRepo = await tickets.GetRepositoryAsync();

                var matchedTicket = await ticketsRepo.GetAsync(conference.ConferenceInstance,
                                                               vote.TicketNumber.ToUpperInvariant());

                // Only if you have a valid ticket
                if (string.IsNullOrEmpty(vote.TicketNumber) || matchedTicket == null)
                {
                    log.LogWarning(
                        "Attempt to submit to SubmitVote endpoint without a valid ticket. Ticket id sent was {ticketNumber}",
                        vote.TicketNumber);
                    return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
                }
            }

            // Get submitted sessions
            var(submissionsRepo, _) = await submissions.GetRepositoryAsync();

            var allSubmissions = await submissionsRepo.GetAllAsync(conference.ConferenceInstance);

            var allSubmissionIds = allSubmissions.Where(s => s.Session != null).Select(s => s.Id.ToString()).ToArray();

            // Valid session ids
            if (vote.SessionIds.Any(id => !allSubmissionIds.Contains(id)) || vote.SessionIds.Distinct().Count() != vote.SessionIds.Count())
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint with at least one invalid or duplicate submission id (got {sessionIds}).", JsonConvert.SerializeObject(vote.SessionIds));
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            // Valid indices
            if (vote.Indices.Any(index => index <= 0 || index > allSubmissionIds.Count()) || vote.Indices.Distinct().Count() != vote.Indices.Count())
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint with at least one invalid or duplicate index (got {indices} when the number of sessions is {totalNumberOfSessions}).", JsonConvert.SerializeObject(vote.Indices), allSubmissionIds.Count());
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            // No existing vote
            var repo = await voting.GetRepositoryAsync();

            var existing = await repo.GetAsync(conference.ConferenceInstance, vote.Id.ToString());

            if (existing != null)
            {
                log.LogWarning("Attempt to submit to SubmitVotes endpoint with a duplicate vote (got {voteId}).", vote.Id);
                return(new StatusCodeResult((int)HttpStatusCode.Conflict));
            }

            // Save vote
            log.LogInformation("Successfully received vote with Id {voteId}; persisting...", vote.Id);
            var voteToPersist = new Vote(conference.ConferenceInstance, vote.Id, vote.SessionIds, vote.Indices, vote.TicketNumber?.ToUpperInvariant(), ip, vote.VoterSessionId, vote.VotingStartTime, keyDates.Now);            await repo.CreateAsync(voteToPersist);

            return(new StatusCodeResult((int)HttpStatusCode.NoContent));
        }
Ejemplo n.º 5
0
        private static async Task <(Ticket[], bool, int?)> GetTicketsAsync(HttpClient httpClient, TitoSyncConfig config, ILogger log, int pageNumber = 1)
        {
            var titoUrl  = $"https://api.tito.io/v3/{config.AccountId}/{config.EventId}/tickets?page={pageNumber}";
            var response = await httpClient.GetAsync(titoUrl);

            if (response.IsSuccessStatusCode)
            {
                try
                {
                    var formatters = new MediaTypeFormatterCollection();
                    formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/vnd.api+json"));
                    var content = await response.Content.ReadAsAsync <PaginatedTitoTicketsResponse>(formatters);

                    return(content.Tickets, content.Meta.HasMoreItems, content.Meta.NextPage);
                }
                catch (Exception ex)
                {
                    log.LogCritical("Error reading Tito response.", ex);
                }
            }
            else
            {
                log.LogCritical("Error connecting to Tito with http response: {reason}. The dump of the response: ", response.StatusCode, response.Content.ReadAsStringAsync());
            }
            return(null, false, null);
        }