diff --git a/DbToDeviceSummary.cs b/DbToDeviceSummary.cs new file mode 100644 index 0000000..25167a3 --- /dev/null +++ b/DbToDeviceSummary.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace HanvonF710XAttendanceService +{ + internal sealed class DbToDeviceFailedEmployee + { + public string SerialNumber { get; set; } = ""; + public string Name { get; set; } = ""; + public string Department { get; set; } = ""; + public string MachineId { get; set; } = ""; + public string MachineIp { get; set; } = ""; + public string MachineType { get; set; } = ""; + public string Reason { get; set; } = ""; + } + + internal sealed class DbToDeviceMissingMappingEmployee + { + public string SerialNumber { get; set; } = ""; + public string MachineId { get; set; } = ""; + public string MachineIp { get; set; } = ""; + public string MachineType { get; set; } = ""; + } + + internal sealed class DbToDeviceTargetSummary + { + public string MachineId { get; set; } = ""; + public string MachineIp { get; set; } = ""; + public string MachineType { get; set; } = ""; + public int AlreadyRegistered { get; set; } + public int NewlyRegistered { get; set; } + public int Failed { get; set; } + public List FailedEmployees { get; } = new List(); + public List MissingMappings { get; } = new List(); + + public void AddFailure(string serial, string name, string department, string reason) + { + Failed++; + FailedEmployees.Add(new DbToDeviceFailedEmployee + { + SerialNumber = serial ?? "", + Name = name ?? "", + Department = department ?? "", + MachineId = MachineId, + MachineIp = MachineIp, + MachineType = MachineType, + Reason = reason ?? "" + }); + } + + public void AddMissingMapping(string serial) + { + MissingMappings.Add(new DbToDeviceMissingMappingEmployee + { + SerialNumber = serial ?? "", + MachineId = MachineId, + MachineIp = MachineIp, + MachineType = MachineType + }); + } + + public string FormatReport() + { + var sb = new StringBuilder(); + sb.AppendLine("================================="); + sb.AppendLine("DB_TO_DEVICE SUMMARY"); + sb.AppendLine("================================="); + sb.AppendLine(); + sb.AppendLine("Target:"); + sb.AppendLine("Machine ID: " + MachineId); + sb.AppendLine("IP: " + MachineIp); + sb.AppendLine("Type: " + MachineType); + sb.AppendLine(); + sb.AppendLine("Already Registered: " + AlreadyRegistered); + sb.AppendLine(); + sb.AppendLine("Newly Registered: " + NewlyRegistered); + sb.AppendLine(); + sb.AppendLine("Failed: " + Failed); + sb.AppendLine(); + sb.AppendLine("Missing Employee Mapping: " + MissingMappings.Count); + if (MissingMappings.Count > 0) + { + sb.AppendLine(); + foreach (var miss in MissingMappings) + { + sb.AppendLine("Serial: " + miss.SerialNumber); + sb.AppendLine("Machine ID: " + miss.MachineId); + sb.AppendLine("Machine IP: " + miss.MachineIp); + sb.AppendLine("Machine Type: " + miss.MachineType); + sb.AppendLine("Reason: No employee row for serial_number_int"); + sb.AppendLine(); + } + } + else + { + sb.AppendLine(); + } + + if (FailedEmployees.Count > 0) + { + sb.AppendLine("Failed Employees:"); + sb.AppendLine(); + foreach (var fail in FailedEmployees) + { + sb.AppendLine("Serial: " + fail.SerialNumber); + sb.AppendLine("Name: " + fail.Name); + sb.AppendLine("Department: " + fail.Department); + sb.AppendLine("Machine ID: " + fail.MachineId); + sb.AppendLine("Machine IP: " + fail.MachineIp); + sb.AppendLine("Machine Type: " + fail.MachineType); + sb.AppendLine("Reason: " + fail.Reason); + sb.AppendLine(); + } + } + + return sb.ToString().TrimEnd() + Environment.NewLine; + } + } + + internal static class DbToDeviceSkipLogic + { + public static bool ShouldSkipAlreadyRegistered(ISet registeredSerials, string employeeSerial) + { + if (registeredSerials == null || string.IsNullOrWhiteSpace(employeeSerial)) + { + return false; + } + + return registeredSerials.Contains(employeeSerial.Trim()); + } + } +} diff --git a/DbToDeviceSummaryTests.cs b/DbToDeviceSummaryTests.cs new file mode 100644 index 0000000..8a92688 --- /dev/null +++ b/DbToDeviceSummaryTests.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; + +namespace HanvonF710XAttendanceService +{ + internal static class DbToDeviceSummaryTests + { + public static int RunAll() + { + int failed = 0; + failed += Run("existing user is skipped", TestExistingUserSkipped); + failed += Run("missing user is enrolled", TestMissingUserEnrolled); + failed += Run("summary contains required fields", TestSummaryContainsRequiredFields); + failed += Run("multiple machines produce separate summaries", TestMultipleMachineSummaries); + return failed; + } + + private static int Run(string name, Func test) + { + try + { + if (!test()) + { + Console.WriteLine("FAIL: " + name); + return 1; + } + + Console.WriteLine("PASS: " + name); + return 0; + } + catch (Exception ex) + { + Console.WriteLine("FAIL: " + name + " err=" + ex.Message); + return 1; + } + } + + private static bool TestExistingUserSkipped() + { + var registered = new HashSet(StringComparer.OrdinalIgnoreCase) { "15057", "6401" }; + return DbToDeviceSkipLogic.ShouldSkipAlreadyRegistered(registered, "15057") + && DbToDeviceSkipLogic.ShouldSkipAlreadyRegistered(registered, " 6401 ") + && !DbToDeviceSkipLogic.ShouldSkipAlreadyRegistered(registered, "99999"); + } + + private static bool TestMissingUserEnrolled() + { + var registered = new HashSet(StringComparer.OrdinalIgnoreCase) { "15057" }; + var summary = new DbToDeviceTargetSummary + { + MachineId = "105", + MachineIp = "192.168.90.223", + MachineType = "HANVON" + }; + + // Existing -> skip + if (DbToDeviceSkipLogic.ShouldSkipAlreadyRegistered(registered, "15057")) + { + summary.AlreadyRegistered++; + } + + // Missing -> enroll + if (!DbToDeviceSkipLogic.ShouldSkipAlreadyRegistered(registered, "99999")) + { + summary.NewlyRegistered++; + } + + return summary.AlreadyRegistered == 1 && summary.NewlyRegistered == 1; + } + + private static bool TestSummaryContainsRequiredFields() + { + var summary = new DbToDeviceTargetSummary + { + MachineId = "105", + MachineIp = "192.168.90.223", + MachineType = "HANVON", + AlreadyRegistered = 620, + NewlyRegistered = 30 + }; + summary.AddMissingMapping("88888"); + summary.AddFailure("15057", "John Smith", "Production", "Employee photo not found"); + summary.AddFailure("6401", "ABC XYZ", "Quality", "DUPLICATE_FACE existing_device_id=6400"); + + string report = summary.FormatReport(); + return report.Contains("DB_TO_DEVICE SUMMARY") + && report.Contains("Machine ID: 105") + && report.Contains("IP: 192.168.90.223") + && report.Contains("Type: HANVON") + && report.Contains("Already Registered: 620") + && report.Contains("Newly Registered: 30") + && report.Contains("Failed: 2") + && report.Contains("Missing Employee Mapping: 1") + && report.Contains("Serial: 15057") + && report.Contains("Name: John Smith") + && report.Contains("Department: Production") + && report.Contains("Reason: Employee photo not found") + && report.Contains("DUPLICATE_FACE existing_device_id=6400"); + } + + private static bool TestMultipleMachineSummaries() + { + var a = new DbToDeviceTargetSummary + { + MachineId = "105", + MachineIp = "192.168.90.223", + MachineType = "HANVON", + AlreadyRegistered = 10, + NewlyRegistered = 2 + }; + var b = new DbToDeviceTargetSummary + { + MachineId = "106", + MachineIp = "10.0.1.7", + MachineType = "HANVON", + AlreadyRegistered = 5, + NewlyRegistered = 7 + }; + b.AddFailure("6401", "ABC XYZ", "Quality", "DUPLICATE_FACE existing_device_id=6400"); + + string reportA = a.FormatReport(); + string reportB = b.FormatReport(); + + return reportA.Contains("Machine ID: 105") + && reportA.Contains("IP: 192.168.90.223") + && reportA.Contains("Already Registered: 10") + && reportA.Contains("Newly Registered: 2") + && !reportA.Contains("10.0.1.7") + && reportB.Contains("Machine ID: 106") + && reportB.Contains("IP: 10.0.1.7") + && reportB.Contains("Already Registered: 5") + && reportB.Contains("Newly Registered: 7") + && reportB.Contains("Failed: 1") + && !reportB.Contains("192.168.90.223"); + } + } +}