From 3e7988c5bc52f836b60de8e915774c2c3d39442e Mon Sep 17 00:00:00 2001 From: JianuoZhu Date: Wed, 21 May 2025 21:48:56 +0800 Subject: [PATCH] feat: add 'export' command to export tasks as csv/json/md --- source/todo/cli_parser.py | 18 +++++- source/todo/todo.py | 58 ++++++++++++++++++++ source/todocli.egg-info/PKG-INFO | 11 ++++ source/todocli.egg-info/SOURCES.txt | 41 ++++++++++++++ source/todocli.egg-info/dependency_links.txt | 1 + source/todocli.egg-info/entry_points.txt | 2 + source/todocli.egg-info/requires.txt | 5 ++ source/todocli.egg-info/top_level.txt | 1 + 8 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 source/todocli.egg-info/PKG-INFO create mode 100644 source/todocli.egg-info/SOURCES.txt create mode 100644 source/todocli.egg-info/dependency_links.txt create mode 100644 source/todocli.egg-info/entry_points.txt create mode 100644 source/todocli.egg-info/requires.txt create mode 100644 source/todocli.egg-info/top_level.txt diff --git a/source/todo/cli_parser.py b/source/todo/cli_parser.py index 43674ae..fed1189 100644 --- a/source/todo/cli_parser.py +++ b/source/todo/cli_parser.py @@ -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 @@ -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)) diff --git a/source/todo/todo.py b/source/todo/todo.py index 2d32473..4c573ec 100755 --- a/source/todo/todo.py +++ b/source/todo/todo.py @@ -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 @@ -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. @@ -471,6 +528,7 @@ def ping_task(args, daccess): 'search': search, 'future': list_future_tasks, 'ping': ping_task, + 'export': export_tasks, } diff --git a/source/todocli.egg-info/PKG-INFO b/source/todocli.egg-info/PKG-INFO new file mode 100644 index 0000000..7ea072f --- /dev/null +++ b/source/todocli.egg-info/PKG-INFO @@ -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 diff --git a/source/todocli.egg-info/SOURCES.txt b/source/todocli.egg-info/SOURCES.txt new file mode 100644 index 0000000..e615374 --- /dev/null +++ b/source/todocli.egg-info/SOURCES.txt @@ -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 \ No newline at end of file diff --git a/source/todocli.egg-info/dependency_links.txt b/source/todocli.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/source/todocli.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/source/todocli.egg-info/entry_points.txt b/source/todocli.egg-info/entry_points.txt new file mode 100644 index 0000000..6b77374 --- /dev/null +++ b/source/todocli.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +todo = todo.todo:main diff --git a/source/todocli.egg-info/requires.txt b/source/todocli.egg-info/requires.txt new file mode 100644 index 0000000..033a839 --- /dev/null +++ b/source/todocli.egg-info/requires.txt @@ -0,0 +1,5 @@ +setuptools==65.5.1 + +[tests] +pyfakefs==4.5.5 +freezegun==1.2.2 diff --git a/source/todocli.egg-info/top_level.txt b/source/todocli.egg-info/top_level.txt new file mode 100644 index 0000000..258cd57 --- /dev/null +++ b/source/todocli.egg-info/top_level.txt @@ -0,0 +1 @@ +todo