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

            await CreateDataAsync(eventCount, false);

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

            Assert.True(fields.IsValid);

            var sf     = new ExceptionlessSystemFilterQuery(ProjectData.GenerateSampleProject(), OrganizationData.GenerateSampleOrganization());
            var result = await _stats.GetNumbersTermsStatsAsync("stack_id", fields.Aggregations, startDate, SystemClock.UtcNow, sf);

            Assert.Equal(eventCount, result.Total);
            Assert.InRange(result.Terms.Count, 1, 25);
            // TODO: Figure out why this is less than eventCount
            Assert.Equal(eventCount, result.Terms.Sum(t => t.Total));
            Assert.InRange(result.Terms.Sum(t => t.Numbers[1]), 1, 25); // new
            foreach (var term in result.Terms)
            {
                Assert.Equal(1, term.Numbers[0]); //unique
                Assert.Equal(1, term.Numbers[1]); // new
            }
        }
示例#2
0
        private async Task <List <ViewOrganization> > PopulateOrganizationStatsAsync(List <ViewOrganization> viewOrganizations)
        {
            if (viewOrganizations.Count <= 0)
            {
                return(viewOrganizations);
            }

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

            var organizations = viewOrganizations.Select(o => new Organization {
                Id = o.Id, RetentionDays = o.RetentionDays
            }).ToList();
            var sf     = new ExceptionlessSystemFilterQuery(organizations);
            var result = await _stats.GetNumbersTermsStatsAsync("organization_id", fields, organizations.GetRetentionUtcCutoff(), DateTime.MaxValue, sf, max : viewOrganizations.Count);

            foreach (var organization in viewOrganizations)
            {
                var organizationStats = result.Terms.FirstOrDefault(t => t.Term == organization.Id);
                organization.EventCount   = organizationStats?.Total ?? 0;
                organization.StackCount   = (long)(organizationStats?.Numbers[0] ?? 0);
                organization.ProjectCount = await _projectRepository.GetCountByOrganizationIdAsync(organization.Id);
            }

            return(viewOrganizations);
        }
示例#3
0
        public async Task CanGetEventTermStatsByTagAsync()
        {
            // capture start date before generating data to make sure that our time range for stats includes all items
            var       startDate  = SystemClock.UtcNow.SubtractDays(60);
            const int eventCount = 100;

            await CreateDataAsync(eventCount, false);

            Log.MinimumLevel = LogLevel.Trace;
            var fields = FieldAggregationProcessor.Process("term:is_first_occurrence:-F", false);

            Assert.True(fields.IsValid);

            var sf     = new ExceptionlessSystemFilterQuery(ProjectData.GenerateSampleProject(), OrganizationData.GenerateSampleOrganization());
            var result = await _stats.GetNumbersTermsStatsAsync("tags", fields.Aggregations, startDate, SystemClock.UtcNow, sf, "fixed:false");

            Assert.Equal(eventCount, result.Total);
            // each event can be in multiple tag buckets since an event can have up to 3 sample tags
            Assert.InRange(result.Terms.Sum(t => t.Total), eventCount, eventCount * 3);
            Assert.InRange(result.Terms.Sum(t => t.Numbers[0]), 1, 25 * TestConstants.EventTags.Count); // new
            Assert.InRange(result.Terms.Count, 1, TestConstants.EventTags.Count);
            foreach (var term in result.Terms)
            {
                Assert.InRange(term.Numbers[0], 1, 25); // new
            }
        }
        private async Task <List <ViewProject> > PopulateProjectStatsAsync(List <ViewProject> viewProjects)
        {
            if (viewProjects.Count <= 0)
            {
                return(viewProjects);
            }

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

            var organizations = await _organizationRepository.GetByIdsAsync(viewProjects.Select(p => p.OrganizationId).ToArray(), true);

            var projects = viewProjects.Select(p => new Project {
                Id = p.Id, OrganizationId = p.OrganizationId
            }).ToList();
            var sf   = new ExceptionlessSystemFilterQuery(projects, organizations);
            var ntsr = await _stats.GetNumbersTermsStatsAsync("project_id", fields, organizations.GetRetentionUtcCutoff(), DateTime.MaxValue, sf, max : viewProjects.Count);

            foreach (var project in viewProjects)
            {
                var term = ntsr.Terms.FirstOrDefault(t => t.Term == project.Id);
                project.EventCount = term?.Total ?? 0;
                project.StackCount = (long)(term?.Numbers[0] ?? 0);
            }

            return(viewProjects);
        }
        private async Task <List <ViewProject> > PopulateProjectStatsAsync(List <ViewProject> viewProjects)
        {
            if (viewProjects.Count <= 0)
            {
                return(viewProjects);
            }

            var organizations = await _organizationRepository.GetByIdsAsync(viewProjects.Select(p => p.OrganizationId).ToArray(), true);

            var projects = viewProjects.Select(p => new Project {
                Id = p.Id, CreatedUtc = p.CreatedUtc, OrganizationId = p.OrganizationId
            }).ToList();
            var sf           = new ExceptionlessSystemFilterQuery(projects, organizations);
            var systemFilter = new ElasticQuery().WithSystemFilter(sf).WithDateRange(organizations.GetRetentionUtcCutoff(), SystemClock.UtcNow, (PersistentEvent e) => e.Date).WithIndexes(organizations.GetRetentionUtcCutoff(), SystemClock.UtcNow);
            var result       = await _eventRepository.CountBySearchAsync(systemFilter, null, $"terms:(project_id~{viewProjects.Count} cardinality:stack_id)");

            foreach (var project in viewProjects)
            {
                var term = result.Aggregations.Terms <string>("terms_project_id")?.Buckets.FirstOrDefault(t => t.Key == project.Id);
                project.EventCount = term?.Total ?? 0;
                project.StackCount = (long)(term?.Aggregations.Cardinality("cardinality_stack_id")?.Value ?? 0);
            }

            return(viewProjects);
        }
        public async Task <IHttpActionResult> GetTimelineAsync(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(NumbersTimelineStatsResult.Empty));
            }

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

            NumbersTimelineStatsResult result;

            try {
                result = await _stats.GetNumbersTimelineStatsAsync(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));
        }
示例#7
0
        private async Task <List <ViewOrganization> > PopulateOrganizationStatsAsync(List <ViewOrganization> viewOrganizations)
        {
            if (viewOrganizations.Count <= 0)
            {
                return(viewOrganizations);
            }

            var organizations = viewOrganizations.Select(o => new Organization {
                Id = o.Id, CreatedUtc = o.CreatedUtc, RetentionDays = o.RetentionDays
            }).ToList();
            var sf           = new ExceptionlessSystemFilterQuery(organizations);
            var systemFilter = new ElasticQuery().WithSystemFilter(sf).WithDateRange(organizations.GetRetentionUtcCutoff(), SystemClock.UtcNow, (PersistentEvent e) => e.Date).WithIndexes(organizations.GetRetentionUtcCutoff(), SystemClock.UtcNow);
            var result       = await _eventRepository.CountBySearchAsync(systemFilter, null, $"terms:(organization_id~{viewOrganizations.Count} cardinality:stack_id)");

            foreach (var organization in viewOrganizations)
            {
                var organizationStats = result.Aggregations.Terms <string>("terms_organization_id")?.Buckets.FirstOrDefault(t => t.Key == organization.Id);
                organization.EventCount   = organizationStats?.Total ?? 0;
                organization.StackCount   = (long?)organizationStats?.Aggregations.Cardinality("cardinality_stack_id")?.Value ?? 0;
                organization.ProjectCount = await _projectRepository.GetCountByOrganizationIdAsync(organization.Id);
            }

            return(viewOrganizations);
        }
示例#8
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);
        }
示例#9
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 sf           = new ExceptionlessSystemFilterQuery(project, organization);
            var systemFilter = new ElasticQuery().WithSystemFilter(sf).WithDateRange(data.UtcStartTime, data.UtcEndTime, (PersistentEvent e) => e.Date).WithIndexes(data.UtcStartTime, data.UtcEndTime);
            var result       = await _eventRepository.CountBySearchAsync(systemFilter, $"{EventIndexType.Alias.Type}:{Event.KnownTypes.Error}", "terms:(is_first_occurrence @include:true) cardinality:stack_id").AnyContext();

            bool hasSubmittedEvents = result.Total > 0;

            if (!hasSubmittedEvents)
            {
                hasSubmittedEvents = await _eventRepository.GetCountByProjectIdAsync(project.Id, true).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.Aggregations.Terms <double>("terms_is_first_occurrence")?.Buckets.FirstOrDefault()?.Total ?? 0,
                UniqueTotal        = result.Aggregations.Cardinality("cardinality_stack_id")?.Value ?? 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);
        }