| Vorher (Original) | Nachher (Synthese) |
| {System.Web.HttpUtility.HtmlEncode(original.Explanation)} | "); + sb.Append($"{System.Web.HttpUtility.HtmlEncode(finalResult.Explanation)} |
| ");
+ sb.Append(" URSACHEN ");
+ foreach (var c in original.Causes) sb.Append($"• {System.Web.HttpUtility.HtmlEncode(c)} ");
+ sb.Append(" | ");
+ sb.Append(" URSACHEN ");
+ foreach (var c in finalResult.Causes) sb.Append($"• {System.Web.HttpUtility.HtmlEncode(c)} ");
+ sb.Append(" |
| ");
+ sb.Append(" WAS ZU TUN IST ");
+ foreach (var s in original.NextSteps) sb.Append($"• {System.Web.HttpUtility.HtmlEncode(s)} ");
+ sb.Append(" | ");
+ sb.Append(" WAS ZU TUN IST ");
+ foreach (var s in finalResult.NextSteps) sb.Append($"• {System.Web.HttpUtility.HtmlEncode(s)} ");
+ sb.Append(" |
| + |
Hallo {name},
+Kurze Erinnerung — die heutige Alarmprüfung (Stapel {batch.BatchNumber}) schließt um 8 Uhr morgen früh. Es dauert nur 15 Minuten!
+ +inesco Energy Monitor
+ + """; + await SendEmailAsync(email, subject, html); + } + + private static async Task SendAdminDailySummaryAsync(BatchRecord batch, int totalReviewed) + { + var submitted = batch.Submissions.Where(kv => kv.Value != null).Select(kv => kv.Key).ToList(); + var totalBatches = (int)Math.Ceiling((double)AllAlarmKeys.Length / BatchSize); + var pct = Math.Round((double)totalReviewed / AllAlarmKeys.Length * 100, 1); + var subject = $"[Alarm Review] Batch {batch.BatchNumber}/{totalBatches} synthesized — {totalReviewed}/{AllAlarmKeys.Length} alarms done ({pct}%)"; + + // Build before/after section for each alarm in the batch + var beforeAfterRows = new StringBuilder(); + foreach (var key in batch.AlarmKeys) + { + var original = AlarmKnowledgeBase.TryGetDiagnosis(key); + var improved = batch.ImprovedEntries.TryGetValue(key, out var imp) ? imp : null; + var label = GermanName(key); + var changed = improved != null && + (improved.Explanation != original?.Explanation || + !improved.Causes.SequenceEqual(original?.Causes ?? Array.Empty{DateTime.Now:yyyy-MM-dd HH:mm}
+| Reviewers responded | +{submitted.Count}/{Reviewers.Length} ({string.Join(", ", submitted)}) |
| Overall progress | +{totalReviewed} / {AllAlarmKeys.Length} ({pct}%) |
Rot = Original · Grün = synthetisiertes Ergebnis
+inesco Energy Monitor
+ + """; + + await SendEmailAsync(AdminEmail, subject, html); + } + + private static async Task SendAdminStallAlertAsync(BatchRecord batch) + { + var subject = $"[Alarm Review] ⚠️ Batch {batch.BatchNumber} stalled — no responses (resend #{batch.ResendCount})"; + var html = $""" + + +No reviewer has responded to Batch {batch.BatchNumber}. The batch has been resent (attempt #{batch.ResendCount}).
+Alarms: {string.Join(", ", batch.AlarmKeys)}
+ + """; + await SendEmailAsync(AdminEmail, subject, html); + } + + private static async Task SendAdminCompletionEmailAsync(AlarmReviewProgress progress) + { + var subject = "✅ Alarm Review Campaign Complete — Ready for Cutover"; + var html = $""" + + +All 229 alarms have been reviewed and synthesized.
+AlarmKnowledgeBaseChecked.cs is ready for cutover on the server.
+curl "{BaseUrl}/DownloadCheckedKnowledgeBase?authToken=YOUR_TOKEN" -o AlarmKnowledgeBaseChecked.cscsharp/App/Backend/Services/AlarmKnowledgeBase.csAlarmKnowledgeBaseChecked → AlarmKnowledgeBasedotnet build && ./deploy.sh| Started | {progress.StartedAt[..10]} |
| Completed | {DateTime.Now:yyyy-MM-dd} |
| Total batches | {progress.Batches.Count} |
| Alarms reviewed | 229 |