refactor: move options as tw's config file format

This commit is contained in:
Johann Dreo 2023-08-19 21:06:03 +02:00
commit 9f252e151f
5 changed files with 108 additions and 78 deletions

View file

@ -47,3 +47,8 @@
{"description":"sorting: allow sorting within a stacker.","entry":"20230816T115710Z","modified":"20230816T193453Z","start":"20230816T193453Z","status":"pending","uuid":"30ec6b32-a32e-4595-a24d-02ac9002fd72","tags":["feat"]}
{"description":"sorting: allow sorting within a stacker.","end":"20230816T193508Z","entry":"20230816T115710Z","modified":"20230816T193508Z","status":"completed","uuid":"30ec6b32-a32e-4595-a24d-02ac9002fd72","tags":["feat"]}
{"description":"sort by urgency","end":"20230816T193637Z","entry":"20230811T170942Z","modified":"20230816T193637Z","priority":"H","status":"completed","uuid":"4f041f52-739a-4aca-94e6-164f43e61866","tags":["feat"]}
{"description":"semantic colors: allow coloring by values, not just fields.","entry":"20230817T065528Z","modified":"20230817T065528Z","status":"pending","uuid":"1a5aab4a-0c2b-444f-9259-6eddc29b9791","tags":["feat","themes"]}
{"description":"config: handle config files from taskwarrior first","entry":"20230811T171231Z","modified":"20230817T070119Z","status":"pending","uuid":"4d822c4a-d51e-4992-8a22-6e0167ac197a","tags":["feat"]}
{"description":"config: handle config files from taskwarrior first","entry":"20230811T171231Z","modified":"20230817T154131Z","start":"20230817T154131Z","status":"pending","uuid":"4d822c4a-d51e-4992-8a22-6e0167ac197a","tags":["feat"]}
{"description":"theme config: use taskwarrior theme config and extends from there.","entry":"20230819T190428Z","modified":"20230819T190428Z","status":"pending","uuid":"6b248843-1b11-487b-85e8-a96e668f6772","tags":["feat","themes"]}
{"description":"config: handle config files from taskwarrior first","end":"20230819T190559Z","entry":"20230811T171231Z","modified":"20230819T190559Z","status":"completed","uuid":"4d822c4a-d51e-4992-8a22-6e0167ac197a","tags":["feat"]}

View file

@ -1,3 +1,4 @@
[description:"config: handle config files from taskwarrior first" end:"1692471959" entry:"1691773951" modified:"1692471959" status:"completed" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
[description:"sort by urgency" end:"1692214597" entry:"1691773782" modified:"1692214597" priority:"H" status:"completed" tags:"feat" tags_feat:"x" uuid:"4f041f52-739a-4aca-94e6-164f43e61866"]
[description:"sorting: allow sorting within a stacker." end:"1692214508" entry:"1692187030" modified:"1692214508" status:"completed" tags:"feat" tags_feat:"x" uuid:"30ec6b32-a32e-4595-a24d-02ac9002fd72"]
[description:"subgrouping: allow to add another sectionning, e.g. based on priority." end:"1692187048" entry:"1692122668" modified:"1692187048" status:"completed" tags:"feat" tags_feat:"x" uuid:"9dc6d43a-b316-4417-ad2a-e47b8a5ddc7a"]

View file

@ -1,9 +1,10 @@
[description:"find data updir: try to find .task on upper directories if not in the existing one" entry:"1691773581" modified:"1692121517" priority:"H" status:"pending" tags:"data,feat" tags_data:"x" tags_feat:"x" uuid:"b4ee0b55-0bb3-4a2f-b7c5-87cd70dd0ff0"]
[description:"semantic tags : allow tags starting with a hash to refer to a github issue or PR" entry:"1691773642" modified:"1692121563" priority:"L" status:"pending" tags:"feat" tags_feat:"x" uuid:"06cf70a9-4f0e-4b32-b7bb-d1799803665c"]
[description:"hide old done tasks: Hide tasks with last status that haven t been touched since a long time." entry:"1691773751" modified:"1692122496" priority:"M" status:"pending" tags:"feat" tags_feat:"x" uuid:"e18707b4-304d-41f3-8504-89476cd796f5"]
[description:"config file.s" entry:"1691773951" modified:"1691773951" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
[description:"fix escaped add: when passing add with escapable characters, bug ensues." entry:"1691774077" modified:"1691774077" status:"pending" tags:"bug" tags_bug:"x" uuid:"79e1ba11-15ae-489f-9868-ab6adea40a91"]
[description:"more themes" entry:"1691864624" modified:"1691990020" start:"1691990020" status:"pending" tags:"themes" tags_themes:"x" uuid:"9ee183d4-3413-4efa-ba68-10d709669c05"]
[description:"init command: add a command to initialize a local .task dir and\/or config file.s" entry:"1692121630" modified:"1692121630" priority:"M" status:"pending" tags:"feat" tags_feat:"x" uuid:"81fc6480-e8e7-4dd4-9f01-a00f34463135"]
[description:"colored panels: allow selecting a different swatch for each &open;sub&close;panel" entry:"1692122434" modified:"1692122452" priority:"M" status:"pending" tags:"feat,themes" tags_feat:"x" tags_themes:"x" uuid:"ca17838d-958f-498b-bff5-a24576820ae7"]
[description:"handle prompts: for some commands like delete, taskwarrior setup an interactive prompt, which needs to be handled." entry:"1692122614" modified:"1692122614" status:"pending" tags:"bug" tags_bug:"x" uuid:"e2480c4b-4c73-4d03-8568-82f14ade7b38"]
[description:"semantic colors: allow coloring by values, not just fields." entry:"1692255328" modified:"1692255328" status:"pending" tags:"feat,themes" tags_feat:"x" tags_themes:"x" uuid:"1a5aab4a-0c2b-444f-9259-6eddc29b9791"]
[description:"theme config: use taskwarrior theme config and extends from there." entry:"1692471868" modified:"1692471868" status:"pending" tags:"feat,themes" tags_feat:"x" tags_themes:"x" uuid:"6b248843-1b11-487b-85e8-a96e668f6772"]

View file

@ -172,3 +172,21 @@ time 1692214597
old [description:"sort by urgency" entry:"1691773782" modified:"1692122485" priority:"H" status:"pending" tags:"feat" tags_feat:"x" uuid:"4f041f52-739a-4aca-94e6-164f43e61866"]
new [description:"sort by urgency" end:"1692214597" entry:"1691773782" modified:"1692214597" priority:"H" status:"completed" tags:"feat" tags_feat:"x" uuid:"4f041f52-739a-4aca-94e6-164f43e61866"]
---
time 1692255328
new [description:"semantic colors: allow coloring by values, not just fields." entry:"1692255328" modified:"1692255328" status:"pending" tags:"feat,themes" tags_feat:"x" tags_themes:"x" uuid:"1a5aab4a-0c2b-444f-9259-6eddc29b9791"]
---
time 1692255679
old [description:"config file.s" entry:"1691773951" modified:"1691773951" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
new [description:"config: handle config files from taskwarrior first" entry:"1691773951" modified:"1692255679" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
---
time 1692286891
old [description:"config: handle config files from taskwarrior first" entry:"1691773951" modified:"1692255679" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
new [description:"config: handle config files from taskwarrior first" entry:"1691773951" modified:"1692286891" start:"1692286891" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
---
time 1692471868
new [description:"theme config: use taskwarrior theme config and extends from there." entry:"1692471868" modified:"1692471868" status:"pending" tags:"feat,themes" tags_feat:"x" tags_themes:"x" uuid:"6b248843-1b11-487b-85e8-a96e668f6772"]
---
time 1692471959
old [description:"config: handle config files from taskwarrior first" entry:"1691773951" modified:"1692286891" start:"1692286891" status:"pending" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
new [description:"config: handle config files from taskwarrior first" end:"1692471959" entry:"1691773951" modified:"1692471959" status:"completed" tags:"feat" tags_feat:"x" uuid:"4d822c4a-d51e-4992-8a22-6e0167ac197a"]
---

View file

@ -377,7 +377,7 @@ class group:
def call_taskwarrior(args:list[str] = ['export']) -> str:
# Local file.
env = os.environ.copy()
env["TASKDATA"] = asked.data
env["TASKDATA"] = ".task" # FIXME handle updir
cmd = ['task'] + args
try:
@ -558,67 +558,73 @@ def get_layouts(kind = None, name = None):
else:
raise KeyError("cannot get layouts with 'name' only")
# We cannot use tomllib because strings are not quoted.
# We cannot use configparser because there is no section and because of the 'include' command.
# FIXME handle possible values when possible.
def parse_config(filename, default):
config = default
with open(filename, 'r') as fd:
for i,line in enumerate(fd.readlines()):
if line.strip() and line.strip()[0] != '#':
if '=' in line:
key,value = line.split('=')
if '#' in value:
value = value.split('#')[0]
config[key.strip()] = value.strip()
elif "include" in line:
_,path = line.split()
config['includes'].append(path.strip())
else:
print(f"Cannot parse line {i} of config file `{filename}`, I'll ignore it.")
return config
def as_bool(s):
if s.lower() in ["true", "yes", "y", "1"]:
return True
elif s.lower() in ["false", "no", "nope", "n", "0"]:
return False
else:
raise ValueError(f"Cannot interpret `{s}` as a boolean.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="XXX",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
default_conf = {
# taskwarrior
"report.list.columns":"id,priority,description,tags",
parser.add_argument("-s", "--show", metavar="columns", type=str, default="id,priority,description,tags",
help="Ordered list of columns to show.")
# taskwarrior-deluxe
"layout.task": "Raw",
"layout.stack": "RawTable",
"layout.stack.sort": "urgency",
"layout.stack.sort.reverse": "false", # urgency and priority are numeric.
"layout.subsections": "",
"layout.subsections.group": "",
"layout.sections": "Horizontal",
"layout.sections.group": "status",
"design.swatch": "none",
"design.icons": "none",
"widget.card.wrap": "25",
}
# TODO seek files up dans in config paths.
config_tw = parse_config(os.path.expanduser('~/.taskrc'), default_conf)
config = parse_config(os.path.expanduser('~/.twdrc'), config_tw)
config_grp = parser.add_argument_group('configuration options')
# for k in config:
# print(k,"=",config[k])
config_grp.add_argument("-d", "--data", metavar="FILE", type=str, default=".task", nargs=1,
help="The input data file.")
list_separator = ','
config_grp.add_argument("--list-separator", metavar="CHARACTER", type=str, default=",", nargs=1,
help="Separator used for lists that are passed as options arguments.")
cmd = sys.argv[1:]
# TODO add an init command to create config and task files.
#if cmd[0] == "init":
layouts_grp = parser.add_argument_group('layout options')
layouts_grp.add_argument('-t', '--layout-task', metavar='NAME', type=str, default='Raw',
choices = get_layouts('task').keys(), help="Layout managing tasks.")
layouts_grp.add_argument('-k', '--layout-stack', metavar='NAME', type=str, default='RawTable',
choices = get_layouts('stack').keys(), help="Layout managing stacks.")
layouts_grp.add_argument('-c', '--layout-sections', metavar='NAME', type=str, default='Horizontal',
choices = get_layouts('sections').keys(), help="Layout managing sections.")
layouts_grp.add_argument('-C', '--layout-subsections', metavar='NAME', type=str, default=None,
choices = get_layouts('sections').keys(), help="Layout managing sub-sections.")
layouts_grp.add_argument('-g', '--group-by', metavar="NAME", type=str, default="status",
help="Create sections by grouping on this field.")
layouts_grp.add_argument('-G', '--subgroup-by', metavar="NAME", type=str, default=None,
help="Create sub-sections by grouping on this field.")
layouts_grp.add_argument('-S', '--sort', metavar="NAME", type=str, default='urgency',
help="Field on which to sort tasks in a stack.")
layouts_grp.add_argument('-R', '--reverse', action = 'store_true',
help="Reverse the sort of tasks in a stack.")
layouts_grp.add_argument('-T', '--swatch', metavar='NAME', type=str, default='none',
choices = get_swatches().keys(), help="Color chart.")
layouts_grp.add_argument('-I', '--icons', metavar='NAME', type=str, default='none',
choices = get_icons().keys(), help="Additional decorative characters.")
layouts_grp.add_argument('--card-wrap', metavar="NB", type=int, default=25,
help="Number of character at which to wrap the description of Cards tasks.")
# Capture whatever remains.
parser.add_argument('cmd', nargs="*")
asked = parser.parse_args()
# print(asked)
# First pass arguments to taskwarrior and let it do its magic.
out = call_taskwarrior(asked.cmd)
out = call_taskwarrior(cmd)
if "Description" not in out:
print(out.strip())
touched = parse_touched(out)
@ -628,68 +634,67 @@ if __name__ == "__main__":
jdata = get_data()
# print(json.dumps(jdata, indent=4))
print(asked.show)
showed = asked.show.split(asked.list_separator)
showed = config["report.list.columns"].split(list_separator)
if not showed:
show_only = None
else:
show_only = showed
swatch = rich.theme.Theme(get_swatches(asked.swatch))
swatch = rich.theme.Theme(get_swatches(config["design.swatch"]))
layouts = get_layouts()
if asked.layout_task == "Card":
tasker = layouts['task']['Card'](show_only, touched = touched, wrap_width = asked.card_wrap, tag_icons = get_icons(asked.icons)['tag'])
elif asked.layout_task == "Sheet":
icons = get_icons(asked.icons)
tasker = layouts['task']['Sheet'](show_only, touched = touched, wrap_width = asked.card_wrap, tag_icons = icons['tag'], title_ends = icons['short'])
if config["layout.task"] == "Card":
tasker = layouts['task']['Card'](show_only, touched = touched, wrap_width = int(config["widget.card.wrap"]), tag_icons = get_icons(config["design.icons"])['tag'])
elif config["layout.task"] == "Sheet":
icons = get_icons(config["design.icons"])
tasker = layouts['task']['Sheet'](show_only, touched = touched, wrap_width = int(config["widget.card.wrap"]), tag_icons = icons['tag'], title_ends = icons['short'])
else:
tasker = layouts['task'][asked.layout_task](show_only, touched = touched)
tasker = layouts['task'][config["layout.task"]](show_only, touched = touched)
if asked.sort:
if asked.sort == "priority":
sorter = stack.sort.Priority(asked.reverse)
elif asked.sort == "urgency":
sorter = stack.sort.Priority(asked.reverse)
if config["layout.stack.sort"]:
if config["layout.stack.sort"] == "priority":
sorter = stack.sort.Priority(as_bool(config["layout.stack.sort.reverse"]))
elif config["layout.stack.sort"] == "urgency":
sorter = stack.sort.Priority(as_bool(config["layout.stack.sort.reverse"]))
else:
sorter = stack.sort.Field(asked.sort, reverse = asked.reverse)
sorter = stack.sort.Field(config["layout.stack.sort"], reverse = as_bool(config["layout.stack.sort.reverse"]))
else:
sorter = None
stacker = layouts['stack'][asked.layout_stack](tasker, sorter = sorter)
stacker = layouts['stack'][config["layout.stack"]](tasker, sorter = sorter)
if asked.group_by:
if asked.group_by.lower() == "status":
if config["layout.sections.group"]:
if config["layout.sections.group"].lower() == "status":
group_by = group.Status()
g_sort_on = group.sort.OnValues(["pending","started","completed"])
elif asked.group_by.lower() == "priority":
elif config["layout.sections.group"].lower() == "priority":
group_by = group.Field("priority")
g_sort_on = group.sort.OnValues(["H","M","L",""])
else:
group_by = group.Field(asked.group_by)
group_by = group.Field(config["layout.sections.group"])
g_sort_on = None
else:
group_by = group.Status()
g_sort_on = group.sort.OnValues(["pending","started","completed"])
if asked.subgroup_by:
if asked.subgroup_by.lower() == "status":
if config["layout.subsections.group"]:
if config["layout.subsections.group"].lower() == "status":
subgroup_by = group.Status()
g_subsort_on = group.sort.OnValues(["pending","started","completed"])
if asked.subgroup_by.lower() == "priority":
if config["layout.subsections.group"].lower() == "priority":
subgroup_by = group.Field("priority")
g_subsort_on = group.sort.OnValues(["H","M","L",""])
else:
subgroup_by = group.Field(asked.subgroup_by)
subgroup_by = group.Field(config["layout.subsections.group"])
g_subsort_on = None
else:
subgroup_by = None
g_subsort_on = None
if asked.layout_subsections and asked.subgroup_by:
subsectioner = layouts['sections'][asked.layout_subsections](stacker, g_subsort_on, subgroup_by)
sectioner = layouts['sections'][asked.layout_sections](subsectioner, g_sort_on, group_by)
if config["layout.subsections"] and config["layout.subsections.group"]:
subsectioner = layouts['sections'][config["layout.subsections"]](stacker, g_subsort_on, subgroup_by)
sectioner = layouts['sections'][config["layout.sections"]](subsectioner, g_sort_on, group_by)
else:
sectioner = layouts['sections'][asked.layout_sections](stacker, g_sort_on, group_by)
sectioner = layouts['sections'][config["layout.sections"]](stacker, g_sort_on, group_by)
console = Console(theme = swatch)
# console.rule("taskwarrior-deluxe")