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

This reverts commit 644cb6d1f67de9531ea1a1dfd3853709e0329ce7.
This commit is contained in:
YEGCSharpDev 2021-10-14 16:00:01 -06:00
parent 644cb6d1f6
commit 98b3c483be
7 changed files with 207 additions and 45 deletions

View File

@ -0,0 +1,39 @@
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

@ -1,15 +0,0 @@
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,11 +1,14 @@
namespace API.DTOs.Stats
namespace API.DTOs.Stats
{
public class ServerInfoDto
{
public string InstallId { get; set; }
public string Os { get; set; }
public bool IsDocker { get; set; }
public string DotnetVersion { 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 int NumOfCores { get; set; }
}
}

View File

@ -0,0 +1,33 @@
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,10 +1,11 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using API.DTOs.Stats;
namespace API.Interfaces.Services
{
public interface IStatsService
{
Task PathData(ClientInfoDto clientInfoDto);
Task CollectAndSendStatsData();
}
}

View File

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

View File

@ -1,7 +1,9 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Stats;
@ -10,12 +12,16 @@ using API.Interfaces.Services;
using API.Services.Clients;
using Kavita.Common;
using Kavita.Common.EnvironmentInfo;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace API.Services.Tasks
{
public class StatsService : IStatsService
{
private const string TempFilePath = "stats/";
private const string TempFileName = "app_stats.json";
private readonly StatsApiClient _client;
private readonly DataContext _dbContext;
private readonly ILogger<StatsService> _logger;
@ -29,22 +35,54 @@ namespace API.Services.Tasks
_logger = logger;
_unitOfWork = unitOfWork;
}
#region Communcation Methods
private async Task CollectAndSendRelevantData()
private static string FinalPath => Path.Combine(Directory.GetCurrentDirectory(), TempFilePath, TempFileName);
private static bool FileExists => File.Exists(FinalPath);
public async Task PathData(ClientInfoDto clientInfoDto)
{
_logger.LogDebug("Collecting server info");
_logger.LogDebug("Pathing client data to the file");
var data = await GetData();
var statisticsDto = await GetData();
_logger.LogDebug("Sending data to the Stats server");
statisticsDto.AddClientInfo(clientInfoDto);
await _client.SendDataToStatsServer(data);
await SaveFile(statisticsDto);
}
#endregion
private async Task CollectRelevantData()
{
_logger.LogDebug("Collecting data from the server and database");
_logger.LogDebug("Collecting usage info");
var usageInfo = await GetUsageInfo();
#region Data Collection
_logger.LogDebug("Collecting server info");
var serverInfo = GetServerInfo();
await PathData(serverInfo, usageInfo);
}
private async Task FinalizeStats()
{
try
{
_logger.LogDebug("Finalizing Stats collection flow");
var data = await GetExistingData<UsageStatisticsDto>();
_logger.LogDebug("Sending data to the Stats server");
await _client.SendDataToStatsServer(data);
_logger.LogDebug("Deleting the file from disk");
if (FileExists) File.Delete(FinalPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error Finalizing Stats collection flow");
throw;
}
}
public async Task CollectAndSendStatsData()
{
@ -54,40 +92,103 @@ namespace API.Services.Tasks
_logger.LogDebug("User has opted out of stat collection, not registering tasks");
return;
}
await CollectAndSendRelevantData();
await CollectRelevantData();
await FinalizeStats();
}
private async ValueTask<InstallationStatsDto> GetData()
private async Task PathData(ServerInfoDto serverInfoDto, UsageInfoDto usageInfoDto)
{
_logger.LogDebug("Pathing server and usage info to the file");
return new InstallationStatsDto
var data = await GetData();
data.ServerInfo = serverInfoDto;
data.UsageInfo = usageInfoDto;
data.MarkAsUpdatedNow();
await SaveFile(data);
}
private async ValueTask<UsageStatisticsDto> GetData()
{
if (!FileExists) return new UsageStatisticsDto {InstallId = HashUtil.AnonymousToken()};
return await GetExistingData<UsageStatisticsDto>();
}
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
{
Os = RuntimeInformation.OSDescription,
DotnetVersion = Environment.Version.ToString(),
KavitaVersion = BuildInfo.Version.ToString(),
InstallId = HashUtil.AnonymousToken(),
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker
UsersCount = usersCount,
LibraryTypesCreated = libsCountByType,
FileTypes = uniqueFileTypes
};
return usageInfo;
}
public static ServerInfoDto GetServerInfo()
{
var serverInfo = new ServerInfoDto
{
Os = RuntimeInformation.OSDescription,
DotnetVersion = Environment.Version.ToString(),
DotNetVersion = Environment.Version.ToString(),
RunTimeVersion = RuntimeInformation.FrameworkDescription,
KavitaVersion = BuildInfo.Version.ToString(),
InstallId = HashUtil.AnonymousToken(),
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker
Culture = Thread.CurrentThread.CurrentCulture.Name,
BuildBranch = BuildInfo.Branch,
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker,
NumOfCores = Environment.ProcessorCount
};
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);
}
}
}