| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816 |
- #!/usr/bin/env python3
- import asyncio, os, sys, tempfile
- from datetime import datetime
- import time
- from tabulate import tabulate
- from colorama import Fore, Style
- import api, lib.util
- known_attrs = ["desc", "rank", "due", "project"]
- async def add_task(task_args, server_name, port):
- task = {
- "title": None,
- "tags": [],
- "desc": None,
- "assign": [],
- "project": [],
- "due": None,
- "rank": None,
- "created_at": lib.util.now(),
- "state": "open"
- }
- # Everything that isn't an attribute is part of the title
- # Open text editor if desc isn't set to write desc text
- title_words = []
- for arg in task_args:
- if arg[0] == "+":
- tag = arg
- if tag in task["tags"]:
- print(f"error: duplicate tag {tag} in task", file=sys.stderr)
- sys.exit(-1)
- task["tags"].append(tag)
- elif arg[0] == "@":
- assign = arg
- if assign in task["assign"]:
- print(f"error: duplicate assign {assign} in task", file=sys.stderr)
- sys.exit(-1)
- task["assign"].append(assign)
- elif ":" in arg and arg.split(":")[0] in known_attrs:
- attr, val = arg.split(":", 1)
- set_task_attr(task, attr, val)
- else:
- title_words.append(arg)
- title = " ".join(title_words)
- if len(title) == 0:
- print("Error: Title is required")
- exit(-1)
- task["title"] = title
- if task["desc"] is None:
- task["desc"] = prompt_description_text(task)
-
- if task["desc"].strip() == '':
- print("Abort adding the task due to empty description.")
- exit(-1)
-
- if task["rank"] is not None:
- task["rank"] = round(task["rank"], 4)
-
- try:
- if task["ref_id"].strip() == '':
- task.pop('ref_id')
- if task["workspace"].strip() == '':
- task.pop('workspace')
- except KeyError:
- pass
- ref = await api.add_task(task, server_name, port)
- if ref:
- return ref, title
- else:
- print("You don't have write access")
- exit(-1)
- def prompt_text(comment_lines):
- temp = tempfile.NamedTemporaryFile()
- temp.write(b"\n")
- for line in comment_lines:
- temp.write(line.encode() + b"\n")
- temp.flush()
- editor = os.environ.get('EDITOR') if os.environ.get('EDITOR') else 'nano'
- os.system(f"{editor} {temp.name}")
- desc = open(temp.name, "r").read()
- # Remove comments and empty lines from desc
- cleaned = []
- for line in desc.split("\n"):
- if line == "# ------------------------ >8 ------------------------":
- break
- if line.startswith("#"):
- continue
- cleaned.append(line)
- return "\n".join(cleaned)
- def prompt_description_text(task):
- return prompt_text([
- "# Write task description above this line.",
- "# These lines will be removed.",
- "# An empty description aborts adding the task",
- "\n# ------------------------ >8 ------------------------",
- "# Do not modify or remove the line above.",
- "# Everything below it will be ignored.",
- f"\n{tabulate_task(task, True)}"
- ])
- def prompt_comment_text():
- return prompt_text([
- "# Write comments above this line",
- "# These lines will be removed"
- ])
- def prompt_description_edit(text):
- return prompt_text([
- f"{text}"
- "# Edit the task description above this line",
- "# These lines will be removed"
- ])
- def set_task_attr(task, attr, val):
- if attr not in known_attrs:
- print(f"Error: invalid attribute: {attr} {val}")
- print("Task is not added")
- exit(-1)
- if val.lower() == "none":
- task[attr] = None
- else:
- val = convert_attr_val(attr, val)
- task[attr] = val
- lib.util._enforce_task_format(task)
- def convert_attr_val(attr, val):
- templ = lib.util.task_template
- if attr in ["desc", "title"]:
- assert templ[attr] == str
- return val
- elif attr == "rank":
- try:
- return float(val)
- except ValueError:
- print(f"error: rank value {val} isn't convertable to float",
- file=sys.stderr)
- sys.exit(-1)
- elif attr == "due":
- # Other date formats not yet supported... ez to add
- if len(val) != 4:
- print(f"Error: due date must be of length 4 in mmyy format")
- sys.exit(-1)
- date = datetime.now().date()
- year = int(date.strftime("%Y"))%100
- try:
- dt = datetime.strptime(f"18:00 {val}{year}", "%H:%M %d%m%y")
- if dt.date() < date:
- dt = datetime.strptime(f"18:00 {val}{year+1}", "%H:%M %d%m%y")
- except ValueError:
- print(f"error: unknown date format {val}")
- sys.exit(-1)
- due = lib.util.datetime_to_unix(dt)
- return due
- elif attr == "project":
- try:
- return [val]
- except ValueError:
- print(f"error: project value {val} isn't convertable to list",
- file=sys.stderr)
- sys.exit(-1)
- else:
- print(f"error: unhandled attr '{attr}' = {val}")
- sys.exit(-1)
- async def show_active_tasks(workspace, server_name, port):
- refids = await api.get_ref_ids(server_name, port)
- tasks = []
- for refid in refids:
- tasks.append(await api.fetch_task(refid, server_name, port))
- list_tasks(tasks, workspace, [])
- async def show_deactive_tasks(month_ts, workspace, server_name, port):
- tasks = await api.fetch_deactive_tasks(month_ts, server_name, port)
- list_tasks(tasks, workspace, [])
- async def show_log(server_name, port, timeframe):
- # fetch all tasks
- refids = await api.get_ref_ids(server_name, port)
- tasks = await api.fetch_deactive_tasks(None, server_name, port)
- for refid in refids:
- tasks.append(await api.fetch_task(refid, server_name, port))
- # list tasks events within a timeframe
- if timeframe == "day":
- timestamp = 86400
- elif timeframe == "week" or timeframe == None:
- timestamp = 604800
- elif timeframe == "month":
- timestamp = 2628002
- else:
- print(f"Error: invalid timeframe {timeframe}")
- print("valid timeframes are: 'day', 'week' and 'month'")
- print("try: tau log week")
- exit(-1)
- res_events = []
- now = lib.util.now()
- for task in tasks:
- events = task["events"]
- for event in events:
- # if timestamp is in ms convert it to s
- event_ts = int(event["timestamp"])
- if event_ts > 10e10:
- event_ts //= 1000
- date = lib.util.unix_to_datetime(event_ts)
- if (now - event_ts) < timestamp:
- if event["action"] == "state":
- action = "stopped" if event["content"] == "stop" else f"{event["content"]}ed"
- res = f"{date}: {event["author"]} {action} task: ({task["ref_id"][:6]}) '{task["title"]}'"
- res_events.append(res)
- if event["action"] == "comment":
- res = f"{date}: {event["author"]} commented on task: ({task["ref_id"][:6]}) '{task["title"]}'"
- res_events.append(res)
- if event["action"] in ["assign", "tags"] :
- res = f"{date}: {event["author"]} added {event["content"][1:]} to {event["action"]} in task: ({task["ref_id"][:6]}) '{task["title"]}'"
- res_events.append(res)
- for i in res_events:
- print(i)
- def list_tasks(tasks, workspace, filters):
- print(f"Workspace: {workspace}")
- headers = ["ID", "Title", "Status", "Project",
- "Tags", "assign", "Rank", "Due", "RefID"]
- table_rows = []
- for id, task in enumerate(tasks, 1):
- if task is None:
- continue
- if is_filtered(task, filters):
- continue
- ref_id = task["ref_id"][:6]
- title = task["title"]
- status = task["state"]
- # project = task["project"] if task["project"] is not None else ""
- tags = " ".join(f"+{tag}" for tag in task["tags"])
- assign = " ".join(f"@{assign}" for assign in task["assign"])
- project = " ".join(f"{project}" for project in task["project"])
- if task["due"] is None:
- due = ""
- else:
- dt = lib.util.unix_to_datetime(task["due"])
- due = dt.strftime("%H:%M %d/%m/%y")
- rank = round(task["rank"], 4) if task["rank"] is not None else ""
- if status == "start":
- id = Fore.GREEN + str(id) + Style.RESET_ALL
- title = Fore.GREEN + str(title) + Style.RESET_ALL
- status = Fore.GREEN + str(status) + Style.RESET_ALL
- project = Fore.GREEN + str(project) + Style.RESET_ALL
- tags = Fore.GREEN + str(tags) + Style.RESET_ALL
- assign = Fore.GREEN + str(assign) + Style.RESET_ALL
- rank = Fore.GREEN + str(rank) + Style.RESET_ALL
- due = Fore.GREEN + str(due) + Style.RESET_ALL
- ref_id = Fore.GREEN + str(ref_id) + Style.RESET_ALL
- elif status == "pause":
- id = Fore.YELLOW + str(id) + Style.RESET_ALL
- title = Fore.YELLOW + str(title) + Style.RESET_ALL
- status = Fore.YELLOW + str(status) + Style.RESET_ALL
- project = Fore.YELLOW + str(project) + Style.RESET_ALL
- tags = Fore.YELLOW + str(tags) + Style.RESET_ALL
- assign = Fore.YELLOW + str(assign) + Style.RESET_ALL
- rank = Fore.YELLOW + str(rank) + Style.RESET_ALL
- due = Fore.YELLOW + str(due) + Style.RESET_ALL
- ref_id = Fore.YELLOW + str(ref_id) + Style.RESET_ALL
- elif status == "stop":
- id = Fore.RED + str(id) + Style.RESET_ALL
- title = Fore.RED + str(title) + Style.RESET_ALL
- status = Fore.RED + str(status) + Style.RESET_ALL
- project = Fore.RED + str(project) + Style.RESET_ALL
- tags = Fore.RED + str(tags) + Style.RESET_ALL
- assign = Fore.RED + str(assign) + Style.RESET_ALL
- rank = Fore.RED + str(rank) + Style.RESET_ALL
- due = Fore.RED + str(due) + Style.RESET_ALL
- ref_id = Fore.RED + str(ref_id) + Style.RESET_ALL
- else:
- #id = Style.DIM + str(id) + Style.RESET_ALL
- #title = Style.DIM + str(title) + Style.RESET_ALL
- #status = Style.DIM + str(status) + Style.RESET_ALL
- project = Style.DIM + str(project) + Style.RESET_ALL
- tags = Style.DIM + str(tags) + Style.RESET_ALL
- #assign = Style.DIM + str(assign) + Style.RESET_ALL
- rank = Style.DIM + str(rank) + Style.RESET_ALL
- due = Style.DIM + str(due) + Style.RESET_ALL
- #ref_id = Style.DIM + str(ref_id) + Style.RESET_ALL
- rank_value = task["rank"] if task["rank"] is not None else 0
- row = [
- id,
- title,
- status,
- project,
- tags,
- assign,
- rank,
- due,
- ref_id
- ]
- table_rows.append((rank_value, row))
- table = [row for (_, row) in
- sorted(table_rows, key=lambda item: item[0], reverse=True)]
- print(tabulate(table, headers=headers))
- async def show_task(refid, server_name, port):
- task = await api.fetch_task(refid, server_name, port)
- task_table(task)
- return 0
- async def show_archive_task(ref_id, month_ts, server_name, port):
- task = await api.fetch_archive_task(ref_id, month_ts, server_name, port)
- task_table(task)
- return 0
- def tabulate_task(task, prompt):
- tags = " ".join(f"+{tag}" for tag in task["tags"])
- assign = " ".join(f"{assign}" for assign in task["assign"])
- project = " ".join(f"{project}" for project in task["project"])
- rank = round(task["rank"], 4) if task["rank"] is not None else ""
- if task["due"] is None:
- due = ""
- else:
- dt = lib.util.unix_to_datetime(task["due"])
- due = dt.strftime("%H:%M %d/%m/%y")
- assert task["created_at"] is not None
- dt = lib.util.unix_to_datetime(task["created_at"])
- created_at = dt.strftime("%H:%M %d/%m/%y")
- if prompt:
- task["ref_id"] = ''
- task["workspace"] = ''
- table = [
- ["RefID:", task["ref_id"]],
- ["Title:", task["title"]],
- ["Workspace:", task["workspace"]],
- ["Description:", task["desc"]],
- ["Status:", task["state"]],
- ["Project:", project],
- ["Tags:", tags],
- ["Assign:", assign],
- ["Rank:", rank],
- ["Due:", due],
- ["Created:", created_at],
- ]
- return tabulate(table, headers=["Attribute", "Value"])
- def task_table(task):
- print(tabulate_task(task, False))
- table = []
- for event in task["events"]:
- act, who, when, args = event["action"], event["author"], event["timestamp"], event["content"]
- when = lib.util.unix_to_datetime(when)
- when = when.strftime("%H:%M %d/%m/%y")
-
- if act == "due" and when is not None:
- due_date = lib.util.unix_to_datetime(args)
- due_date = due_date.strftime("%H:%M %d/%m/%y")
- table.append([
- Style.DIM + f"{who} changed {act} to {due_date}" + Style.RESET_ALL,
- "",
- Style.DIM + when + Style.RESET_ALL
- ])
- elif act == "tags" or act == "assign":
- val = f"{args}"
- event = f"{who} added {val} to {act}"
- if val[0] == "-":
- event = f"{who} removed {val[1:]} from {act}"
- table.append([
- Style.DIM + event + Style.RESET_ALL,
- "",
- Style.DIM + when + Style.RESET_ALL
- ])
- elif act == "state":
- status = args
- if status == "pause":
- status_verb = "paused"
- elif status in ["start", "open"]:
- status_verb = f"{status}ed"
- elif status == "stop":
- status_verb = f"stopped"
- else:
- print(f"internal error: unhandled task state {status}",
- file=sys.stderr)
- sys.exit(-2)
- table.append([
- f"{who} {status_verb} task",
- "",
- Style.DIM + when + Style.RESET_ALL
- ])
- elif act == "comment":
- continue
- else:
- table.append([
- Style.DIM + f"{who} changed {act} to {args}" + Style.RESET_ALL,
- "",
- Style.DIM + when + Style.RESET_ALL
- ])
- print(tabulate(table))
- table = []
- for event in task['events']:
- act, who, when, args = event["action"], event["author"], event["timestamp"], event["content"]
- when = lib.util.unix_to_datetime(when)
- when = when.strftime("%H:%M %d/%m/%y")
- if act == "comment":
- comment = args
- table.append([
- f"{who}>",
- wrap_comment(comment, 58),
- Style.DIM + when + Style.RESET_ALL
- ])
- if len(table) > 0:
- print("Comments:")
- print(tabulate(table))
- def wrap_comment(comment, width):
- lines = []
- line_start = 0
- for i, char in enumerate(comment):
- if char == ' ' and (i - line_start >= width):
- lines.append(comment[line_start:i + 1])
- line_start = i + 1
- if line_start < len(comment):
- lines.append(comment[line_start:])
- return '\n'.join(lines)
- async def modify_task(refid, args, server_name, port):
- changes = {}
- changes["assign"] = []
- changes["tags"] = []
- for arg in args:
- # This must go before the next elif block
- if arg.startswith("@") or arg.startswith("-@"):
- changes["assign"].append(arg)
- elif arg.startswith("+") or arg.startswith("-"):
- changes["tags"].append(arg)
- elif arg.lower() in ["desc", "description"]:
- task = await api.fetch_task(refid, server_name, port)
- desc = task["desc"]
- new_desc = prompt_description_edit(desc).lstrip()
- if desc == new_desc:
- print("Abort due to unchanged description")
- exit(-1)
- changes["desc"] = new_desc
- elif ":" in arg:
- attr, val = arg.split(":", 1)
- if val.lower() == "none":
- if attr not in ["project", "rank", "due"]:
- print(f"error: invalid you cannot set {attr} to none",
- file=sys.stderr)
- return -1
- val = None
- else:
- val = convert_attr_val(attr, val)
- changes[str(attr)] = val
- else:
- print(f"warning: unknown arg '{arg}'. Skipping...", file=sys.stderr)
- if not await api.modify_task(refid, changes, server_name, port):
- print("You don't have write access")
- exit(-1)
- return 0
- async def change_task_status(refid, status, server_name, port):
- task = await api.fetch_task(refid, server_name, port)
- assert task is not None
- title = task["title"]
- if not await api.change_task_status(refid, status, server_name, port):
- return -1
- if status == "start":
- print(f"Started task '{title}'")
- elif status == "pause":
- print(f"Paused task '{title}'")
- elif status == "stop":
- print(f"Completed task '{title}'")
- elif status == "open":
- print(f"Opened task '{title}'")
- return 0
- async def comment(refid, args, server_name, port):
- if not args:
- comment = prompt_comment_text()
- else:
- comment = " ".join(args)
- if comment.strip() == '':
- print("Abort adding comment due to empty content.")
- exit(-1)
- if not await api.add_task_comment(refid, comment, server_name, port):
- print("You don't have write access")
- exit(-1)
- # Two json rpcs back to back cause Unexpected EOF error
- time.sleep(0.1)
- task = await api.fetch_task(refid, server_name, port)
- assert task is not None
- title = task["title"]
- print(f"Commented on task '{title}'")
- return 0
- def is_filtered(task, filters):
- for fltr in filters:
- if fltr.startswith("+"):
- tag = fltr[1:]
- if tag not in task["tags"]:
- return True
- elif fltr.startswith("@"):
- assign = fltr[1:]
- if assign not in task["assign"]:
- return True
- elif ":" in fltr:
- attr, val = fltr.split(":", 1)
- if val.lower() == "none":
- if attr not in ["project", "rank", "due"]:
- print(f"error: invalid you cannot set {attr} to none",
- file=sys.stderr)
- sys.exit(-1)
- if task[attr] is not None:
- return True
- elif attr == "state" :
- if val not in ["open", "start", "pause"]:
- print(f"error: invalid, filter by {attr} can only be [\"open\", \"start\", \"pause\"]",
- file=sys.stderr)
- sys.exit(-1)
- if task["state"] != val:
- return True
- else:
- val = convert_attr_val(attr, val)
- if task[attr] != val:
- return True
- else:
- print(f"error: unknown arg '{fltr}'", file=sys.stderr)
- sys.exit(-1)
- return False
- def find_free_id(task_ids):
- for i in range(1, 1000):
- if i not in task_ids:
- return i
- 1
- def map_ids(task_ids, ref_ids):
- return dict(zip(task_ids, ref_ids))
- async def main():
- val = str('127.0.0.1:23330')
- allowed_states = ["start", "pause", "stop", "open"]
- for i in range(1, len(sys.argv)):
- if sys.argv[i] == "-e":
- val = sys.argv[i+1]
- del sys.argv[i]
- del sys.argv[i]
- break
-
- server_name, port = val.split(':')
-
- refids = await api.get_ref_ids(server_name, port)
- free_ids = []
- tasks = []
- for refid in refids:
- tasks.append(await api.fetch_task(refid, server_name, port))
- free_ids.append(find_free_id(free_ids))
- data = map_ids(free_ids, refids)
- workspace = await api.get_workspace(server_name, port)
- if len(sys.argv) == 1:
- await show_active_tasks(workspace, server_name, port)
- return 0
- if any(x in ["-h", "--help", "help"] for x in sys.argv):
- print('''USAGE:
- tau [OPTIONS] [SUBCOMMAND]
- OPTIONS:
- -h, --help Print help information
- -e RPC endpoint [default: 127.0.0.1:23330]
- SUBCOMMANDS:
- add Add a new task.
- archive Show completed tasks.
- comment Write comment for task by id.
- modify Modify an existing task by id.
- pause Pause task(s).
- start Start task(s).
- stop Stop task(s).
- switch Switch between configured workspaces.
- show List filtered tasks.
- export Save current workspace tasks to a path.
- import Load current workspace tasks from a path.
- help Show this help text.
- Examples:
- tau add task one due:0312 rank:1.022 project:zk +lol @sk desc:desc +abc +def
- tau add task two rank:1.044 project:cr +mol @up desc:desc2
- tau add task three due:0512 project:zy +trol @kk desc:desc3 +who
- tau 1 modify @upgr due:1112 rank:none
- tau 1 modify -@up
- tau 1 modify -mol -xx
- tau 1,2 modify +dev @erto
- tau 1-3 start
- tau 1 comment "this is an awesome comment"
- tau 2 pause
- tau show @erto state:start # list started tasks that are assigned to 'erto'
- tau show +dev project:zk # list tasks with 'dev' tag project 'zk'
- tau switch darkfi # switch to configured 'darkfi' workspace
- tau archive # current month's completed tasks
- tau archive 1122 # completed tasks of Nov. 2022
- tau archive 1122 1 # show info of task completed in Nov. 2022
- ''')
- return 0
- elif sys.argv[1] == "log":
- if len(sys.argv) == 3:
- timeframe = sys.argv[2]
- else:
- timeframe = None
- await show_log(server_name, port, timeframe)
- return 0
- elif sys.argv[1] == "add":
- task_args = sys.argv[2:]
- ref, title = await add_task(task_args, server_name, port)
- if title:
- print(f"Created task ({find_free_id(free_ids)}) ({ref[:7]}) '{title}'.")
- return 0
- elif sys.argv[1] == "archive":
- if len(sys.argv) == 4:
- if len(sys.argv[2]) == 4:
- month = sys.argv[2]
- month_ts = lib.util.month_to_unix(month)
- else:
- print("error: usage format is: tau archive [MONTH] [ID]")
- return -1
-
- archive_refids = await api.get_archive_ref_ids(month_ts, server_name, port)
- afree_ids = []
- atasks = []
- for arefid in archive_refids:
- atasks.append(await api.fetch_archive_task(arefid, month_ts, server_name, port))
- afree_ids.append(find_free_id(afree_ids))
- adata = map_ids(afree_ids, archive_refids)
- if len(sys.argv[3]) < 4:
- try:
- tid = int(sys.argv[3])
- arefid = adata[tid]
- except (ValueError, KeyError):
- print("error: invalid ID", file=sys.stderr)
- return -1
- else:
- print("error: invalid ID", file=sys.stderr)
- return -1
-
-
- if (errc := await show_archive_task(arefid, month_ts, server_name, port)) < 0:
- return errc
- elif len(sys.argv) == 3:
- if sys.argv[2] == "all":
- await show_deactive_tasks(None, workspace, server_name, port)
- elif len(sys.argv[2]) == 4:
- month = sys.argv[2]
- month_ts = lib.util.month_to_unix(month)
- await show_deactive_tasks(month_ts, workspace, server_name, port)
- elif len(sys.argv[2]) < 4:
- month_ts = lib.util.month_to_unix()
- archive_refids = await api.get_archive_ref_ids(month_ts, server_name, port)
- afree_ids = []
- atasks = []
- for arefid in archive_refids:
- atasks.append(await api.fetch_archive_task(arefid, month_ts, server_name, port))
- afree_ids.append(find_free_id(afree_ids))
- adata = map_ids(afree_ids, archive_refids)
- try:
- tid = int(sys.argv[2])
- arefid = adata[tid]
- except (ValueError, KeyError):
- print("error: invalid ID", file=sys.stderr)
- return -1
-
- if (errc := await show_archive_task(arefid, month_ts, server_name, port)) < 0:
- return errc
- else:
- print("error: month must be of format MMYY")
- return -1
- else:
- month_ts = lib.util.month_to_unix()
- await show_deactive_tasks(month_ts, workspace, server_name, port)
-
- return 0
- elif sys.argv[1] == "show":
- if len(sys.argv) > 2:
- filters = sys.argv[2:]
- list_tasks(tasks, workspace, filters)
- else:
- await show_active_tasks(workspace, server_name, port)
- return 0
- elif sys.argv[1] == "switch":
- if not len(sys.argv) == 3:
- print("Error: you must provide workspace name")
- return 0
- if not await api.switch_workspace(sys.argv[2], server_name, port):
- print(f"Error: Workspace \"{sys.argv[2]}\" is not configured.")
- else:
- print(f"You are now on \"{sys.argv[2]}\" workspace.")
- return 0
- elif sys.argv[1] == "export":
- if len(sys.argv) == 2:
- path = "~/.local/share/darkfi"
- else:
- path = sys.argv[2]
- if await api.export_to(path, server_name, port):
- print(f"Exported tasks successfuly to {path}")
- return 0
- elif sys.argv[1] == "import":
- if len(sys.argv) == 2:
- path = "~/.local/share/darkfi"
- else:
- path = sys.argv[2]
- if await api.import_from(path, server_name, port):
- print(f"Imported tasks successfuly from {path}")
- return 0
- try:
- id = sys.argv[1]
- subcommands = ["modify", "comment"]
- if any(id in ls for ls in [allowed_states, subcommands]):
- user_input = input("This command has no filter, and will modify all tasks. Are you sure? [y/N] ")
- if user_input.lower() in ['y', 'yes']:
- refid = list(refids)
- args = sys.argv[1:]
- else:
- print("Command prevented from running.")
- exit(-1)
- elif any(id == rfid[:len(id)] for rfid in refids if len(id) > 2):
- refid = []
- for rid in refids:
- if id == rid[:len(id)]:
- refid.append(rid)
- args = sys.argv[2:]
- else:
- lines = id.split(',')
- numbers = []
- for line in lines:
- if line == '':
- continue
- elif '-' in line:
- t = line.split('-')
- numbers += range(int(t[0]), int(t[1]) + 1)
- else:
- numbers.append(int(line))
- refid = []
- for i in numbers:
- refid.append(data[i])
- args = sys.argv[2:]
- except (ValueError, KeyError):
- print("error: invalid ID", file=sys.stderr)
- return -1
- except EOFError:
- print('\nOperation is cancelled')
- return -1
- if not args:
- for rid in refid:
- await show_task(rid, server_name, port)
- return 0
-
- subcmd, args = args[0], args[1:]
- if subcmd == "modify":
- if not args:
- print("Error: modify subcommand must have at least one argument.")
- exit(-1)
- for rid in refid:
- if (errc := await modify_task(rid, args, server_name, port)) < 0:
- return errc
- time.sleep(0.1)
- await show_task(rid, server_name, port)
- elif subcmd in allowed_states:
- status = subcmd
- for rid in refid:
- if (errc := await change_task_status(rid, status, server_name, port)) < 0:
- return errc
- time.sleep(0.1)
- elif subcmd == "comment":
- for rid in refid:
- if (errc := await comment(rid, args, server_name, port)) < 0:
- return errc
- else:
- print(f"error: unknown subcommand '{subcmd}'")
- return -1
- return 0
- asyncio.run(main())
|