Skip to content

Commit

Permalink
Add filter schedule and prioritize
Browse files Browse the repository at this point in the history
  • Loading branch information
alanvardy committed Oct 17, 2023
1 parent 7e30b81 commit d040ca6
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- Remove `scheduled` flag from `task list`, the `filter` flag covers this use case now. Use `today & !no time`.
- Add `filter` flag to `task edit` and `task next`
- Add `filter label`
- Add `filter schedule`
- Add `filter prioritize`

## 2023-10-09 v0.5.3

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Usage: tod [OPTIONS] [COMMAND]
Commands:
task
project
filter
version
help Print this message or the help of the given subcommand(s)

Expand Down
137 changes: 130 additions & 7 deletions src/filters.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
color,
config::Config,
input,
input::{self, DateTimeInput},
tasks::{self, FormatType, Task},
todoist,
};
Expand Down Expand Up @@ -122,6 +122,61 @@ fn handle_task(config: &Config, task: Task) -> Option<Result<String, String>> {
}
}

/// Prioritize all unprioritized tasks in a project
pub fn prioritize_tasks(config: &Config, filter: &String) -> Result<String, String> {
let tasks = todoist::tasks_for_filter(config, filter)?;

if tasks.is_empty() {
Ok(color::green_string(&format!(
"No tasks to prioritize in '{filter}'"
)))
} else {
for task in tasks.iter() {
tasks::set_priority(config, task.to_owned())?;
}
Ok(color::green_string(&format!(
"Successfully prioritized '{filter}'"
)))
}
}

/// Put dates on all tasks without dates
pub fn schedule(config: &Config, filter: &String) -> Result<String, String> {
let tasks = todoist::tasks_for_filter(config, filter)?;

if tasks.is_empty() {
Ok(color::green_string(&format!(
"No tasks to schedule in '{filter}'"
)))
} else {
for task in tasks.iter() {
println!("{}", task.fmt(config, FormatType::Single));
let datetime_input = input::datetime(
config.mock_select,
config.mock_string.clone(),
config.natural_language_only,
)?;
match datetime_input {
input::DateTimeInput::Complete => {
let config = config.set_next_id(&task.id);
todoist::complete_task(&config)?
}
DateTimeInput::Skip => "Skipped".to_string(),

input::DateTimeInput::Text(due_string) => {
todoist::update_task_due(config, task.to_owned(), due_string)?
}
input::DateTimeInput::None => {
todoist::update_task_due(config, task.to_owned(), "No Date".to_string())?
}
};
}
Ok(color::green_string(&format!(
"Successfully scheduled tasks in '{filter}'"
)))
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -138,7 +193,7 @@ mod tests {
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let config = test::fixtures::config().mock_url(server.url());
Expand Down Expand Up @@ -167,7 +222,7 @@ mod tests {
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let config = test::fixtures::config()
Expand All @@ -188,7 +243,7 @@ mod tests {
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let config = test::fixtures::config().mock_url(server.url());
Expand Down Expand Up @@ -219,14 +274,14 @@ mod tests {
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let mock2 = server
.mock("POST", "/rest/v2/tasks/999999")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let config = test::fixtures::config().mock_url(server.url());
Expand Down Expand Up @@ -261,7 +316,7 @@ mod tests {
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::rest_tasks())
.with_body(test::responses::get_tasks())
.create();

let mock2 = server
Expand All @@ -286,4 +341,72 @@ mod tests {
mock.assert();
mock2.assert();
}

#[test]
fn test_schedule() {
let mut server = mockito::Server::new();
let mock = server
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::get_unscheduled_tasks())
.create();

let mock2 = server
.mock("POST", "/rest/v2/tasks/999999")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::task())
.create();

let config = test::fixtures::config()
.mock_url(server.url())
.mock_select(1)
.mock_string("tod");

let filter = String::from("today");
let result = schedule(&config, &filter);
assert_eq!(
result,
Ok("Successfully scheduled tasks in 'today'".to_string())
);

let config = config.mock_select(2);

let filter = String::from("today");
let result = schedule(&config, &filter);
assert_eq!(
result,
Ok("Successfully scheduled tasks in 'today'".to_string())
);

mock.expect(2);
mock2.expect(2);
}
#[test]
fn test_prioritize_tasks() {
let mut server = mockito::Server::new();
let mock = server
.mock("GET", "/rest/v2/tasks/?filter=today")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::get_tasks())
.create();
let mock2 = server
.mock("POST", "/rest/v2/tasks/999999")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::get_tasks())
.create();

let config = test::fixtures::config()
.mock_url(server.url())
.mock_select(1);

let filter = String::from("today");
let result = prioritize_tasks(&config, &filter);
assert_eq!(result, Ok(String::from("Successfully prioritized 'today'")));
mock.assert();
mock2.assert();
}
}
28 changes: 28 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ fn main() {
Some(("filter", filter_matches)) => match filter_matches.subcommand() {
Some(("label", m)) => filter_label(m),
Some(("process", m)) => filter_process(m),
Some(("prioritize", m)) => filter_prioritize(m),
Some(("schedule", m)) => filter_schedule(m),
_ => unreachable!(),
},
Some(("version", version_matches)) => match version_matches.subcommand() {
Expand Down Expand Up @@ -213,6 +215,14 @@ fn cmd() -> Command {
.arg(flag_arg("verbose", 'v', "Display additional debug info while processing"))
.arg(filter_arg())
.arg(config_arg()),
Command::new("prioritize").about("Give every task a priority")
.arg(flag_arg("verbose", 'v', "Display additional debug info while processing"))
.arg(filter_arg())
.arg(config_arg()),
Command::new("schedule").about("Assign dates to all tasks individually")
.arg(flag_arg("verbose", 'v', "Display additional debug info while processing"))
.arg(filter_arg())
.arg(config_arg()),
]
),
Command::new("version")
Expand Down Expand Up @@ -436,6 +446,24 @@ fn filter_process(matches: &ArgMatches) -> Result<String, String> {
_ => unreachable!(),
}
}

#[cfg(not(tarpaulin_include))]
fn filter_prioritize(matches: &ArgMatches) -> Result<String, String> {
let config = fetch_config(matches)?;
match fetch_project(matches, &config)? {
Flag::Filter(filter) => filters::prioritize_tasks(&config, &filter),
_ => unreachable!(),
}
}

#[cfg(not(tarpaulin_include))]
fn filter_schedule(matches: &ArgMatches) -> Result<String, String> {
let config = fetch_config(matches)?;
match fetch_project(matches, &config)? {
Flag::Filter(filter) => filters::schedule(&config, &filter),
_ => unreachable!(),
}
}
// --- VERSION ---

#[cfg(not(tarpaulin_include))]
Expand Down
2 changes: 1 addition & 1 deletion src/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ mod tests {
.mock("POST", "/sync/v9/projects/get_data")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(test::responses::unscheduled_tasks())
.with_body(test::responses::post_unscheduled_tasks())
.create();

let mock2 = server
Expand Down
36 changes: 34 additions & 2 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub mod responses {
time::today_string(&fixtures::config())
)
}
pub fn rest_tasks() -> String {
pub fn get_tasks() -> String {
format!(
"
[
Expand Down Expand Up @@ -193,7 +193,7 @@ pub mod responses {
)
}

pub fn unscheduled_tasks() -> String {
pub fn post_unscheduled_tasks() -> String {
String::from(
"{\
\"items\":\
Expand Down Expand Up @@ -226,6 +226,38 @@ pub mod responses {
)
}

pub fn get_unscheduled_tasks() -> String {
String::from(
"
[
{\
\"added_by_uid\":44444444,\
\"assigned_by_uid\":null,\
\"checked\":false,\
\"child_order\":-5,\
\"collapsed\":false,\
\"content\":\"Put out recycling\",\
\"date_added\":\"2021-06-15T13:01:28Z\",\
\"date_completed\":null,\
\"description\":\"\",\
\"due\":null,\
\"id\":\"999999\",\
\"is_deleted\":false,\
\"labels\":[],\
\"note_count\":0,\
\"parent_id\":null,\
\"priority\":3,\
\"project_id\":22222222,\
\"responsible_uid\":null,\
\"section_id\":333333333,\
\"sync_id\":null,\
\"user_id\":111111111\
}
]
",
)
}

pub fn task() -> String {
String::from(
"\
Expand Down

0 comments on commit d040ca6

Please sign in to comment.