Compare commits
4 commits
0de4e2e55d
...
8f9a0d2fa9
Author | SHA1 | Date | |
---|---|---|---|
|
8f9a0d2fa9 | ||
|
fe6ac592be | ||
|
cbeba49bb3 | ||
|
7e3039ef1a |
6 changed files with 52 additions and 14 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1561,7 +1561,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mostr"
|
name = "mostr"
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"colog",
|
"colog",
|
||||||
|
|
|
@ -5,7 +5,7 @@ repository = "https://forge.ftt.gmbh/janek/mostr"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "GPL 3.0"
|
license = "GPL 3.0"
|
||||||
authors = ["melonion"]
|
authors = ["melonion"]
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
rust-version = "1.82"
|
rust-version = "1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "mostr"
|
default-run = "mostr"
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -5,7 +5,7 @@ use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERT
|
||||||
use crate::task::{State, StateChange, Task, MARKER_PROPERTY};
|
use crate::task::{State, StateChange, Task, MARKER_PROPERTY};
|
||||||
use crate::tasks::{referenced_event, PropertyCollection, StateFilter, TasksRelay};
|
use crate::tasks::{referenced_event, PropertyCollection, StateFilter, TasksRelay};
|
||||||
|
|
||||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
use chrono::{DateTime, Local, TimeDelta, TimeZone, Utc};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use env_logger::{Builder, Target, WriteStyle};
|
use env_logger::{Builder, Target, WriteStyle};
|
||||||
|
@ -27,6 +27,7 @@ use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
use std::ops::Add;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -363,9 +364,7 @@ async fn main() -> Result<()> {
|
||||||
Some(_) =>
|
Some(_) =>
|
||||||
if let Some((left, arg)) = command.split_once("@") {
|
if let Some((left, arg)) = command.split_once("@") {
|
||||||
if !arg.contains(|s: char| s.is_alphabetic()) {
|
if !arg.contains(|s: char| s.is_alphabetic()) {
|
||||||
let pos = tasks.get_position_timestamped();
|
if let Some(time) = tasks.parse_tracking_stamp_relative(arg) {
|
||||||
let time = pos.1.and_then(|_| Local.timestamp_opt(pos.0.as_u64() as i64, 0).earliest());
|
|
||||||
if let Some(time) = parse_tracking_stamp(arg, time) {
|
|
||||||
command = left.to_string();
|
command = left.to_string();
|
||||||
tasks.custom_time = Some(time);
|
tasks.custom_time = Some(time);
|
||||||
}
|
}
|
||||||
|
@ -684,9 +683,7 @@ async fn main() -> Result<()> {
|
||||||
match arg {
|
match arg {
|
||||||
None => tasks.move_to(None),
|
None => tasks.move_to(None),
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
let pos = tasks.get_position_timestamped();
|
if tasks.parse_tracking_stamp_relative(arg)
|
||||||
let time = pos.1.and_then(|_| Local.timestamp_opt(pos.0.as_u64() as i64, 0).earliest());
|
|
||||||
if parse_tracking_stamp(arg, time)
|
|
||||||
.and_then(|stamp| tasks.track_at(stamp, None)).is_some() {
|
.and_then(|stamp| tasks.track_at(stamp, None)).is_some() {
|
||||||
println!("{}", tasks.times_tracked(15));
|
println!("{}", tasks.times_tracked(15));
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,13 +87,17 @@ impl Task {
|
||||||
|
|
||||||
pub(crate) fn get_participants(&self) -> impl Iterator<Item=PublicKey> + '_ {
|
pub(crate) fn get_participants(&self) -> impl Iterator<Item=PublicKey> + '_ {
|
||||||
self.tags()
|
self.tags()
|
||||||
.filter(|t| t.kind() == TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::P)))
|
.filter(|t| t.kind() == TagKind::p())
|
||||||
.filter_map(|t| t.content()
|
.filter_map(|t| t.content()
|
||||||
.and_then(|c| PublicKey::from_str(c).inspect_err(|e| warn!("Unparseable pubkey in {:?}", t)).ok()))
|
.and_then(|c| PublicKey::from_str(c).inspect_err(|e| warn!("Unparseable pubkey in {:?}", t)).ok()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_owner(&self) -> PublicKey {
|
pub(crate) fn get_assignee(&self) -> Option<PublicKey> {
|
||||||
self.get_participants().next()
|
self.get_participants().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_owner(&self) -> PublicKey {
|
||||||
|
self.get_assignee()
|
||||||
.unwrap_or_else(|| self.event.pubkey)
|
.unwrap_or_else(|| self.event.pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
src/tasks.rs
19
src/tasks.rs
|
@ -16,7 +16,7 @@ use crate::tasks::children_traversal::ChildrenTraversal;
|
||||||
use crate::tasks::durations::{referenced_events, timestamps, Durations};
|
use crate::tasks::durations::{referenced_events, timestamps, Durations};
|
||||||
pub use crate::tasks::nostr_users::NostrUsers;
|
pub use crate::tasks::nostr_users::NostrUsers;
|
||||||
|
|
||||||
use chrono::{Local, TimeDelta};
|
use chrono::{Local, TimeDelta, TimeZone};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
@ -242,6 +242,12 @@ impl TasksRelay {
|
||||||
self.get_position_at(now())
|
self.get_position_at(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse_tracking_stamp_relative(&self, input: &str) -> Option<Timestamp> {
|
||||||
|
let pos = self.get_position_timestamped();
|
||||||
|
let mut pos_time = pos.1.and_then(|_| Local.timestamp_opt(pos.0.as_u64() as i64, 0).earliest());
|
||||||
|
parse_tracking_stamp(input, pos_time.take_if(|t| Local::now() - *t > TimeDelta::hours(6)))
|
||||||
|
}
|
||||||
|
|
||||||
fn sorting_key(&self, task: &Task) -> impl Ord {
|
fn sorting_key(&self, task: &Task) -> impl Ord {
|
||||||
self.sorting
|
self.sorting
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -694,6 +700,7 @@ impl TasksRelay {
|
||||||
"progress" => prog_string.clone(),
|
"progress" => prog_string.clone(),
|
||||||
|
|
||||||
"owner" => format!("{:.6}", self.users.get_username(&task.get_owner())),
|
"owner" => format!("{:.6}", self.users.get_username(&task.get_owner())),
|
||||||
|
"assignee" => format!("{:.6}", task.get_assignee().map(|u| self.users.get_username(&u)).unwrap_or_default()),
|
||||||
"author" | "creator" => format!("{:.6}", self.users.get_username(&task.event.pubkey)), // FIXME temporary until proper column alignment
|
"author" | "creator" => format!("{:.6}", self.users.get_username(&task.event.pubkey)), // FIXME temporary until proper column alignment
|
||||||
"prio" => self
|
"prio" => self
|
||||||
.traverse_up_from(Some(task.get_id()))
|
.traverse_up_from(Some(task.get_id()))
|
||||||
|
@ -1113,11 +1120,18 @@ impl TasksRelay {
|
||||||
Some(id)
|
Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create the task, only adding context tags
|
||||||
fn make_task_unchecked(
|
fn make_task_unchecked(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &str,
|
input: &str,
|
||||||
tags: Vec<Tag>,
|
tags: Vec<Tag>,
|
||||||
) -> EventId {
|
) -> EventId {
|
||||||
|
let assignee =
|
||||||
|
if tags.iter().any(|t| t.kind() == TagKind::p()) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.pubkey.map(|p| Tag::public_key(p))
|
||||||
|
};
|
||||||
let prio =
|
let prio =
|
||||||
if tags.iter().any(|t| t.kind().to_string() == PRIO) {
|
if tags.iter().any(|t| t.kind().to_string() == PRIO) {
|
||||||
None
|
None
|
||||||
|
@ -1128,7 +1142,8 @@ impl TasksRelay {
|
||||||
EventBuilder::new(TASK_KIND, input)
|
EventBuilder::new(TASK_KIND, input)
|
||||||
.tags(self.context_hashtags())
|
.tags(self.context_hashtags())
|
||||||
.tags(tags)
|
.tags(tags)
|
||||||
.tags(prio),
|
.tags(prio)
|
||||||
|
.tags(assignee),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,29 @@ fn test_context() {
|
||||||
// s2-4 are newest while s2,s3,hp are highest prio
|
// s2-4 are newest while s2,s3,hp are highest prio
|
||||||
assert_tasks_visible!(tasks, [s4, s2, s3, anid, id_hp]);
|
assert_tasks_visible!(tasks, [s4, s2, s3, anid, id_hp]);
|
||||||
|
|
||||||
tasks.pubkey = Some(Keys::generate().public_key);
|
// ASSIGNEE
|
||||||
|
assert_eq!(tasks.pubkey, Some(tasks.sender.pubkey()));
|
||||||
|
let hoi = tasks.make_task("hoi").unwrap();
|
||||||
|
let hoi = tasks.get_by_id(&hoi).unwrap();
|
||||||
|
assert_eq!(hoi.get_owner(), tasks.sender.pubkey());
|
||||||
|
assert_eq!(hoi.get_participants().collect_vec(), vec![tasks.sender.pubkey()]);
|
||||||
|
assert_eq!(hoi.get_assignee(), Some(tasks.sender.pubkey()));
|
||||||
|
|
||||||
|
let pubkey = Keys::generate().public_key;
|
||||||
|
let test1id = tasks.make_task(&("test1 @".to_string() + &pubkey.to_string())).unwrap();
|
||||||
|
let test1 = tasks.get_by_id(&test1id).unwrap();
|
||||||
|
assert_eq!(test1.get_owner(), pubkey);
|
||||||
|
|
||||||
|
tasks.pubkey = Some(pubkey);
|
||||||
|
let test2id = tasks.make_task("test2").unwrap();
|
||||||
|
let test2 = tasks.get_by_id(&test2id).unwrap();
|
||||||
|
assert_eq!(test2.get_owner(), pubkey);
|
||||||
|
|
||||||
|
tasks.pubkey = None;
|
||||||
|
let all = tasks.make_task("all").unwrap();
|
||||||
|
let all = tasks.get_by_id(&all).unwrap();
|
||||||
|
assert_eq!(all.get_assignee(), None);
|
||||||
|
assert_eq!(all.get_owner(), tasks.sender.pubkey());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Reference in a new issue