Analytics and statistics library for AcontPlus applications providing comprehensive metrics, trends, and business intelligence capabilities across multiple domains.
$ dotnet add package Acontplus.AnalyticsA comprehensive analytics and statistics library for .NET applications, providing domain-agnostic metrics, trends, and business intelligence capabilities. Built with modern .NET 10 features and designed for cross-domain reusability.
Works across any business domain:
dotnet add package Acontplus.Analytics
Install-Package Acontplus.Analytics
<PackageReference Include="Acontplus.Analytics" Version="1.0.0" />
Extend the base DTOs with your domain-specific properties:
using Acontplus.Analytics.Dtos;
// Your custom dashboard stats
public class SalesDashboardDto : BaseDashboardStatsDto
{
public decimal AverageOrderSize { get; set; }
public int TopSellingProductId { get; set; }
public string TopSellingProductName { get; set; } = string.Empty;
}
// Your custom real-time stats
public class SalesRealTimeDto : BaseRealTimeStatsDto
{
public int ActiveCheckouts { get; set; }
public decimal PendingPaymentsTotal { get; set; }
}
// Use base classes directly or extend them
public class SalesAggregatedDto : BaseAggregatedStatsDto { }
public class SalesTrendDto : BaseTrendDto { }
Use the extension method to register the generic service with your specific DTOs and stored procedure names.
using Acontplus.Analytics.Extensions;
// In your Program.cs or DependencyInjection setup
services.AddStatisticsService<SalesDashboardDto, SalesRealTimeDto, SalesAggregatedDto, SalesTrendDto>(
dashboardSpName: "Sales.GetDashboardStats",
realTimeSpName: "Sales.GetRealTimeStats",
aggregatedSpName: "Sales.GetAggregatedStats",
trendsSpName: "Sales.GetTrendStats"
);
// OR use the module-based convention
// This assumes SPs are named: Sales.AnalyticsGetDashboard, Sales.AnalyticsGetRealTime, etc.
services.AddStatisticsService<SalesDashboardDto, SalesRealTimeDto, SalesAggregatedDto, SalesTrendDto>(
moduleName: "Sales.Analytics"
);
Inject the generic interface into your controllers or endpoints.
using Acontplus.Analytics.Interfaces;
using Acontplus.Core.Dtos.Requests;
public class SalesAnalyticsEndpoints
{
public static void Map(IEndpointRouteBuilder app)
{
app.MapPost("/api/analytics/dashboard", async (
IStatisticsService<SalesDashboardDto, SalesRealTimeDto, SalesAggregatedDto, SalesTrendDto> statsService,
FilterRequest filter) =>
{
var result = await statsService.GetDashboardStatsAsync(filter);
if (result.IsFailure)
{
return Results.BadRequest(result.Error);
}
return Results.Ok(result.Value);
});
}
}
-- Example stored procedure using the SQL templates
CREATE PROCEDURE [dbo].[sp_GetSalesDashboardStats]
@StartDate DATETIME2 = NULL,
@EndDate DATETIME2 = NULL
AS
BEGIN
SET NOCOUNT ON;
-- Use the date range defaults from StatsSqlTemplates
SET @StartDate = ISNULL(@StartDate, DATEADD(DAY, -30, GETUTCDATE()));
SET @EndDate = ISNULL(@EndDate, GETUTCDATE());
-- Calculate previous period for comparison
DECLARE @PeriodDays INT = DATEDIFF(DAY, @StartDate, @EndDate);
DECLARE @PrevStartDate DATETIME2 = DATEADD(DAY, -@PeriodDays, @StartDate);
DECLARE @PrevEndDate DATETIME2 = @StartDate;
SELECT
-- Transaction Metrics
COUNT(*) AS TotalTransactions,
SUM(CASE WHEN Status = 'Completed' THEN 1 ELSE 0 END) AS CompletedTransactions,
SUM(CASE WHEN Status = 'Cancelled' THEN 1 ELSE 0 END) AS CancelledTransactions,
SUM(CASE WHEN Status = 'Pending' THEN 1 ELSE 0 END) AS ActiveTransactions,
-- Revenue Metrics
SUM(TotalAmount) AS TotalRevenue,
SUM(TotalAmount - TaxAmount) AS NetRevenue,
SUM(TaxAmount) AS TotalTax,
SUM(DiscountAmount) AS TotalDiscounts,
AVG(TotalAmount) AS AverageTransactionValue,
-- Entity Metrics
COUNT(DISTINCT CustomerId) AS UniqueEntities,
SUM(CASE WHEN CustomerCreatedDate >= @StartDate THEN 1 ELSE 0 END) AS NewEntities
FROM Orders
WHERE CreatedAt BETWEEN @StartDate AND @EndDate;
END
Comprehensive business metrics for executive dashboards:
| Property | Type | Description |
|---|---|---|
TotalTransactions | int | Total count of operations |
CompletedTransactions | int | Successfully completed |
TotalRevenue | decimal | Gross revenue |
NetRevenue | decimal | Revenue after deductions |
GrowthRate | decimal | Growth percentage vs previous period |
UniqueEntities | int | Unique customers/clients |
CompletionRate | decimal | Success rate percentage |
40+ properties covering transactions, revenue, volumes, entities, and comparisons.
Live operational metrics:
| Property | Type | Description |
|---|---|---|
ActiveOperations | int | Current active operations |
OperationsLast5Min | int | Activity in last 5 minutes |
CurrentHourRevenue | decimal | Revenue this hour |
ItemsInQueue | int | Pending items |
AverageProcessingTime | decimal | Processing time in minutes |
CapacityUtilizationRate | decimal | Resource usage percentage |
25+ properties for real-time monitoring and capacity planning.
Time-series aggregations with statistical analysis:
| Property | Type | Description |
|---|---|---|
Period | DateTime | Time period for this data point |
Value | decimal | Primary metric value |
MinValue / MaxValue / AvgValue | decimal | Statistical aggregates |
PreviousPeriodValue | decimal? | Comparison value |
ChangePercent | decimal? | Period-over-period change |
Percentile25 / 50 / 75 | decimal? | Distribution metrics |
30+ properties for comprehensive statistical analysis.
Advanced trend analysis with forecasting:
| Property | Type | Description |
|---|---|---|
Date | DateTime | Data point timestamp |
Value | decimal | Actual observed value |
Forecast | decimal? | Predicted value |
MovingAverage7 / 30 / 90 | decimal? | Trend smoothing |
SamePeriodLastYear | decimal? | Year-over-year comparison |
IsAnomaly | bool | Outlier detection |
35+ properties for deep trend analysis and forecasting.
Use StatsSqlTemplates for consistent SQL patterns:
using Acontplus.Analytics.Models;
// Date range parameters
StatsSqlTemplates.DateRangeParams
StatsSqlTemplates.DateRangeDefaults
StatsSqlTemplates.PreviousPeriodCalc
// Aggregation helpers
StatsSqlTemplates.GroupByCase // Dynamic time grouping
StatsSqlTemplates.PeriodLabelFormat // Human-readable labels
// Statistical functions
StatsSqlTemplates.MovingAverage7 // 7-period moving average
StatsSqlTemplates.MovingAverage30 // 30-period moving average
StatsSqlTemplates.PercentChange // Percentage change calculation
StatsSqlTemplates.TrendDirection // up/down/stable classification
// Special functions
StatsSqlTemplates.AnomalyDetection // Outlier detection
StatsSqlTemplates.IsWeekend // Weekend classification
All DTOs include an optional Labels dictionary property that your application can populate with localized strings:
// In your application's localization service
public class SalesStatisticsLocalization
{
public static Dictionary<string, string> GetSpanishLabels() => new()
{
{ "TotalRevenue", "Ingresos Totales" },
{ "NetRevenue", "Ingresos Netos" },
{ "GrowthRate", "Tasa de Crecimiento" },
{ "AverageOrderValue", "Valor Promedio del Pedido" }
};
public static Dictionary<string, string> GetEnglishLabels() => new()
{
{ "TotalRevenue", "Total Revenue" },
{ "NetRevenue", "Net Revenue" },
{ "GrowthRate", "Growth Rate" },
{ "AverageOrderValue", "Average Order Value" }
};
}
// Populate labels in your service
var dashboard = await GetDashboardStatsAsync(filter);
if (dashboard.IsSuccess)
{
dashboard.Value.Labels = language == "es"
? SalesStatisticsLocalization.GetSpanishLabels()
: SalesStatisticsLocalization.GetEnglishLabels();
}
Note: Localization is intentionally left to the consuming application, allowing you to integrate with your preferred localization system (RESX files, database, JSON, or any i18n framework).
├── API Layer (Demo.Api)
│ └── Endpoints/Business/Analytics/SalesAnalyticsEndpoints.cs
│ → Maps HTTP requests to service calls
│ → Applies localization at presentation layer
│
├── Application Layer (Demo.Application)
│ ├── Interfaces/ISalesAnalyticsService.cs
│ │ → Domain-specific analytics contract
│ ├── Services/SalesAnalyticsService.cs
│ │ → Inherits from StatisticsService<T1,T2,T3,T4>
│ │ → Configures stored procedure names
│ ├── Dtos/Analytics/
│ │ ├── SalesDashboardDto.cs (extends BaseDashboardStatsDto)
│ │ └── SalesRealTimeDto.cs (extends BaseRealTimeStatsDto)
│ └── Helpers/SalesAnalyticsLocalization.cs
│ → Application-specific label provider (Spanish/English)
│
├── Domain Layer (Acontplus.TestDomain)
│ └── Entities/Sale.cs
│ → Business entity with analytics-relevant properties
│
└── Infrastructure Layer (Database)
└── StoredProcedures/SalesAnalytics.sql
→ SQL procedures implementing analytics logic
File: apps/src/Acontplus.TestDomain/Entities/Sale.cs
public class Sale : BaseEntity
{
public int CustomerId { get; set; }
public DateTime SaleDate { get; set; }
public decimal TotalAmount { get; set; }
public decimal TaxAmount { get; set; }
public decimal DiscountAmount { get; set; }
public string Status { get; set; } // Pending, Completed, Cancelled
public string PaymentMethod { get; set; }
public int ItemCount { get; set; }
}
File: apps/src/Acontplus.TestApplication/Dtos/Analytics/SalesDashboardDto.cs
public class SalesDashboardDto : BaseDashboardStatsDto
{
// Extends base DTO with sales-specific properties
public decimal AverageOrderValue { get; set; }
public decimal DiscountPercentage { get; set; }
public decimal CancellationRate { get; set; }
public decimal CashSales { get; set; }
public decimal CreditCardSales { get; set; }
public int NewCustomers { get; set; }
public decimal CustomerRetentionRate { get; set; }
}
File: apps/src/Acontplus.TestApplication/Services/SalesAnalyticsService.cs
public class SalesAnalyticsService : StatisticsService<
SalesDashboardDto,
SalesRealTimeDto,
BaseAggregatedStatsDto,
BaseTrendDto>,
ISalesAnalyticsService
{
public SalesAnalyticsService(IAdoRepository adoRepository)
: base(
adoRepository,
dashboardSpName: "Sales.GetDashboardStats",
realTimeSpName: "Sales.GetRealTimeStats",
aggregatedSpName: "Sales.GetAggregatedStats",
trendsSpName: "Sales.GetTrendStats")
{
}
}
File: apps/src/Demo.Api/Endpoints/Business/Analytics/SalesAnalyticsEndpoints.cs
public static class SalesAnalyticsEndpoints
{
public static IEndpointRouteBuilder MapSalesAnalyticsEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/analytics/sales")
.WithTags("Sales Analytics");
// Dashboard endpoint
group.MapGet("/dashboard", async (
[FromQuery] DateTime? startDate,
[FromQuery] DateTime? endDate,
[FromQuery] string? language,
[FromServices] ISalesAnalyticsService analyticsService,
CancellationToken cancellationToken) =>
{
var filter = new FilterRequest
{
Filters = new Dictionary<string, object>
{
{ "StartDate", startDate ?? DateTime.UtcNow.AddDays(-30) },
{ "EndDate", endDate ?? DateTime.UtcNow }
}
};
var result = await analyticsService.GetDashboardStatsAsync(filter, cancellationToken);
return result.Match(
success: data =>
{
data.Labels = SalesAnalyticsLocalization.GetLabels(language ?? "en");
return Results.Ok(data);
},
failure: error => Results.BadRequest(new { error = error.Message, code = error.Code }));
});
return app;
}
}
File: apps/database/StoredProcedures/SalesAnalytics.sql
CREATE OR ALTER PROCEDURE [Sales].[GetDashboardStats]
@StartDate DATETIME2 = NULL,
@EndDate DATETIME2 = NULL
AS
BEGIN
SET @StartDate = ISNULL(@StartDate, DATEADD(DAY, -30, GETUTCDATE()));
SET @EndDate = ISNULL(@EndDate, GETUTCDATE());
SELECT
-- Base properties
COUNT(*) AS TotalTransactions,
SUM(TotalAmount) AS TotalRevenue,
AVG(TotalAmount) AS AverageTransactionValue,
-- Sales-specific properties
AVG(TotalAmount) AS AverageOrderValue,
SUM(DiscountAmount) AS TotalDiscounts,
SUM(CASE WHEN PaymentMethod = 'Cash' THEN TotalAmount ELSE 0 END) AS CashSales,
COUNT(DISTINCT CustomerId) AS NewCustomers
FROM Sales
WHERE SaleDate BETWEEN @StartDate AND @EndDate;
END
StatisticsService<T1,T2,T3,T4> can be reused for any domainpublic class OrderDashboardDto : BaseDashboardStatsDto
{
public decimal AverageOrderValue { get; set; }
public int AbandonedCarts { get; set; }
public decimal ConversionRate { get; set; }
}
public class PatientDashboardDto : BaseDashboardStatsDto
{
public int TotalAppointments { get; set; }
public int CompletedTreatments { get; set; }
public decimal PatientSatisfactionScore { get; set; }
}
public class TransactionDashboardDto : BaseDashboardStatsDto
{
public decimal ProcessingFees { get; set; }
public int ChargebackCount { get; set; }
public decimal ApprovalRate { get; set; }
}
We welcome contributions! Please see Contributing Guidelines.
Ivan Paz – @iferpaz7
Acontplus – Software Solutions, Ecuador
Built with ❤️ for data-driven business applications