feat: automatically add tags from task properties
This commit is contained in:
parent
44feea9894
commit
2cec689bf1
|
@ -146,10 +146,10 @@ pub(crate) fn to_prio_tag(value: Prio) -> Tag {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_tags() {
|
fn test_extract_tags() {
|
||||||
assert_eq!(extract_tags("Hello from #mars with #greetings *4 # # yeah done-it"),
|
assert_eq!(extract_tags("Hello from #mars with #greetings #yeah *4 # # yeah done-it"),
|
||||||
("Hello from #mars with #greetings".to_string(),
|
("Hello from #mars with #greetings #yeah".to_string(),
|
||||||
["mars", "greetings", "yeah", "done-it"].into_iter().map(to_hashtag)
|
std::iter::once(Tag::custom(TagKind::Custom(Cow::from(PRIO)), [40.to_string()]))
|
||||||
.chain(std::iter::once(Tag::custom(TagKind::Custom(Cow::from(PRIO)), [40.to_string()]))).collect()));
|
.chain(["done-it", "greetings", "mars", "yeah"].into_iter().map(to_hashtag)).collect()));
|
||||||
assert_eq!(extract_tags("So tagless #"),
|
assert_eq!(extract_tags("So tagless #"),
|
||||||
("So tagless".to_string(), vec![]));
|
("So tagless".to_string(), vec![]));
|
||||||
}
|
}
|
|
@ -677,7 +677,7 @@ async fn main() -> Result<()> {
|
||||||
let filtered =
|
let filtered =
|
||||||
tasks.get_filtered(pos, |t| {
|
tasks.get_filtered(pos, |t| {
|
||||||
transform(&t.event.content).contains(&remaining) ||
|
transform(&t.event.content).contains(&remaining) ||
|
||||||
t.tags.iter().flatten().any(
|
t.get_hashtags().any(
|
||||||
|tag| tag.content().is_some_and(|s| transform(s).contains(&remaining)))
|
|tag| tag.content().is_some_and(|s| transform(s).contains(&remaining)))
|
||||||
});
|
});
|
||||||
if filtered.len() == 1 {
|
if filtered.len() == 1 {
|
||||||
|
|
34
src/task.rs
34
src/task.rs
|
@ -10,7 +10,7 @@ use colored::{ColoredString, Colorize};
|
||||||
use itertools::Either::{Left, Right};
|
use itertools::Either::{Left, Right};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use nostr_sdk::{Event, EventId, Kind, Tag, TagStandard, Timestamp};
|
use nostr_sdk::{Alphabet, Event, EventId, Kind, Tag, TagStandard, Timestamp};
|
||||||
|
|
||||||
use crate::helpers::{format_timestamp_local, some_non_empty};
|
use crate::helpers::{format_timestamp_local, some_non_empty};
|
||||||
use crate::kinds::{is_hashtag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
use crate::kinds::{is_hashtag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
||||||
|
@ -23,8 +23,8 @@ pub static MARKER_PROPERTY: &str = "property";
|
||||||
pub(crate) struct Task {
|
pub(crate) struct Task {
|
||||||
/// Event that defines this task
|
/// Event that defines this task
|
||||||
pub(crate) event: Event,
|
pub(crate) event: Event,
|
||||||
/// Cached sorted tags of the event with references remove - do not modify!
|
/// Cached sorted tags of the event with references removed
|
||||||
pub(crate) tags: Option<BTreeSet<Tag>>,
|
tags: Option<BTreeSet<Tag>>,
|
||||||
/// Task references derived from the event tags
|
/// Task references derived from the event tags
|
||||||
refs: Vec<(String, EventId)>,
|
refs: Vec<(String, EventId)>,
|
||||||
/// Events belonging to this task, such as state updates and notes
|
/// Events belonging to this task, such as state updates and notes
|
||||||
|
@ -172,16 +172,26 @@ impl Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_tags<P>(&self, predicate: P) -> Option<String>
|
pub(crate) fn get_hashtags(&self) -> impl Iterator<Item=&Tag> {
|
||||||
|
self.tags().filter(|t| is_hashtag(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> impl Iterator<Item=&Tag> {
|
||||||
|
self.props.iter().flat_map(|e| e.tags.iter()
|
||||||
|
.filter(|t| t.single_letter_tag().is_none_or(|s| s.character != Alphabet::E)))
|
||||||
|
.chain(self.tags.iter().flatten())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_tags<P>(&self, predicate: P) -> String
|
||||||
where
|
where
|
||||||
P: FnMut(&&Tag) -> bool,
|
P: FnMut(&&Tag) -> bool,
|
||||||
{
|
{
|
||||||
self.tags.as_ref().map(|tags| {
|
self.tags()
|
||||||
tags.iter()
|
.filter(predicate)
|
||||||
.filter(predicate)
|
.map(|t| t.content().unwrap().to_string())
|
||||||
.map(|t| t.content().unwrap().to_string())
|
.sorted_unstable()
|
||||||
.join(" ")
|
.dedup()
|
||||||
})
|
.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get(&self, property: &str) -> Option<String> {
|
pub(crate) fn get(&self, property: &str) -> Option<String> {
|
||||||
|
@ -198,8 +208,8 @@ impl Task {
|
||||||
"status" => self.state_label().map(|c| c.to_string()),
|
"status" => self.state_label().map(|c| c.to_string()),
|
||||||
"desc" => self.descriptions().last().cloned(),
|
"desc" => self.descriptions().last().cloned(),
|
||||||
"description" => Some(self.descriptions().join(" ")),
|
"description" => Some(self.descriptions().join(" ")),
|
||||||
"hashtags" => self.filter_tags(|tag| { is_hashtag(tag) }),
|
"hashtags" => Some(self.join_tags(|tag| { is_hashtag(tag) })),
|
||||||
"tags" => self.filter_tags(|_| true),
|
"tags" => Some(self.join_tags(|_| true)),
|
||||||
"alltags" => Some(format!("{:?}", self.tags)),
|
"alltags" => Some(format!("{:?}", self.tags)),
|
||||||
"refs" => Some(format!("{:?}", self.refs.iter().map(|re| format!("{}: {}", re.0, re.1)).collect_vec())),
|
"refs" => Some(format!("{:?}", self.refs.iter().map(|re| format!("{}: {}", re.0, re.1)).collect_vec())),
|
||||||
"props" => Some(format!(
|
"props" => Some(format!(
|
||||||
|
|
20
src/tasks.rs
20
src/tasks.rs
|
@ -241,8 +241,7 @@ impl TasksRelay {
|
||||||
pub(crate) fn all_hashtags(&self) -> impl Iterator<Item=&str> {
|
pub(crate) fn all_hashtags(&self) -> impl Iterator<Item=&str> {
|
||||||
self.tasks.values()
|
self.tasks.values()
|
||||||
.filter(|t| t.pure_state() != State::Closed)
|
.filter(|t| t.pure_state() != State::Closed)
|
||||||
.filter_map(|t| t.tags.as_ref()).flatten()
|
.flat_map(|t| t.get_hashtags())
|
||||||
.filter(|tag| is_hashtag(tag))
|
|
||||||
.filter_map(|tag| tag.content().map(|s| s.trim()))
|
.filter_map(|tag| tag.content().map(|s| s.trim()))
|
||||||
.sorted_unstable()
|
.sorted_unstable()
|
||||||
.dedup()
|
.dedup()
|
||||||
|
@ -449,14 +448,11 @@ impl TasksRelay {
|
||||||
self.priority.is_none_or(|prio| {
|
self.priority.is_none_or(|prio| {
|
||||||
task.priority().unwrap_or(DEFAULT_PRIO) >= prio
|
task.priority().unwrap_or(DEFAULT_PRIO) >= prio
|
||||||
}) &&
|
}) &&
|
||||||
task.tags.as_ref().map_or(true, |tags| {
|
!task.get_hashtags().any(|tag| self.tags_excluded.contains(tag)) &&
|
||||||
!tags.iter().any(|tag| self.tags_excluded.contains(tag))
|
(self.tags.is_empty() || {
|
||||||
}) &&
|
let mut iter = task.get_hashtags().sorted_unstable();
|
||||||
(self.tags.is_empty() ||
|
self.tags.iter().all(|tag| iter.any(|t| t == tag))
|
||||||
task.tags.as_ref().map_or(false, |tags| {
|
})
|
||||||
let mut iter = tags.iter();
|
|
||||||
self.tags.iter().all(|tag| iter.any(|t| t == tag))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn filtered_tasks<'a>(&'a self, position: Option<&'a EventId>, sparse: bool) -> Vec<&'a Task> {
|
pub(crate) fn filtered_tasks<'a>(&'a self, position: Option<&'a EventId>, sparse: bool) -> Vec<&'a Task> {
|
||||||
|
@ -1654,6 +1650,10 @@ mod tasks_test {
|
||||||
tasks.move_to(Some(parent));
|
tasks.move_to(Some(parent));
|
||||||
let sub = tasks.make_task("sub # tag2");
|
let sub = tasks.make_task("sub # tag2");
|
||||||
assert_eq!(tasks.all_hashtags().collect_vec(), vec!["tag1", "tag2"]);
|
assert_eq!(tasks.all_hashtags().collect_vec(), vec!["tag1", "tag2"]);
|
||||||
|
tasks.make_note("note with #tag3 # yeah");
|
||||||
|
assert_eq!(tasks.all_hashtags().collect_vec(), vec!["tag1", "tag2", "tag3", "yeah"]);
|
||||||
|
tasks.update_state("Done #yei", State::Done);
|
||||||
|
// TODO assert_eq!(tasks.all_hashtags().collect_vec(), vec!["tag1", "tag2", "tag3", "yeah", "yei"]);
|
||||||
tasks.update_state("Closing Down", State::Closed);
|
tasks.update_state("Closing Down", State::Closed);
|
||||||
assert_eq!(tasks.get_by_id(&sub).unwrap().pure_state(), State::Closed);
|
assert_eq!(tasks.get_by_id(&sub).unwrap().pure_state(), State::Closed);
|
||||||
assert_eq!(tasks.all_hashtags().next(), None);
|
assert_eq!(tasks.all_hashtags().next(), None);
|
||||||
|
|
Loading…
Reference in New Issue