server: render text extract calendar info w/ ics template

This commit is contained in:
Bill Thiede 2025-08-27 17:08:02 -07:00
parent 3fd41062d7
commit 3311f2fc00

View File

@ -189,10 +189,6 @@ pub fn extract_calendar_metadata_from_mail(
} }
} }
// Fallback: if body_html is still None, generate a minimal calendar HTML using all available metadata // Fallback: if body_html is still None, generate a minimal calendar HTML using all available metadata
let summary_val = summary.clone().unwrap_or_default();
let organizer_val = organizer.clone().unwrap_or_default();
let start_val = start_date.clone().unwrap_or_default();
let end_val = end_date.clone().unwrap_or_default();
// Improved recurrence detection: check for common recurrence phrases in subject, HTML, and plain text body // Improved recurrence detection: check for common recurrence phrases in subject, HTML, and plain text body
let mut has_recurrence = false; let mut has_recurrence = false;
let recurrence_phrases = [ let recurrence_phrases = [
@ -220,31 +216,54 @@ pub fn extract_calendar_metadata_from_mail(
} }
} }
} }
let recurrence_html: &str = if has_recurrence { "<div class='ical-recurrence'>Repeats</div>" } else { "" };
let minimal_html = format!(
r#"<div class='ical-flex'><div class='ical-summary'>{}</div><div class='ical-organizer'>{}</div><div class='ical-dates'>{} to {}</div>{}</div>"#,
html_escape::encode_text(&summary_val),
html_escape::encode_text(&organizer_val),
html_escape::encode_text(&start_val),
html_escape::encode_text(&end_val),
recurrence_html
);
let needs_ical_flex = summary.is_some() || start_date.is_some() || end_date.is_some() || has_recurrence; let needs_ical_flex = summary.is_some() || start_date.is_some() || end_date.is_some() || has_recurrence;
if needs_ical_flex { if needs_ical_flex {
let summary_val = summary.clone().unwrap_or_default();
let organizer_val = organizer.clone().unwrap_or_default();
let start_val = start_date.clone().unwrap_or_default();
let end_val = end_date.clone().unwrap_or_default();
let recurrence_display = if has_recurrence { "Repeats".to_string() } else { String::new() };
let template = IcalSummaryTemplate {
summary: &summary_val,
local_fmt_start: &start_val,
local_fmt_end: &end_val,
organizer: &organizer_val,
organizer_cn: "",
all_days: vec![],
event_days: vec![],
caption: String::new(),
description_paragraphs: &[],
today: Some(chrono::Local::now().date_naive()),
recurrence_display,
};
let fallback_html = template.render().unwrap_or_else(|_| String::from("<div class='ical-flex'></div>"));
match &mut body_html { match &mut body_html {
Some(existing) => { Some(existing) => {
if !existing.starts_with(&minimal_html) { if !existing.starts_with(&fallback_html) {
*existing = format!("{}{}", minimal_html, existing); *existing = format!("{}{}", fallback_html, existing);
} }
}, },
None => { None => {
body_html = Some(minimal_html); body_html = Some(fallback_html);
} }
} }
} }
// Final guarantee: if body_html is still None, set to minimal ical-flex HTML with empty fields // Final guarantee: if body_html is still None, set to minimal ical-flex HTML with empty fields using the template
if body_html.is_none() { if body_html.is_none() {
body_html = Some("<div class='ical-flex'><div class='ical-summary'></div><div class='ical-organizer'></div><div class='ical-dates'> to </div></div>".to_string()); let template = IcalSummaryTemplate {
summary: "",
local_fmt_start: "",
local_fmt_end: "",
organizer: "",
organizer_cn: "",
all_days: vec![],
event_days: vec![],
caption: String::new(),
description_paragraphs: &[],
today: Some(chrono::Local::now().date_naive()),
recurrence_display: String::new(),
};
body_html = Some(template.render().unwrap_or_else(|_| String::from("<div class='ical-flex'></div>")));
} }
// Improved fallback: extract summary, start_date, end_date, and recurrence from subject/body if not found // Improved fallback: extract summary, start_date, end_date, and recurrence from subject/body if not found
@ -314,11 +333,7 @@ pub fn extract_calendar_metadata_from_mail(
} }
} }
// Try to detect recurrence from subject // Try to detect recurrence from subject
if recurrence_html.is_empty() { // recurrence detection and rendering is now handled by the template logic
if subject.to_lowercase().contains("recurr") || subject.to_lowercase().contains("repeat") {
// recurrence_html assignment removed; handled at HTML generation
}
}
} }
// Try to extract summary from body if still missing // Try to extract summary from body if still missing
if summary.is_none() { if summary.is_none() {
@ -2154,19 +2169,20 @@ mod tests {
// Calendar widget should be rendered // Calendar widget should be rendered
let html = meta.body_html.expect("body_html"); let html = meta.body_html.expect("body_html");
println!("Rendered HTML for verification:\n{}", html); println!("Rendered HTML for verification:\n{}", html);
// Check that the HTML contains the summary, organizer, start, and end times // Check that the HTML contains the summary, organizer, start, and end times with labels
assert!(html.contains(">McClure BLT<"), "HTML should contain the summary/title"); assert!(html.contains("<b>Summary:</b> McClure BLT"), "HTML should contain the labeled summary/title");
assert!(html.contains(">calendar-notification@google.com<"), "HTML should contain the organizer"); assert!(html.contains("<b>Organizer:</b> calendar-notification@google.com"), "HTML should contain the labeled organizer");
assert!(html.contains(">20250911 to 20260131<"), "HTML should contain the start and end times"); assert!(html.contains("<b>Start:</b> 20250911"), "HTML should contain the labeled start time");
assert!(html.contains("<b>End:</b> 20260131"), "HTML should contain the labeled end time");
if !html.contains("ical-flex") { if !html.contains("ical-flex") {
println!("FAIL: html did not contain 'ical-flex':\n{}", html); println!("FAIL: html did not contain 'ical-flex':\n{}", html);
} }
assert!(html.contains("ical-flex"), "Calendar widget should be rendered"); assert!(html.contains("ical-flex"), "Calendar widget should be rendered");
// Recurrence info should be present // Recurrence info should be present
if !(html.contains("Repeats") || html.contains("recurr") || html.contains("RRULE")) { if !(html.contains("<b>Repeats:</b> Repeats") || html.contains("recurr") || html.contains("RRULE")) {
println!("FAIL: html did not contain recurrence info:\n{}", html); println!("FAIL: html did not contain recurrence info:\n{}", html);
} }
assert!(html.contains("Repeats") || html.contains("recurr") || html.contains("RRULE"), "Recurrence info should be present in HTML"); assert!(html.contains("<b>Repeats:</b> Repeats") || html.contains("recurr") || html.contains("RRULE"), "Recurrence info should be present in HTML");
} }
use super::*; use super::*;
#[test] #[test]