Files
Omika/tools/Omika.RendezVousProbe/Program.cs
T
2026-10-23 18:46:50 +02:00

147 lines
5.2 KiB
C#

// Probe LECTURE SEULE ciblé RendezVous.
// Reproduit EXACTEMENT le mécanisme de connexion de l'API
// (BusinessOleDbConnection -> HfSqlOleDbConnectionFactory -> BuildExtendedPropertiesFromFilePasswords)
// pour lire Planning_RDV sur BATIK_TEST et identifier les lignes où PL_DateFin < PL_DateDebut.
//
// Aucune écriture. Aucun password affiché. Aucune donnée personnelle loggée
// (nom client, personne, adresse, notes, téléphone, email).
// Seuls PL_PKID (identifiant technique) et les valeurs brutes DATE/HEURE sont affichés.
using System.Globalization;
using Microsoft.Extensions.Configuration;
using Omika.Data.Configuration;
using Omika.Data.Connection;
using Omika.Data.HfSql.Connection;
// --- 1. Charger la configuration Hfsql (avec FilePasswords) ---
var configPath = Path.GetFullPath(Path.Combine(
Directory.GetCurrentDirectory(),
"config", "hfsql-test.local.json"));
var config = new ConfigurationBuilder()
.AddJsonFile(configPath, optional: false, reloadOnChange: false)
.Build();
var hfsqlOptions = new HfsqlOptions();
config.GetSection(HfsqlOptions.SectionName).Bind(hfsqlOptions);
Console.WriteLine($"=== Probe RendezVous (base {hfsqlOptions.DatabaseName}) ===");
Console.WriteLine();
// --- 2. Reproduire le mécanisme de l'API : BusinessOleDbConnection ---
var factory = new HfSqlOleDbConnectionFactory();
await using var business = new BusinessOleDbConnection(hfsqlOptions, factory);
var sw = System.Diagnostics.Stopwatch.StartNew();
int totalRows = 0;
int countInvalid = 0;
int countCrossMidnight = 0;
int countAnomalyPure = 0;
long? firstInvalidPk = null;
DateTime? firstInvalidStart = null;
DateTime? firstInvalidEnd = null;
try
{
await business.OpenAsync();
Console.WriteLine("Connexion OLE DB ouverte (mécanisme API).");
// --- 3. Lire les colonnes DATE/HEURE de Planning_RDV ---
// Même périmètre que le repository (SearchAsync) : toutes les lignes.
string sql = "SELECT PL_PKID, PL_DateDebut, PL_DateFin FROM Planning_RDV";
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var rows = await business.QueryAsync(sql, cancellationToken: cts.Token);
totalRows = rows.Count;
Console.WriteLine($"rows lues = {totalRows}");
foreach (var row in rows)
{
long pk = ToLong(row, "PL_PKID");
DateTime start = ToDateTime(row, "PL_DateDebut");
DateTime end = ToDateTime(row, "PL_DateFin");
if (end < start)
{
countInvalid++;
if (firstInvalidPk is null)
{
firstInvalidPk = pk;
firstInvalidStart = start;
firstInvalidEnd = end;
}
// Distinguer un éventuel rendez-vous à cheval sur minuit
// (fin le lendemain du début) d'une anomalie pure (fin avant début).
// Cross-midnight : la fin est le jour suivant le début.
bool isCrossMidnight = end.Date == start.Date.AddDays(1);
if (isCrossMidnight)
{
countCrossMidnight++;
}
else
{
countAnomalyPure++;
}
}
}
sw.Stop();
Console.WriteLine();
Console.WriteLine("--- Résultat ---");
Console.WriteLine($"rows = {totalRows}");
Console.WriteLine($"count_invalid (PL_DateFin < PL_DateDebut) = {countInvalid}");
Console.WriteLine($" dont cross-midnight (fin = lendemain du début) = {countCrossMidnight}");
Console.WriteLine($" dont anomalie pure (fin avant début, hors lendemain) = {countAnomalyPure}");
Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms");
if (firstInvalidPk is not null)
{
Console.WriteLine();
Console.WriteLine("--- Première ligne invalide (technique, non sensible) ---");
Console.WriteLine($"PL_PKID = {firstInvalidPk}");
Console.WriteLine($"PL_DateDebut (brut) = {firstInvalidStart:O}");
Console.WriteLine($"PL_DateFin (brut) = {firstInvalidEnd:O}");
Console.WriteLine($"start calculé = {firstInvalidStart:O}");
Console.WriteLine($"end calculé = {firstInvalidEnd:O}");
}
else
{
Console.WriteLine("Aucune ligne invalide détectée.");
}
}
catch (Exception ex)
{
sw.Stop();
Console.WriteLine($"ERREUR: {ex.Message}");
Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms");
}
Console.WriteLine();
Console.WriteLine("Probe RendezVous terminé (lecture seule).");
// --- Helpers ---
static long ToLong(IReadOnlyDictionary<string, object?> row, string column)
{
if (row.TryGetValue(column, out var value) && value is not null)
{
if (value is long l) return l;
if (value is int i) return i;
if (value is decimal d) return (long)d;
if (long.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out long parsed))
return parsed;
}
return 0;
}
static DateTime ToDateTime(IReadOnlyDictionary<string, object?> row, string column)
{
if (row.TryGetValue(column, out var value) && value is not null)
{
if (value is DateTime dt) return dt;
if (DateTime.TryParse(value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed))
return parsed;
}
return default;
}