147 lines
5.2 KiB
C#
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;
|
|
}
|