Esempio n. 1
0
        public async Task CanGetNumbersAsync()
        {
            // capture start date before generating data to make sure that our time range for stats includes all items
            var       startDate  = DateTime.UtcNow.SubtractDays(60);
            const int eventCount = 100;

            await RemoveDataAsync();
            await CreateDataAsync(eventCount, false);

            var fields = FieldAggregationProcessor.Process("distinct:stack_id,term:is_first_occurrence:-F", false);

            Assert.True(fields.IsValid);
            Assert.Equal(2, fields.Aggregations.Count);

            var result = await _stats.GetNumbersTimelineStatsAsync(fields.Aggregations, startDate, DateTime.UtcNow, null, userFilter : $"project:{TestConstants.ProjectId}");

            Assert.Equal(eventCount, result.Total);
            Assert.Equal(eventCount, result.Timeline.Sum(t => t.Total));
            Assert.Equal(2, result.Numbers.Length);
            Assert.Equal(await _stackRepository.CountAsync(), result.Numbers[0]);
            Assert.Equal(await _stackRepository.CountAsync(), result.Timeline.Sum(t => t.Numbers[1]));

            var stacks = await _stackRepository.GetByOrganizationIdAsync(TestConstants.OrganizationId, new PagingOptions().WithLimit(100));

            foreach (var stack in stacks.Documents)
            {
                var nsr = await _stats.GetNumbersStatsAsync(fields.Aggregations, startDate, DateTime.UtcNow, null, userFilter : "stack:" + stack.Id);

                Assert.Equal(stack.TotalOccurrences, nsr.Total);
            }
        }
Esempio n. 2
0
        public async Task <IHttpActionResult> GetAsync(string fields = null, string filter = null, string time = null, string offset = null)
        {
            var far = FieldAggregationProcessor.Process(fields);

            if (!far.IsValid)
            {
                return(BadRequest(far.Message));
            }

            var pr = QueryProcessor.Process(filter);

            if (!pr.IsValid)
            {
                return(BadRequest(pr.Message));
            }

            var organizations = await GetAssociatedActiveOrganizationsAsync(_organizationRepository);

            if (organizations.Count == 0)
            {
                return(Ok(NumbersStatsResult.Empty));
            }

            var ti = GetTimeInfo(time, offset, organizations.GetRetentionUtcCutoff());
            var sf = new ExceptionlessSystemFilterQuery(organizations)
            {
                UsesPremiumFeatures       = far.UsesPremiumFeatures || pr.UsesPremiumFeatures,
                IsUserOrganizationsFilter = true
            };

            NumbersStatsResult result;

            try {
                result = await _stats.GetNumbersStatsAsync(far.Aggregations, ti.UtcRange.Start, ti.UtcRange.End, ShouldApplySystemFilter(sf, filter)?sf : null, pr.ExpandedQuery, ti.Offset);
            } catch (ApplicationException ex) {
                _logger.Error().Exception(ex)
                .Message("An error has occurred. Please check your search filter.")
                .Property("Search Filter", new { SystemFilter = sf, UserFilter = filter, Time = time, Offset = offset })
                .Tag("Search")
                .Identity(ExceptionlessUser.EmailAddress)
                .Property("User", ExceptionlessUser)
                .SetActionContext(ActionContext).Write();

                return(BadRequest("An error has occurred. Please check your search filter."));
            }

            return(Ok(result));
        }
Esempio n. 3
0
        public async Task <IHttpActionResult> GetAsync(string fields = null, string filter = null, string time = null, string offset = null)
        {
            var far = FieldAggregationProcessor.Process(fields);

            if (!far.IsValid)
            {
                return(BadRequest(far.Message));
            }

            var processResult = QueryProcessor.Process(filter);

            if (!processResult.IsValid)
            {
                return(BadRequest(processResult.Message));
            }

            string systemFilter = await GetAssociatedOrganizationsFilterAsync(_organizationRepository, far.UsesPremiumFeatures || processResult.UsesPremiumFeatures, HasOrganizationOrProjectFilter(filter));

            NumbersStatsResult result;

            try {
                var timeInfo = GetTimeInfo(time, offset);
                result = await _stats.GetNumbersStatsAsync(far.Aggregations, timeInfo.UtcRange.Start, timeInfo.UtcRange.End, systemFilter, processResult.ExpandedQuery, timeInfo.Offset);
            } catch (ApplicationException ex) {
                _logger.Error().Exception(ex).Property("Search Filter", new {
                    SystemFilter = systemFilter,
                    UserFilter   = filter,
                    Time         = time,
                    Offset       = offset
                }).Tag("Search").Identity(ExceptionlessUser.EmailAddress).Property("User", ExceptionlessUser).SetActionContext(ActionContext).Write();

                return(BadRequest("An error has occurred. Please check your search filter."));
            }

            return(Ok(result));
        }
Esempio n. 4
0
        private async Task ProcessSummaryNotificationAsync(SummaryNotification data)
        {
            var project = await _projectRepository.GetByIdAsync(data.Id, true).AnyContext();

            var organization = await _organizationRepository.GetByIdAsync(project.OrganizationId, true).AnyContext();

            var userIds = project.NotificationSettings.Where(n => n.Value.SendDailySummary).Select(n => n.Key).ToList();

            if (userIds.Count == 0)
            {
                _logger.Info("Project \"{0}\" has no users to send summary to.", project.Id);
                return;
            }

            var users = (await _userRepository.GetByIdsAsync(userIds).AnyContext()).Documents.Where(u => u.IsEmailAddressVerified && u.EmailNotificationsEnabled && u.OrganizationIds.Contains(organization.Id)).ToList();

            if (users.Count == 0)
            {
                _logger.Info("Project \"{0}\" has no users to send summary to.", project.Id);
                return;
            }

            _logger.Info("Sending daily summary: users={0} project={1}", users.Count, project.Id);

            var fields = new List <FieldAggregation> {
                new FieldAggregation {
                    Type = FieldAggregationType.Distinct, Field = "stack_id"
                },
                new TermFieldAggregation {
                    Field = "is_first_occurrence", ExcludePattern = "F"
                }
            };

            var result = await _stats.GetNumbersStatsAsync(fields, data.UtcStartTime, data.UtcEndTime, $"project:{data.Id} type:error").AnyContext();

            bool hasSubmittedEvents = result.Total > 0;

            if (!hasSubmittedEvents)
            {
                hasSubmittedEvents = await _eventRepository.GetCountByProjectIdAsync(project.Id).AnyContext() > 0;
            }

            var notification = new DailySummaryModel {
                ProjectId          = project.Id,
                ProjectName        = project.Name,
                StartDate          = data.UtcStartTime,
                EndDate            = data.UtcEndTime,
                Total              = result.Total,
                PerHourAverage     = result.Total / data.UtcEndTime.Subtract(data.UtcStartTime).TotalHours,
                NewTotal           = result.Numbers[1],
                UniqueTotal        = result.Numbers[0],
                HasSubmittedEvents = hasSubmittedEvents,
                IsFreePlan         = organization.PlanId == BillingManager.FreePlan.Id
            };

            foreach (var user in users)
            {
                await _mailer.SendDailySummaryAsync(user.EmailAddress, notification).AnyContext();
            }

            _logger.Info("Done sending daily summary: users={0} project={1} events={2}", users.Count, project.Id, notification.Total);
        }
Esempio n. 5
0
        private async Task <bool> SendSummaryNotificationAsync(Project project, SummaryNotification data)
        {
            var userIds = project.NotificationSettings.Where(n => n.Value.SendDailySummary).Select(n => n.Key).ToList();

            if (userIds.Count == 0)
            {
                _logger.Info().Project(project.Id).Message("Project \"{0}\" has no users to send summary to.", project.Name).Write();
                return(false);
            }

            var results = await _userRepository.GetByIdsAsync(userIds, true).AnyContext();

            var users = results.Where(u => u.IsEmailAddressVerified && u.EmailNotificationsEnabled && u.OrganizationIds.Contains(project.OrganizationId)).ToList();

            if (users.Count == 0)
            {
                _logger.Info().Project(project.Id).Message("Project \"{0}\" has no users to send summary to.", project.Name);
                return(false);
            }

            // TODO: What should we do about suspended organizations.
            var organization = await _organizationRepository.GetByIdAsync(project.OrganizationId, true).AnyContext();

            if (organization == null)
            {
                _logger.Info().Project(project.Id).Message("The organization \"{0}\" for project \"{1}\" may have been deleted. No summaries will be sent.", project.OrganizationId, project.Name);
                return(false);
            }

            _logger.Info("Sending daily summary: users={0} project={1}", users.Count, project.Id);
            var fields = new List <FieldAggregation> {
                new FieldAggregation {
                    Type = FieldAggregationType.Distinct, Field = "stack_id"
                },
                new TermFieldAggregation {
                    Field = "is_first_occurrence", ExcludePattern = "F"
                }
            };

            var sf     = new ExceptionlessSystemFilterQuery(project, organization);
            var result = await _stats.GetNumbersStatsAsync(fields, data.UtcStartTime, data.UtcEndTime, sf, $"{EventIndexType.Fields.Type}:{Event.KnownTypes.Error}").AnyContext();

            bool hasSubmittedEvents = result.Total > 0;

            if (!hasSubmittedEvents)
            {
                hasSubmittedEvents = await _eventRepository.GetCountByProjectIdAsync(project.Id).AnyContext() > 0;
            }

            var notification = new DailySummaryModel {
                ProjectId          = project.Id,
                ProjectName        = project.Name,
                StartDate          = data.UtcStartTime,
                EndDate            = data.UtcEndTime,
                Total              = result.Total,
                PerHourAverage     = result.Total / data.UtcEndTime.Subtract(data.UtcStartTime).TotalHours,
                NewTotal           = result.Numbers[1],
                UniqueTotal        = result.Numbers[0],
                HasSubmittedEvents = hasSubmittedEvents,
                IsFreePlan         = organization.PlanId == BillingManager.FreePlan.Id
            };

            foreach (var user in users)
            {
                await _mailer.SendDailySummaryAsync(user.EmailAddress, notification).AnyContext();
            }

            _logger.Info().Project(project.Id).Message("Done sending daily summary: users={0} project={1} events={2}", users.Count, project.Name, notification.Total);
            return(true);
        }