auto.bus_api/Server/Services/StatisticsService.cs
2022-11-24 10:57:05 +02:00

176 lines
6.4 KiB
C#

using System.Dynamic;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Server.Helpers;
using Server.Models;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Statistics;
namespace Server.Services;
public class StatisticsService : IStatisticsService
{
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
private readonly IDataShaper<UserDto> _userDataShaper;
private readonly IDataShaper<CompanyDto> _companyDataShaper;
public StatisticsService(ApplicationDbContext dbContext, IMapper mapper,
IDataShaper<UserDto> userDataShaper, IDataShaper<CompanyDto> companyDataShaper)
{
_dbContext = dbContext;
_mapper = mapper;
_userDataShaper = userDataShaper;
_companyDataShaper = companyDataShaper;
}
// Popularity is measured in number of purchased tickets
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> route)>
GetPopularRoutes(int amount)
{
throw new NotImplementedException();
}
// Engagement is measured in number of tickets bought in last 60 days
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> users, PagingMetadata<User> pagingMetadata)>
GetEngagedUsers(EngagedUserParameters parameters)
{
var fromDateUtc = DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays);
var resultObjects = _dbContext.Users
.Include(u => u.Tickets)
.Select(u => new
{
User = u,
Tickets = u.Tickets.Where(t => t.PurchaseDateTimeUtc >= fromDateUtc)
})
.OrderByDescending(o => o.User.Tickets.Count)
.Take(parameters.Amount);
var dbUsers = resultObjects.Select(i => i.User);
var pagingMetadata = ApplyPaging(ref dbUsers, parameters.PageNumber,
parameters.PageSize);
var userDtos = _mapper.ProjectTo<UserDto>(dbUsers).ToArray();
var shapedData = _userDataShaper
.ShapeData(userDtos, parameters.Fields ?? parameters.DefaultFields)
.ToArray();
if (parameters.Fields != null &&
parameters.Fields.ToLower().Contains("ticketCount".ToLower()))
{
var dbUsersArray = await dbUsers.ToArrayAsync();
for (int i = 0; i < dbUsersArray.Length; i++)
{
var ticketCount = dbUsersArray[i].Tickets.Count;
shapedData[i].TryAdd("TicketCount", ticketCount);
}
}
return (true, null, shapedData, pagingMetadata);
}
// Popularity is measured in average rating of all VehicleEnrollments of a company
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> companies, PagingMetadata<Company> pagingMetadata)>
GetPopularCompanies(PopularCompanyParameters parameters)
{
var dbCompanies = _dbContext.Companies
.Include(c => c.Vehicles)
.ThenInclude(v => v.VehicleEnrollments)
.ThenInclude(ve => ve.Tickets)
.Include(c => c.Vehicles)
.ThenInclude(v => v.VehicleEnrollments)
.ThenInclude(ve => ve.Reviews);
// Calculate average rating for each company
var dbCompaniesArray = await dbCompanies.ToArrayAsync();
double[] companiesAvgRatings = new double[dbCompaniesArray.Length];
for (int i = 0; i < dbCompaniesArray.Length; i++)
{
double tempC = 0;
foreach (var v in dbCompaniesArray[i].Vehicles)
{
double tempV = 0;
foreach (var ve in v.VehicleEnrollments)
{
double tempVE = 0;
foreach (var r in ve.Reviews)
{
tempVE += r.Rating;
}
tempV += tempVE / ve.Reviews.Count;
}
tempC += tempV / v.VehicleEnrollments.Count;
}
companiesAvgRatings[i] = tempC / dbCompaniesArray[i].Vehicles.Count;
}
// Sort companiesAvgRatings and apply the same sorting to dbCompaniesArray
int n = companiesAvgRatings.Length;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (companiesAvgRatings[j] > companiesAvgRatings[j + 1])
{
// swap temp and arr[i]
(companiesAvgRatings[j], companiesAvgRatings[j + 1]) = (companiesAvgRatings[j + 1], companiesAvgRatings[j]);
(dbCompaniesArray[j], dbCompaniesArray[j + 1]) = (dbCompaniesArray[j + 1], dbCompaniesArray[j]);
}
}
}
companiesAvgRatings = companiesAvgRatings.Skip(companiesAvgRatings.Length - parameters.Amount).Reverse().ToArray();
var popularCompanies = dbCompaniesArray.Skip(companiesAvgRatings.Length - parameters.Amount).Reverse().AsQueryable();
// Apply paging, convert to DTOs and shape data
var pagingMetadata = ApplyPaging(ref popularCompanies, parameters.PageNumber, parameters.PageSize);
var companyDtos = _mapper.ProjectTo<CompanyDto>(popularCompanies).ToArray();
var shapedData = _companyDataShaper.ShapeData(companyDtos, parameters.Fields ?? parameters.DefaultFields).ToArray();
if (parameters.Fields != null &&
parameters.Fields.ToLower().Contains("rating".ToLower()))
{
for (int i = 0; i < shapedData.Length; i++)
{
shapedData[i].TryAdd("Rating", companiesAvgRatings[i]);
}
}
return (true, null, shapedData, pagingMetadata);
}
// Popularity is measured in number of routes using the station
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> stations)>
GetPopularStations(int amount)
{
throw new NotImplementedException();
}
PagingMetadata<T> ApplyPaging<T>(ref IQueryable<T> obj,
int pageNumber, int pageSize)
{
var metadata = new PagingMetadata<T>(obj,
pageNumber, pageSize);
obj = obj
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
return metadata;
}
}