Compare commits

...

4 Commits

Author SHA1 Message Date
289c16efd3 merge upstream 2025-07-02 18:34:40 +00:00
b29e7aa277 merge upstream 2025-05-18 22:48:44 +00:00
Comfy-Mittens
cf8717b60e
refactor 2025-05-17 21:35:02 +01:00
Comfy-Mittens
24bcecbd42
reimplement reporting 2025-05-17 13:41:35 +01:00
5 changed files with 240 additions and 44 deletions

5
OF DL/Extension.props Normal file
View File

@ -0,0 +1,5 @@
<Project>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1"/>
</ItemGroup>
</Project>

View File

@ -2873,4 +2873,80 @@ public class APIHelper : IAPIHelper
} }
return null; return null;
} }
public async Task<Dictionary<string, Subscriptions.List>?> GetActiveSubscribed(string endpoint, bool includeRestricted, IDownloadConfig config)
{
Dictionary<string, string> getParams = new()
{
{ "offset", "0" },
{ "limit", "50" },
{ "type", "active" },
{ "format", "infinite"}
};
return await GetSubscriptions(getParams, endpoint, includeRestricted, config);
}
public async Task<Dictionary<string, Subscriptions.List>?> GetSubscriptions(Dictionary<string, string> getParams, string endpoint, bool includeRestricted, IDownloadConfig config)
{
try
{
Dictionary<string, Entities.Subscriptions.List> users = new();
Subscriptions subscriptions = new();
Log.Debug("Calling GetAllSubscrptions");
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
subscriptions = JsonConvert.DeserializeObject<Subscriptions>(body);
if (subscriptions is { hasMore: true })
{
getParams["offset"] = subscriptions.list.Count.ToString();
while (true)
{
Subscriptions newSubscriptions = new();
string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
if (!string.IsNullOrEmpty(loopbody) && (!loopbody.Contains("[]") || loopbody.Trim() != "[]"))
{
newSubscriptions = JsonConvert.DeserializeObject<Subscriptions>(loopbody, m_JsonSerializerSettings);
}
else
{
break;
}
subscriptions.list.AddRange(newSubscriptions.list);
if (!newSubscriptions.hasMore)
{
break;
}
getParams["offset"] = subscriptions.list.Count.ToString();
}
}
foreach (Subscriptions.List subscription in subscriptions.list)
{
if ((!(subscription.isRestricted ?? false) || ((subscription.isRestricted ?? false) && includeRestricted)))
{
users.TryAdd(subscription.username, subscription);
}
}
return users;
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
if (ex.InnerException != null)
{
Console.WriteLine("\nInner Exception:");
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
}
}
return null;
}
} }

View File

@ -46,4 +46,5 @@
</None> </None>
</ItemGroup> </ItemGroup>
<Import Project="Extension.props" />
</Project> </Project>

View File

@ -844,8 +844,16 @@ public class Program
{ {
DateTime startTime = DateTime.Now; DateTime startTime = DateTime.Now;
Dictionary<string, int> users = new(); Dictionary<string, int> users = new();
Dictionary<string, int> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config); var activeSubbed = await m_ApiHelper.GetActiveSubscribed("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
var activeSubs = activeSubbed?.ToDictionary(e => e.Key, e => e.Value.id);
{
if (Directory.Exists(Config.DownloadPath) && activeSubbed != null)
{
var reportPath = Path.Combine(Config.DownloadPath, "Report.csv");
var report = new Reporting(activeSubbed?.Values);
report.GenerateReport(reportPath);
}
}
Log.Debug("Subscriptions: "); Log.Debug("Subscriptions: ");
foreach (KeyValuePair<string, int> activeSub in activeSubs) foreach (KeyValuePair<string, int> activeSub in activeSubs)

106
OF DL/Reporting.cs Normal file
View File

@ -0,0 +1,106 @@
using CsvHelper;
using CsvHelper.Configuration;
using System.Formats.Asn1;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace OF_DL;
internal class Reporting(IEnumerable<Entities.Subscriptions.List> subscribedUsers)
{
public void GenerateReport(string reportPath)
{
using var fileStream = File.Open(reportPath, FileMode.Create);
using var streamWriter = new StreamWriter(fileStream);
using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Context.RegisterClassMap<UserReportViewModelClassMap>();
csvWriter.WriteRecords(subscribedUsers.Select(user => new UserReportViewModel(user)).OrderByDescending(e => e.category).ThenBy(e => e.currentTotal));
}
}
internal class UserReportViewModel
{
private readonly Entities.Subscriptions.List source;
public const long YoursId = 817758071L;
public const long MineId = 817758033L;
public const decimal tax = 0.2m;
private Lazy<string> _category;
private Lazy<decimal?> _currentPrice;
private Lazy<decimal?> _nextPrice;
public UserReportViewModel(Entities.Subscriptions.List source)
{
this.source = source;
_category = new Lazy<string>(GetCategory);
_currentPrice = new Lazy<decimal?>(() => decimal.TryParse(source.subscribedByData.price, out var price) ? price : null);
_nextPrice = new Lazy<decimal?>(() => decimal.TryParse(source.subscribedByData.regularPrice, out var price) ? price : null);
}
public string name => source.name;
public string username => source.username;
public string category => GetCategory();
public string status => GetStatus();
public DateTime? until => source.subscribedByData.expiredAt;
public decimal? currentPrice => _currentPrice.Value;
public decimal? currentTax => currentPrice * tax;
public decimal? currentTotal => currentPrice + currentTax;
public decimal? renewPrice => _nextPrice.Value;
public decimal? renewTax => renewPrice * tax;
public decimal? renewTotal => renewPrice + renewTax;
public string GetCategory()
{
if (source.listsStates.SingleOrDefault(l => l.id is long intId && intId == YoursId && l.hasUser == true) is { } yours)
{
return yours.name;
}
if (source.listsStates.SingleOrDefault(l => l.id is long intId && intId == MineId && l.hasUser == true) is { } mine)
{
return mine.name;
}
return "None";
}
public string GetStatus()
{
if (source.subscribedByData is { } subData)
{
if (!string.IsNullOrEmpty(subData.status)) return subData.status;
return "Subscribed";
}
return "API Error";
}
}
internal class UserReportViewModelClassMap : ClassMap<UserReportViewModel>
{
public UserReportViewModelClassMap()
{
var index = 0;
Map(m => m.name).Index(index++).Name("Name");
Map(m => m.username).Index(index++).Name("Username");
Map(m => m.category).Index(index++).Name("Category");
Map(m => m.status).Index(index++).Name("Status");
Map(m => m.until).Index(index++).Name("Until");
Map(m => m.currentPrice).Index(index++).Name("Price");
Map(m => m.currentTax).Index(index++).Name("Tax");
Map(m => m.currentTotal).Index(index++).Name("Total");
Map(m => m.renewPrice).Index(index++).Name("Price");
Map(m => m.renewTax).Index(index++).Name("Tax");
Map(m => m.renewTotal).Index(index++).Name("Total");
}
}