156 lines
6.4 KiB
C#
156 lines
6.4 KiB
C#
using System.Data;
|
|
using System.Globalization;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
internal static class ManualFinancialListCompatibilityAudit
|
|
{
|
|
internal static async Task RunAsync(
|
|
IDataQueryExecutor executor,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(executor);
|
|
var service = new LegacyManualFinancialScreenService(executor);
|
|
foreach (var contract in ManualFinancialCutContracts.All.OrderBy(
|
|
contract => contract.Screen))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
var table = await executor.ExecuteAsync(
|
|
DataSourceKind.Oracle,
|
|
"GRAPH_E_LIST_COUNT_" + contract.Screen,
|
|
$"SELECT COUNT(*) AS ROW_COUNT FROM {contract.TableName}",
|
|
cancellationToken).ConfigureAwait(false);
|
|
if (table.Columns.Count != 1 || table.Rows.Count != 1 ||
|
|
!string.Equals(
|
|
table.Columns[0].ColumnName,
|
|
"ROW_COUNT",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new InvalidDataException(
|
|
"The GraphE list count query returned an invalid schema.");
|
|
}
|
|
|
|
var count = Convert.ToInt32(
|
|
table.Rows[0][0],
|
|
CultureInfo.InvariantCulture);
|
|
if (count < 0)
|
|
{
|
|
throw new InvalidDataException(
|
|
"The GraphE list count query returned an invalid count.");
|
|
}
|
|
|
|
var result = await service.SearchAsync(
|
|
contract.Screen,
|
|
query: string.Empty,
|
|
maximumResults: LegacyManualFinancialScreenService.MaximumResults,
|
|
cancellationToken: cancellationToken)
|
|
.ConfigureAwait(false);
|
|
var accountedRows = checked(result.Items.Count + result.RejectedItemCount);
|
|
var strictReadyRawRows = result.RawItems.Count(item => item.IsPlayoutReady);
|
|
var rawDeferredRows = result.RawItems.Count(item => !item.IsPlayoutReady);
|
|
if (result.Screen != contract.Screen ||
|
|
result.Query.Length != 0 ||
|
|
result.IsTruncated ||
|
|
!result.RawItemsComplete ||
|
|
accountedRows != count ||
|
|
result.RawItems.Count != count ||
|
|
strictReadyRawRows != result.Items.Count ||
|
|
rawDeferredRows != result.RejectedItemCount ||
|
|
result.UnreadableStockNames.Count != result.RejectedItemCount ||
|
|
result.Items.Any(item =>
|
|
item.Record.Identity.Screen != contract.Screen) ||
|
|
result.RawItems.Any(item =>
|
|
item.Record.Identity.Screen != contract.Screen))
|
|
{
|
|
throw new InvalidDataException(
|
|
"The GraphE full-list compatibility read did not account for every DB row.");
|
|
}
|
|
|
|
var rawProbe = result.RawItems.FirstOrDefault(item => !item.IsPlayoutReady) ??
|
|
result.RawItems.FirstOrDefault();
|
|
if (rawProbe is not null)
|
|
{
|
|
var fresh = await service.GetRawAsync(rawProbe, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
if (fresh.Record.Identity != rawProbe.Record.Identity ||
|
|
fresh.Record.StorageValues.Count != rawProbe.Record.StorageValues.Count)
|
|
{
|
|
throw new InvalidDataException(
|
|
"The GraphE exact raw-row read was not correlated to its list handle.");
|
|
}
|
|
}
|
|
|
|
var legacyShapeCompatibleDeferredRows = result.RawItems.Count(item =>
|
|
!item.IsPlayoutReady && IsLegacySceneShapeCompatible(item.Record));
|
|
|
|
Console.WriteLine(
|
|
$"GRAPH_E_LIST_COMPAT_AUDIT: screen={contract.Screen} " +
|
|
$"dbRows={count} rawEditable={result.RawItems.Count} " +
|
|
$"strictReady={result.Items.Count} " +
|
|
$"strictDeferred={result.RejectedItemCount} " +
|
|
$"legacyShapeCompatibleDeferred={legacyShapeCompatibleDeferredRows} " +
|
|
$"exactRawReads={(rawProbe is null ? 0 : 1)} truncated=false");
|
|
}
|
|
}
|
|
|
|
private static bool IsLegacySceneShapeCompatible(ManualFinancialRawRecord record) =>
|
|
record.Identity.Screen switch
|
|
{
|
|
ManualFinancialScreenKind.RevenueComposition =>
|
|
record.StorageValues.Skip(1).All(IsLegacyRevenueSlice),
|
|
ManualFinancialScreenKind.GrowthMetrics =>
|
|
record.StorageValues.Take(4).All(value => IsLegacyNumberSeries(value, 4)) &&
|
|
HasAtLeastTokens(record.StorageValues[4], 4),
|
|
ManualFinancialScreenKind.Sales or ManualFinancialScreenKind.OperatingProfit =>
|
|
record.StorageValues.All(IsLegacyQuarter),
|
|
_ => false
|
|
};
|
|
|
|
private static bool IsLegacyRevenueSlice(string value)
|
|
{
|
|
var tokens = value.Split('_', StringSplitOptions.None);
|
|
if (tokens.Length < 2)
|
|
{
|
|
return value.Length == 0;
|
|
}
|
|
|
|
if (tokens[0].Length == 0 || tokens[1] is "" or "-")
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return double.TryParse(
|
|
tokens[1],
|
|
NumberStyles.Float | NumberStyles.AllowThousands,
|
|
CultureInfo.CurrentCulture,
|
|
out var parsed) &&
|
|
double.IsFinite(parsed);
|
|
}
|
|
|
|
private static bool IsLegacyNumberSeries(string value, int minimumTokens)
|
|
{
|
|
var tokens = value.Split('_', StringSplitOptions.None);
|
|
return tokens.Length >= minimumTokens && tokens.All(token =>
|
|
token.Length == 0 ||
|
|
double.TryParse(
|
|
token,
|
|
NumberStyles.Float | NumberStyles.AllowThousands,
|
|
CultureInfo.CurrentCulture,
|
|
out var parsed) &&
|
|
double.IsFinite(parsed));
|
|
}
|
|
|
|
private static bool HasAtLeastTokens(string value, int minimumTokens) =>
|
|
value.Split('_', StringSplitOptions.None).Length >= minimumTokens;
|
|
|
|
private static bool IsLegacyQuarter(string value)
|
|
{
|
|
var tokens = value.Split('_', StringSplitOptions.None);
|
|
return tokens.Length >= 2 &&
|
|
(tokens[1].Length == 0 || int.TryParse(
|
|
tokens[1],
|
|
NumberStyles.Integer,
|
|
CultureInfo.CurrentCulture,
|
|
out _));
|
|
}
|
|
}
|