Fix name sanitization to be based on specific tags instead of on input tag filtering

This commit is contained in:
Campbell Alden 2023-07-24 13:56:56 +09:00
parent e6ffc866c4
commit d232763850
8 changed files with 53 additions and 12 deletions

2
Cargo.lock generated
View file

@ -173,7 +173,7 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]] [[package]]
name = "day-reporter" name = "day-reporter"
version = "1.0.0" version = "1.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "day-reporter" name = "day-reporter"
version = "1.0.0" version = "1.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -8,7 +8,6 @@ use reporter::{MarkdownReporter, Reporter, Resolution, ReportOptions};
use things::task::{Task, Status}; use things::task::{Task, Status};
use anyhow::Result; use anyhow::Result;
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use names::sanitize_names;
#[derive(ValueEnum, Copy, Clone, Eq, PartialEq)] #[derive(ValueEnum, Copy, Clone, Eq, PartialEq)]
enum Modes { enum Modes {
@ -22,12 +21,13 @@ enum Modes {
} }
impl Modes { impl Modes {
fn format_tasks(&self, tasks: Vec<Task>, tags: &Vec<String>) -> String { fn format_tasks(&self, tasks: Vec<Task>, tags: &Vec<String>, sanitize_names: bool) -> String {
match self { match self {
Modes::Morning => { Modes::Morning => {
let task_report = MarkdownReporter.report(tasks, &ReportOptions { let task_report = MarkdownReporter.report(tasks, &ReportOptions {
resolution: Resolution::FullTask, resolution: Resolution::FullTask,
tags: tags.to_vec(), tags: tags.to_vec(),
sanitize_names,
}); });
format!("{}\n\n{}", emoji::pick(3).join(" "), task_report) format!("{}\n\n{}", emoji::pick(3).join(" "), task_report)
}, },
@ -35,6 +35,7 @@ impl Modes {
let task_report = MarkdownReporter.report(tasks, &ReportOptions { let task_report = MarkdownReporter.report(tasks, &ReportOptions {
resolution: Resolution::FullTask, resolution: Resolution::FullTask,
tags: tags.to_vec(), tags: tags.to_vec(),
sanitize_names,
}); });
format!("Stopping now\n\n{}", task_report) format!("Stopping now\n\n{}", task_report)
}, },
@ -48,6 +49,7 @@ impl Modes {
let task_report = MarkdownReporter.report(further_filtered, &ReportOptions { let task_report = MarkdownReporter.report(further_filtered, &ReportOptions {
resolution: Resolution::Project, resolution: Resolution::Project,
tags: tags.to_vec(), tags: tags.to_vec(),
sanitize_names,
}); });
format!("*Cycle Report*\n\n{}", task_report) format!("*Cycle Report*\n\n{}", task_report)
}, },
@ -72,6 +74,12 @@ struct CliArgs {
#[arg(short, long, default_value_t = Modes::default())] #[arg(short, long, default_value_t = Modes::default())]
#[clap(value_enum)] #[clap(value_enum)]
mode: Modes, mode: Modes,
/// By default, any @<name> style tags will be sanitized in the output to avoid @-mentions in
/// slack. This is done by replacing vowel characters with look unicode lookalikes. If this
/// flag is set then the names will be passed through unsanitized
#[arg(long, default_value_t = false)]
no_sanitize: bool,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
@ -84,9 +92,8 @@ fn main() -> Result<()> {
let reported: Vec<Task> = tasks.into_iter().filter(|task| { let reported: Vec<Task> = tasks.into_iter().filter(|task| {
args.tags.iter().all(|tag| task.has_tag(tag)) args.tags.iter().all(|tag| task.has_tag(tag))
}).collect(); }).collect();
let report = args.mode.format_tasks(reported, &args.tags); let report = args.mode.format_tasks(reported, &args.tags, !args.no_sanitize);
let sanitized = sanitize_names(&report, &args.tags); println!("{report}");
println!("{sanitized}");
Ok(()) Ok(())
} }

View file

@ -1,4 +1,5 @@
use crate::things::task::Task; use crate::things::task::Task;
use crate::names::sanitize_names;
/// Given a notes field and a list of possible tags for sections, return the content of triple tick /// Given a notes field and a list of possible tags for sections, return the content of triple tick
/// blocks containing those tags /// blocks containing those tags
@ -36,6 +37,7 @@ pub struct ProjectTree {
id: String, id: String,
title: String, title: String,
notes: Option<String>, notes: Option<String>,
tags: Vec<String>,
tasks: Vec<Task>, tasks: Vec<Task>,
} }
@ -72,6 +74,7 @@ impl AreaTree {
id: project.id.clone(), id: project.id.clone(),
title: project.title.clone(), title: project.title.clone(),
notes: project.notes.clone(), notes: project.notes.clone(),
tags: project.tags.clone(),
tasks: vec![task], tasks: vec![task],
}); });
} }
@ -103,6 +106,7 @@ impl ThingsTree {
id: project.id.clone(), id: project.id.clone(),
title: project.title.clone(), title: project.title.clone(),
notes: project.notes.clone(), notes: project.notes.clone(),
tags: project.tags.clone(),
tasks: vec![task], tasks: vec![task],
}); });
} }
@ -130,6 +134,7 @@ pub enum Resolution {
pub struct ReportOptions { pub struct ReportOptions {
pub resolution: Resolution, pub resolution: Resolution,
pub tags: Vec<String>, pub tags: Vec<String>,
pub sanitize_names: bool,
} }
pub trait Reporter { pub trait Reporter {
@ -171,7 +176,11 @@ impl Reporter for MarkdownReporter {
.map(|l| format!("\n{}- {}", String::from(" ").repeat(depth + 4), l)) .map(|l| format!("\n{}- {}", String::from(" ").repeat(depth + 4), l))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(""); .join("");
format!("\n{}- {}{}", String::from(" ").repeat(depth), task.title, relevant_notes) let mut output = format!("\n{}- {}{}", String::from(" ").repeat(depth), task.title, relevant_notes);
if options.sanitize_names {
output = sanitize_names(&output, &task.tags);
}
return output;
} }
fn report_project(&mut self, project: &ProjectTree, depth: usize, options: &ReportOptions) -> String { fn report_project(&mut self, project: &ProjectTree, depth: usize, options: &ReportOptions) -> String {
let resolution = &options.resolution; let resolution = &options.resolution;
@ -182,7 +191,7 @@ impl Reporter for MarkdownReporter {
.map(|l| format!("\n{}- {}", String::from(" ").repeat(depth + 4), l)) .map(|l| format!("\n{}- {}", String::from(" ").repeat(depth + 4), l))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(""); .join("");
match resolution { let mut output = match resolution {
Resolution::FullTask => { Resolution::FullTask => {
let tasks = project.tasks let tasks = project.tasks
.iter() .iter()
@ -194,7 +203,13 @@ impl Reporter for MarkdownReporter {
Resolution::Project => { Resolution::Project => {
format!("{}- {}{}", String::from(" ").repeat(depth), project.title, relevant_notes) format!("{}- {}{}", String::from(" ").repeat(depth), project.title, relevant_notes)
} }
};
if options.sanitize_names {
output = sanitize_names(&output, &project.tags);
} }
return output;
} }
fn report_single_area(&mut self, area: &AreaTree, options: &ReportOptions) -> String { fn report_single_area(&mut self, area: &AreaTree, options: &ReportOptions) -> String {
let project_reports = area.projects let project_reports = area.projects

View file

@ -27,7 +27,13 @@ logbook.filter(task => {
notes: todo.notes() || null, notes: todo.notes() || null,
status: todo.status(), status: todo.status(),
completion_date: todo.completionDate(), completion_date: todo.completionDate(),
project: proj && { id: proj.id(), title: proj.name(), status: proj.status(), notes: proj.notes() }, project: proj && {
id: proj.id(),
title: proj.name(),
status: proj.status(),
notes: proj.notes(),
tags: proj.tagNames().split(', '),
},
area: area && { id: area.id(), title: area.name() }, area: area && { id: area.id(), title: area.name() },
tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t), tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t),
}); });

View file

@ -27,7 +27,13 @@ logbook.filter(task => {
notes: todo.notes() || null, notes: todo.notes() || null,
status: todo.status(), status: todo.status(),
completion_date: todo.completionDate(), completion_date: todo.completionDate(),
project: proj && { id: proj.id(), title: proj.name(), status: proj.status(), notes: proj.notes() }, project: proj && {
id: proj.id(),
title: proj.name(),
status: proj.status(),
notes: proj.notes(),
tags: proj.tagNames().split(', '),
},
area: area && { id: area.id(), title: area.name() }, area: area && { id: area.id(), title: area.name() },
tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t), tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t),
}); });

View file

@ -19,6 +19,7 @@ pub enum Status {
pub struct Project { pub struct Project {
pub id: String, pub id: String,
pub title: String, pub title: String,
pub tags: Vec<String>,
pub notes: Option<String>, pub notes: Option<String>,
pub status: Status, pub status: Status,
} }

View file

@ -15,7 +15,13 @@ today.forEach(todo => {
notes: todo.notes() || null, notes: todo.notes() || null,
status: todo.status(), status: todo.status(),
completion_date: todo.completionDate(), completion_date: todo.completionDate(),
project: proj && { id: proj.id(), title: proj.name(), status: proj.status(), notes: proj.notes() }, project: proj && {
id: proj.id(),
title: proj.name(),
status: proj.status(),
notes: proj.notes(),
tags: proj.tagNames().split(', '),
},
area: area && { id: area.id(), title: area.name() }, area: area && { id: area.id(), title: area.name() },
tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t), tags: [...tags, ...todo.tagNames().split(', ')].filter(t => t),
}); });