Skip to content

Instantly share code, notes, and snippets.

@simonbru
Last active February 12, 2020 20:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonbru/c06cd9e6e2286a9b6df3c389d62cce22 to your computer and use it in GitHub Desktop.
Save simonbru/c06cd9e6e2286a9b6df3c389d62cce22 to your computer and use it in GitHub Desktop.
Sort todo.txt file according to multiple critera.
#!/usr/bin/env python3
"""
Sort todo.txt file according to multiple critera.
Depends on todotxt-machine
"""
import argparse
import datetime
import sys
from dataclasses import dataclass
from pathlib import Path
from todotxt_machine.todo import Todo, Todos
@dataclass
class Criterion:
value: str
after: bool = False
def key(self, todo):
if self.value.startswith("+"):
does_match = self.value in todo.projects
elif self.value.startswith("@"):
does_match = self.value in todo.contexts
return int(does_match if self.after else not does_match)
@dataclass
class Priority:
ascending: bool = False
def key(self, todo):
value = ord(todo.priority if todo.priority else "Z")
return value if self.ascending else -value
@dataclass
class Done:
after: bool = False
def key(self, todo):
value = todo.is_complete()
if not self.after:
value = not value
return int(value)
@dataclass
class CompletionDate:
ascending: bool = False
null_after: bool = False
def key(self, todo):
try:
value = datetime.date.fromisoformat(todo.completed_date).toordinal()
except (TypeError, ValueError):
value = math.inf if self.null_after else 0
return value if self.ascending else -value
ORDERING = (
Done(after=False),
CompletionDate(ascending=True, null_after=False),
Criterion("+y"),
Criterion("+m"),
Criterion("+w"),
Criterion("+wait"),
Priority(),
)
def sort_key(todo):
return tuple(predicate.key(todo) for predicate in ORDERING)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("todo_file")
parser.add_argument("-s", "--save", action="store_true")
parser.add_argument("-q", "--quiet", action="store_true")
args = parser.parse_args()
todos_path = Path(args.todo_file)
todos = Todos(todos_path.read_text().split("\n"), str(todos_path), None)
todos.todo_items.sort(key=sort_key)
if args.save:
todos.save()
if not args.quiet:
todos_raw = "\n".join(todo.raw for todo in todos)
print(todos_raw)
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment