public static async Task Run(
            [TimerTrigger("%SessionizeReadModelSyncSchedule%")]
            TimerInfo timer,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindSessionsConfig]
            SessionsConfig sessions,
            [BindSessionizeSyncConfig]
            SessionizeSyncConfig sessionize
            )
        {
            if (keyDates.Before(x => x.StopSyncingSessionsFromDate) || keyDates.After(x => x.StopSyncingAgendaFromDate))
            {
                log.LogInformation("SessionizeAgendaSync sync not active");
                return;
            }

            using (var httpClient = new HttpClient())
            {
                var apiClient = new SessionizeApiClient(httpClient, sessionize.AgendaApiKey);
                var(sessionsRepo, presentersRepo) = await sessions.GetRepositoryAsync();

                await SyncService.Sync(apiClient, sessionsRepo, presentersRepo, log, new DateTimeProvider(), conference.ConferenceInstance);
            }
        }
Пример #2
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindSubmissionsConfig]
            SubmissionsConfig submissions
            )
        {
            if (keyDates.Before(x => x.SubmissionsAvailableFromDate) || keyDates.After(x => x.SubmissionsAvailableToDate))
            {
                log.LogWarning("Attempt to access GetSubmissions endpoint outside of allowed window of {start} -> {end}.", keyDates.SubmissionsAvailableFromDate, keyDates.SubmissionsAvailableToDate);
                return(new StatusCodeResult(404));
            }

            var(submissionsRepo, submittersRepo) = await submissions.GetRepositoryAsync();

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

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

            var submissionData = receivedSubmissions.Where(x => x.Session != null)
                                 .Select(x => x.GetSession())
                                 .Select(s => new Submission
            {
                Id         = s.Id.ToString(),
                Title      = s.Title,
                Abstract   = s.Abstract,
                Format     = s.Format,
                Level      = s.Level,
                Tags       = s.Tags,
                Presenters = conference.AnonymousSubmissions
                        ? new Submitter[0]
                        : s.PresenterIds.Select(pId => submitters.Where(p => p.Id == pId).Select(p => p.GetPresenter()).Select(p => new Submitter
                {
                    Id              = p.Id.ToString(),
                    Name            = p.Name,
                    Tagline         = p.Tagline,
                    Bio             = p.Bio,
                    ProfilePhotoUrl = p.ProfilePhotoUrl,
                    TwitterHandle   = p.TwitterHandle,
                    WebsiteUrl      = p.WebsiteUrl
                }).Single()).ToArray()
            })
                                 .OrderBy(x => Random.Next())
                                 .ToArray();

            var settings = new JsonSerializerSettings();

            settings.ContractResolver = new DefaultContractResolver();

            return(new JsonResult(submissionData, settings));
        }
Пример #3
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindSessionsConfig]
            SessionsConfig sessionsConfig)
        {
            if (keyDates.Before(x => x.SubmissionsAvailableToDate))
            {
                log.LogWarning("Attempt to access GetAgenda endpoint before they are available at {availableDate}.", keyDates.SubmissionsAvailableToDate);
                return(new StatusCodeResult(404));
            }

            var(sessionsRepo, presentersRepo) = await sessionsConfig.GetRepositoryAsync();

            var sessions = await sessionsRepo.GetAllAsync(conference.ConferenceInstance);

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

            var agenda = sessions.Select(x => x.GetSession())
                         .Select(s => new Session
            {
                Id         = s.Id.ToString(),
                Title      = s.Title,
                Abstract   = s.Abstract,
                Format     = s.Format,
                Level      = s.Level,
                Tags       = s.Tags,
                Presenters = s.PresenterIds.Select(pId => presenters.Where(p => p.Id == pId).Select(p => p.GetPresenter()).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
                }).Single()).ToArray()
            })
                         .OrderBy(x => x.Title)
                         .ToArray();

            var settings = new JsonSerializerSettings();

            settings.ContractResolver = new DefaultContractResolver();

            return(new JsonResult(agenda, settings));
        }
Пример #4
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());
        }
Пример #5
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());
        }
Пример #6
0
        public static async Task Run(
            [TimerTrigger("%AppInsightsSyncSchedule%")]
            TimerInfo timer,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindAppInsightsSyncConfig]
            AppInsightsSyncConfig appInsights,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates
            )
        {
            if (keyDates.Before(x => x.StartSyncingAppInsightsFromDate) || keyDates.After(x => x.StopSyncingAppInsightsFromDate, TimeSpan.FromMinutes(10)))
            {
                log.LogInformation("AppInsightsSync sync not active");
                return;
            }

            var http = new HttpClient();

            http.DefaultRequestHeaders.Add("x-api-key", appInsights.ApplicationKey);

            var response = await http.GetAsync($"https://api.applicationinsights.io/v1/apps/{appInsights.ApplicationId}/query?timespan={WebUtility.UrlEncode(keyDates.StartSyncingAppInsightsFrom)}%2F{WebUtility.UrlEncode(keyDates.StopSyncingAppInsightsFrom)}&query={WebUtility.UrlEncode(VotingUserQuery.Query)}");

            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsAsync <AppInsightsQueryResponse <VotingUserQuery> >();

            var currentRecords = content.Data.Select(x => new AppInsightsVotingUser(conference.ConferenceInstance, x.UserId, x.VoteId, x.StartTime)).ToArray();

            var repo = await appInsights.GetRepositoryAsync();

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

            // Taking up to 100 records to meet Azure Storage Bulk Operation limit
            var newRecords = currentRecords.Except(existingRecords, new AppInsightsVotingUserComparer()).Take(100).ToArray();

            log.LogInformation("Found {existingCount} existing app insights voting users and {currentCount} current app insights voting users. Inserting {newCount} new orders.", existingRecords.Count, currentRecords.Length, newRecords.Length);
            await repo.CreateBatchAsync(newRecords);
        }
Пример #7
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));
        }
Пример #8
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindSubmissionsConfig]
            SubmissionsConfig submissions
            )
        {
            if (keyDates.Before(x => x.SubmissionsAvailableFromDate) || keyDates.After(x => x.SubmissionsAvailableToDate))
            {
                log.LogWarning("Attempt to access GetSubmissions endpoint outside of allowed window of {start} -> {end}.", keyDates.SubmissionsAvailableFromDate, keyDates.SubmissionsAvailableToDate);
                return(new StatusCodeResult(404));
            }

            var(submissionsRepo, submittersRepo) = await submissions.GetRepositoryAsync();

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

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

            var submissionData = receivedSubmissions.Where(x => x.Session != null)
                                 .Select(x => x.GetSession())
                                 .Select(s =>
            {
                var otherTagsKeys   = s.DataFields.Keys.Where(k => SessionizeAdapter.TagsTitles.Contains(k)).ToList();
                var otherTagsValues = new List <string>();
                foreach (var key in otherTagsKeys)
                {
                    var found = s.DataFields.TryGetValue(key, out var value);
                    if (found)
                    {
                        otherTagsValues.AddRange(value.Split(',').Select(v => v.Trim()).ToList());
                    }
                }

                var tags = otherTagsValues.Union(s.Tags, StringComparer.InvariantCultureIgnoreCase).ToArray();
                return(new Submission
                {
                    Id = s.Id.ToString(),
                    Title = s.Title,
                    Abstract = s.Abstract,
                    Format = s.Format,
                    Level = s.Level,
                    Tags = tags,
                    Presenters = conference.AnonymousSubmissions
                            ? new Submitter[0]
                            : s.PresenterIds.Select(pId => submitters.Where(p => p.Id == pId)
                                                    .Select(p => p.GetPresenter()).Select(p => new Submitter
                    {
                        Id = p.Id.ToString(),
                        Name = AnonymiseName(p.Name),
                        Tagline = p.Tagline,
                        Bio = p.Bio,
                        ProfilePhotoUrl = p.ProfilePhotoUrl,
                        TwitterHandle = p.TwitterHandle,
                        WebsiteUrl = p.WebsiteUrl
                    }).Single()).ToArray()
                });
            })
                                 .OrderBy(x => Random.Next())
                                 .ToArray();

            var settings = new JsonSerializerSettings();

            settings.ContractResolver = new DefaultContractResolver();

            return(new JsonResult(submissionData, settings));
        }
Пример #9
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]
            HttpRequestMessage req,
            ILogger log,
            [BindConferenceConfig]
            ConferenceConfig conference,
            [BindKeyDatesConfig]
            KeyDatesConfig keyDates,
            [BindFeedbackConfig]
            FeedbackConfig feedbackConfig,
            [BindSessionsConfig]
            SessionsConfig sessionsConfig
            )
        {
            var feedback = await req.Content.ReadAsAsync <FeedbackRequest>();

            // Within feedback window
            if (keyDates.Before(x => x.FeedbackAvailableFromDate) || keyDates.After(x => x.FeedbackAvailableToDate))
            {
                log.LogWarning("Attempt to access SubmitFeedback endpoint outside of allowed window of {start} -> {end}.", keyDates.FeedbackAvailableFromDate, keyDates.FeedbackAvailableToDate);
                return(new StatusCodeResult((int)HttpStatusCode.NotFound));
            }

            // Valid feedback
            if (string.IsNullOrWhiteSpace(feedback.Likes) && string.IsNullOrWhiteSpace(feedback.ImprovementIdeas))
            {
                log.LogWarning("Attempt to access SubmitFeedback endpoint with invalid feedback details from {DeviceId}", feedback.DeviceId);
                return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
            }

            var(conferenceFeedbackRepo, sessionFeedbackRepo) = await feedbackConfig.GetRepositoryAsync();

            if (feedback.IsConferenceFeedback)
            {
                // Save conference feedback
                log.LogInformation("Successfully received conference feedback for from DeviceId {DeviceId}; persisting...", feedback.DeviceId);
                var feedbackToPersist = new ConferenceFeedbackEntity(
                    conference.ConferenceInstance,
                    feedback.Name,
                    feedback.Rating.ToString(),
                    feedback.Likes,
                    feedback.ImprovementIdeas,
                    feedback.DeviceId.ToString());

                await conferenceFeedbackRepo.CreateAsync(feedbackToPersist);
            }
            else
            {
                // valid Session existed
                var(sessionsRepo, presentersRepo) = await sessionsConfig.GetRepositoryAsync();

                var allSessions = await sessionsRepo.GetAllAsync(conference.ConferenceInstance);

                var allPresenters = await presentersRepo.GetAllAsync(conference.ConferenceInstance);

                var sessionRow = allSessions.First(s => s != null && feedback.SessionId == s.Id.ToString());
                if (sessionRow == null && !feedback.IsConferenceFeedback)
                {
                    log.LogWarning("Attempt to submit to SubmitFeedback endpoint with invalid SessionId {SessionId} from {DeviceId}", feedback.SessionId, feedback.DeviceId);
                    return(new StatusCodeResult((int)HttpStatusCode.BadRequest));
                }

                var session        = sessionRow.GetSession();
                var presenterNames = string.Join(", ", session.PresenterIds.Select(pId => allPresenters.Single(p => p.Id == pId)).Select(x => x.Name));

                // Save feedback
                log.LogInformation("Successfully received feedback for {SessionId} from DeviceId {DeviceId}; persisting...", feedback.SessionId, feedback.DeviceId);
                var feedbackToPersist = new SessionFeedbackEntity(
                    session.Id.ToString(),
                    conference.ConferenceInstance,
                    feedback.Name,
                    feedback.Rating.ToString(),
                    feedback.Likes,
                    feedback.ImprovementIdeas,
                    $"{session.Title} - {presenterNames}",
                    feedback.DeviceId.ToString());

                await sessionFeedbackRepo.CreateAsync(feedbackToPersist);
            }
            return(new StatusCodeResult((int)HttpStatusCode.NoContent));
        }