Files
MBN_STOCK_WEBVIEW/tools/MBN_STOCK_WEBVIEW.DbSmoke/ManualFinancialListCompatibilityAudit.cs

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 _));
}
}