Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion source/todo/cli_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
COMMANDS = {
'add', 'done', 'task', 'edit', 'rm', 'ctx', 'contexts', 'history',
'purge', 'mv', 'rmctx', 'search', 'future', '-h', '--help', '--location',
'--version', '--install-autocompletion', 'undone', 'ping'}
'--version', '--install-autocompletion', 'undone', 'ping', 'export'}


## Argument parsing error messages
Expand Down Expand Up @@ -425,6 +425,22 @@ def parse_command(argv):
help="The list of tasks' IDs to ping",
)

export_parser = subparsers.add_parser(
'export',
help="Export tasks in various formats (csv, json, md)"
)
export_parser.set_defaults(command='export')
export_parser.add_argument(
'-f', '--format',
choices=['csv', 'json', 'md'],
default='csv',
help="Export format: csv (default), json, or md"
)
export_parser.add_argument(
'-o', '--output',
help="Output file path (default is stdout)"
)

return vars(parser.parse_args(argv))


Expand Down
58 changes: 58 additions & 0 deletions source/todo/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import os.path as op
from datetime import date, datetime, timezone
from typing import List
import csv
import json

from . import cli_parser, utils, data_access, core
from .bash_completion import installation as bash_completion_installation
Expand Down Expand Up @@ -451,6 +453,61 @@ def ping_task(args, daccess):
return 'multiple_tasks_update', not_found


def export_tasks(args, daccess):
"""
Export all tasks (including subcontexts) to csv/json/md.
"""
fmt = args.get('format', 'csv')
out_path = args.get('output')
# get all tasks
tasks = daccess.todo(path='', recursive=True)

# open the output
fout = open(out_path, 'w', newline='', encoding='utf-8') if out_path else sys.stdout

if fmt == 'csv':
fieldnames = ['ID','Title','Context','Deadline','Start','Period','Priority','Status']
writer = csv.DictWriter(fout, fieldnames=fieldnames)
writer.writeheader()
for t in tasks:
writer.writerow({
'ID': t['user_task_id'],
'Title': t.get('title',''),
'Context': t.get('ctx_path',''),
'Deadline':t.get('deadline') or '',
'Start': t.get('start') or '',
'Period': t.get('period') or '',
'Priority':t.get('priority') or '',
'Status': 'done' if core.current_period_is_done(t) else 'todo'
})

elif fmt == 'json':
json.dump(tasks, fout, default=str, indent=2)

elif fmt == 'md':
headers = ['ID','Title','Context','Deadline','Start','Period','Priority','Status']
fout.write('| ' + ' | '.join(headers) + ' |\n')
fout.write('| ' + ' | '.join(['---']*len(headers)) + ' |\n')
for t in tasks:
row = [
str(t['user_task_id']),
t.get('title',''),
t.get('ctx_path',''),
str(t.get('deadline') or ''),
str(t.get('start') or ''),
str(t.get('period') or ''),
str(t.get('priority') or ''),
'done' if core.current_period_is_done(t) else 'todo'
]
fout.write('| ' + ' | '.join(row) + ' |\n')

else:
print(f"Unsupported format: {fmt}", file=sys.stderr)

if out_path:
fout.close()


## DISPATCHING

# Map of the names of the commands to handlers defined above.
Expand All @@ -471,6 +528,7 @@ def ping_task(args, daccess):
'search': search,
'future': list_future_tasks,
'ping': ping_task,
'export': export_tasks,
}


Expand Down
11 changes: 11 additions & 0 deletions source/todocli.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Metadata-Version: 2.1
Name: todocli
Version: 5.0.0
Summary: A command line todo list manager
Home-page: https://github.com/foobuzz/todo
Author: foobuzz
Author-email: foobuzz@fastmail.com
License: MIT
Keywords: command line todo list
Requires-Python: >=3.8
Provides-Extra: tests
41 changes: 41 additions & 0 deletions source/todocli.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
MANIFEST.in
setup.py
test.py
todo.py
tests/.toduhrc
tests/__init__.py
tests/test.py
tests/test_cli_parser.py
tests/test_get_neighbourhood_occurrences.py
tests/test_rainbow.py
tests/test_text_wrap.py
tests/test_todo.py
tests/test_utils.py
tests/utils.py
tests/test_bash_completion/__init__.py
tests/test_bash_completion/test_installation.py
tests/traces/contexts
tests/traces/dependencies
tests/traces/future
tests/traces/guide
tests/traces/recurrence
tests/traces/search
todo/__init__.py
todo/cli_parser.py
todo/core.py
todo/data_access.py
todo/init_db.py
todo/rainbow.py
todo/text_wrap.py
todo/todo.py
todo/types.py
todo/utils.py
todo/bash_completion/__init__.py
todo/bash_completion/installation.py
todo/bash_completion/toduh.sh
todocli.egg-info/PKG-INFO
todocli.egg-info/SOURCES.txt
todocli.egg-info/dependency_links.txt
todocli.egg-info/entry_points.txt
todocli.egg-info/requires.txt
todocli.egg-info/top_level.txt
1 change: 1 addition & 0 deletions source/todocli.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions source/todocli.egg-info/entry_points.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[console_scripts]
todo = todo.todo:main
5 changes: 5 additions & 0 deletions source/todocli.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
setuptools==65.5.1

[tests]
pyfakefs==4.5.5
freezegun==1.2.2
1 change: 1 addition & 0 deletions source/todocli.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
todo