Changed the stats that are sent back to stat server from installed server.

This commit is contained in:
YEGCSharpDev 2021-10-14 15:56:38 -06:00
parent 4921b9d26b
commit 644cb6d1f6
7 changed files with 46 additions and 208 deletions

View File

@ -1,39 +0,0 @@
using System;
using System.Threading.Tasks;
using API.DTOs.Stats;
using API.Interfaces.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace API.Controllers
{
public class StatsController : BaseApiController
{
private readonly ILogger<StatsController> _logger;
private readonly IStatsService _statsService;
public StatsController(ILogger<StatsController> logger, IStatsService statsService)
{
_logger = logger;
_statsService = statsService;
}
[AllowAnonymous]
[HttpPost("client-info")]
public async Task<IActionResult> AddClientInfo([FromBody] ClientInfoDto clientInfoDto)
{
try
{
await _statsService.PathData(clientInfoDto);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating the usage statistics");
throw;
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace API.DTOs.Stats
{
public class InstallationStatsDto
{
public string InstallId { get; set; }
public string Os { get; set; }
public bool IsDocker { get; set; }
public string DotnetVersion { get; set; }
public string KavitaVersion { get; set; }
}
}

View File

@ -1,14 +1,11 @@
namespace API.DTOs.Stats namespace API.DTOs.Stats
{ {
public class ServerInfoDto public class ServerInfoDto
{ {
public string InstallId { get; set; }
public string Os { get; set; } public string Os { get; set; }
public string DotNetVersion { get; set; }
public string RunTimeVersion { get; set; }
public string KavitaVersion { get; set; }
public string BuildBranch { get; set; }
public string Culture { get; set; }
public bool IsDocker { get; set; } public bool IsDocker { get; set; }
public int NumOfCores { get; set; } public string DotnetVersion { get; set; }
public string KavitaVersion { get; set; }
} }
} }

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace API.DTOs.Stats
{
public class UsageStatisticsDto
{
public UsageStatisticsDto()
{
MarkAsUpdatedNow();
ClientsInfo = new List<ClientInfoDto>();
}
public string InstallId { get; set; }
public DateTime LastUpdate { get; set; }
public UsageInfoDto UsageInfo { get; set; }
public ServerInfoDto ServerInfo { get; set; }
public List<ClientInfoDto> ClientsInfo { get; set; }
public void MarkAsUpdatedNow()
{
LastUpdate = DateTime.UtcNow;
}
public void AddClientInfo(ClientInfoDto clientInfoDto)
{
if (ClientsInfo.Any(x => x.IsTheSameDevice(clientInfoDto))) return;
ClientsInfo.Add(clientInfoDto);
}
}
}

View File

@ -1,11 +1,10 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using API.DTOs.Stats; using API.DTOs.Stats;
namespace API.Interfaces.Services namespace API.Interfaces.Services
{ {
public interface IStatsService public interface IStatsService
{ {
Task PathData(ClientInfoDto clientInfoDto);
Task CollectAndSendStatsData(); Task CollectAndSendStatsData();
} }
} }

View File

@ -22,7 +22,7 @@ namespace API.Services.Clients
_client.Timeout = TimeSpan.FromSeconds(30); _client.Timeout = TimeSpan.FromSeconds(30);
} }
public async Task SendDataToStatsServer(UsageStatisticsDto data) public async Task SendDataToStatsServer(InstallationStatsDto data)
{ {
var responseContent = string.Empty; var responseContent = string.Empty;

View File

@ -1,9 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json; using System.Text.Json;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using API.Data; using API.Data;
using API.DTOs.Stats; using API.DTOs.Stats;
@ -12,16 +10,12 @@ using API.Interfaces.Services;
using API.Services.Clients; using API.Services.Clients;
using Kavita.Common; using Kavita.Common;
using Kavita.Common.EnvironmentInfo; using Kavita.Common.EnvironmentInfo;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace API.Services.Tasks namespace API.Services.Tasks
{ {
public class StatsService : IStatsService public class StatsService : IStatsService
{ {
private const string TempFilePath = "stats/";
private const string TempFileName = "app_stats.json";
private readonly StatsApiClient _client; private readonly StatsApiClient _client;
private readonly DataContext _dbContext; private readonly DataContext _dbContext;
private readonly ILogger<StatsService> _logger; private readonly ILogger<StatsService> _logger;
@ -35,55 +29,23 @@ namespace API.Services.Tasks
_logger = logger; _logger = logger;
_unitOfWork = unitOfWork; _unitOfWork = unitOfWork;
} }
#region Communcation Methods
private static string FinalPath => Path.Combine(Directory.GetCurrentDirectory(), TempFilePath, TempFileName); private async Task CollectAndSendRelevantData()
private static bool FileExists => File.Exists(FinalPath);
public async Task PathData(ClientInfoDto clientInfoDto)
{ {
_logger.LogDebug("Pathing client data to the file");
var statisticsDto = await GetData();
statisticsDto.AddClientInfo(clientInfoDto);
await SaveFile(statisticsDto);
}
private async Task CollectRelevantData()
{
_logger.LogDebug("Collecting data from the server and database");
_logger.LogDebug("Collecting usage info");
var usageInfo = await GetUsageInfo();
_logger.LogDebug("Collecting server info"); _logger.LogDebug("Collecting server info");
var serverInfo = GetServerInfo();
await PathData(serverInfo, usageInfo); var data = await GetData();
}
private async Task FinalizeStats()
{
try
{
_logger.LogDebug("Finalizing Stats collection flow");
var data = await GetExistingData<UsageStatisticsDto>();
_logger.LogDebug("Sending data to the Stats server"); _logger.LogDebug("Sending data to the Stats server");
await _client.SendDataToStatsServer(data);
_logger.LogDebug("Deleting the file from disk"); await _client.SendDataToStatsServer(data);
if (FileExists) File.Delete(FinalPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error Finalizing Stats collection flow");
throw;
}
} }
#endregion
#region Data Collection
public async Task CollectAndSendStatsData() public async Task CollectAndSendStatsData()
{ {
var allowStatCollection = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).AllowStatCollection; var allowStatCollection = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).AllowStatCollection;
@ -92,103 +54,40 @@ namespace API.Services.Tasks
_logger.LogDebug("User has opted out of stat collection, not registering tasks"); _logger.LogDebug("User has opted out of stat collection, not registering tasks");
return; return;
} }
await CollectRelevantData(); await CollectAndSendRelevantData();
await FinalizeStats();
} }
private async Task PathData(ServerInfoDto serverInfoDto, UsageInfoDto usageInfoDto) private async ValueTask<InstallationStatsDto> GetData()
{ {
_logger.LogDebug("Pathing server and usage info to the file");
var data = await GetData(); return new InstallationStatsDto
data.ServerInfo = serverInfoDto;
data.UsageInfo = usageInfoDto;
data.MarkAsUpdatedNow();
await SaveFile(data);
}
private async ValueTask<UsageStatisticsDto> GetData()
{ {
if (!FileExists) return new UsageStatisticsDto {InstallId = HashUtil.AnonymousToken()}; Os = RuntimeInformation.OSDescription,
DotnetVersion = Environment.Version.ToString(),
return await GetExistingData<UsageStatisticsDto>(); KavitaVersion = BuildInfo.Version.ToString(),
} InstallId = HashUtil.AnonymousToken(),
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker
private async Task<UsageInfoDto> GetUsageInfo()
{
var usersCount = await _dbContext.Users.CountAsync();
var libsCountByType = await _dbContext.Library
.AsNoTracking()
.GroupBy(x => x.Type)
.Select(x => new LibInfo {Type = x.Key, Count = x.Count()})
.ToArrayAsync();
var uniqueFileTypes = await _unitOfWork.FileRepository.GetFileExtensions();
var usageInfo = new UsageInfoDto
{
UsersCount = usersCount,
LibraryTypesCreated = libsCountByType,
FileTypes = uniqueFileTypes
}; };
return usageInfo;
} }
public static ServerInfoDto GetServerInfo() public static ServerInfoDto GetServerInfo()
{ {
var serverInfo = new ServerInfoDto var serverInfo = new ServerInfoDto
{ {
Os = RuntimeInformation.OSDescription, Os = RuntimeInformation.OSDescription,
DotNetVersion = Environment.Version.ToString(), DotnetVersion = Environment.Version.ToString(),
RunTimeVersion = RuntimeInformation.FrameworkDescription,
KavitaVersion = BuildInfo.Version.ToString(), KavitaVersion = BuildInfo.Version.ToString(),
Culture = Thread.CurrentThread.CurrentCulture.Name, InstallId = HashUtil.AnonymousToken(),
BuildBranch = BuildInfo.Branch, IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker,
NumOfCores = Environment.ProcessorCount
}; };
return serverInfo; return serverInfo;
} }
#endregion
private async Task<T> GetExistingData<T>()
{
_logger.LogInformation("Fetching existing data from file");
var existingDataJson = await GetFileDataAsString();
_logger.LogInformation("Deserializing data from file to object");
var existingData = JsonSerializer.Deserialize<T>(existingDataJson);
return existingData;
}
private async Task<string> GetFileDataAsString()
{
_logger.LogInformation("Reading file from disk");
return await File.ReadAllTextAsync(FinalPath);
}
private async Task SaveFile(UsageStatisticsDto statisticsDto)
{
_logger.LogDebug("Saving file");
var finalDirectory = FinalPath.Replace(TempFileName, string.Empty);
if (!Directory.Exists(finalDirectory))
{
_logger.LogDebug("Creating tmp directory");
Directory.CreateDirectory(finalDirectory);
}
_logger.LogDebug("Serializing data to write");
var dataJson = JsonSerializer.Serialize(statisticsDto);
_logger.LogDebug("Writing file to the disk");
await File.WriteAllTextAsync(FinalPath, dataJson);
}
} }
} }