Compare commits

..

7 commits

Author SHA1 Message Date
426925c6a7
Fix missing mention of idea toggle in README
Added usage instructions for turning off idea syncing.
2021-12-14 18:00:48 +01:00
253dc4ba37
Fix default file path
Fixed the location of the default file path to mirror the one mentioned
in the README. It now roughly adheres to the XDG home data location as
its default.

Added simple path validation for passed in file paths - will not see if
its readable/writable but simply look for the file's existence and error
out otherwise.
2021-12-14 17:57:19 +01:00
417536f1b5
Switching subprocess commands to recommended ones
Switched out all `subprocess.Popen` statements for `subprocess.run`
since they are the more recommended and less low-level versions.
2021-12-14 17:55:48 +01:00
978da38001
Fix file writing and replacement process 2021-11-01 12:01:10 +01:00
ec5ff47449
Refactor options 2021-11-01 12:00:48 +01:00
c6cb3dd6c3
Add idea parsing for jrnl file 2021-11-01 10:50:49 +01:00
787a24c6dc
Refactor subprocess stdlib import 2021-11-01 10:28:55 +01:00
4 changed files with 123 additions and 40 deletions

View file

@ -1,13 +0,0 @@
# You can override the included template(s) by including variable overrides
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
- test
sast:
stage: test
include:
- template: Security/SAST.gitlab-ci.yml

View file

@ -12,13 +12,52 @@ More specifically, it:
(marked with an empty `[ ] ` at the beginning of their line)
* adds an entry for today's date if none exists yet and populates it with due/overdue tasks from `taskwarrior`
To accomplish this it borrows a little from the [todo.txt](http://todotxt.org/) syntax ---
namely the idea of (A) (B) (C) prioritization and `x task done syntax`
(i.e. starting a line with `x ` or `[x] ` means it represents an accomplished task).
All three of these operations *only* operate on entries which are marked with a special title (`todotxt` by default),
though this can be changed through a regex option.
That means, it is also entirely possible to have other entries (with the same date) which will simply be ignored by the script.
To accomplish this it borrows a little from the [todo.txt](http://todotxt.org/) syntax ---
namely the idea of (A) (B) (C) prioritization and `x task done syntax`
(i.e. starting a line with `x ` or `[x] ` means it represents an accomplished task).
Lastly, it transfers what I call 'ideas' to `taskwarrior`.
These are small tidbits and notions I jot down during the day (hence, 'ideas')
and marked as such by starting the line either with `idea: ` or the `taskwarrior` equivalent `+idea `.
I want them transferred to `taskwarrior` so they don't get lost overall,
but they should be
a) not bound to a specific date, even when written in a specific `jrnl` entry, and
b) specifically marked as vague ideas in `taskwarrior`.
For now, they will be applied the fixed tags `+idea +maybe` in `taskwarrior` and simply
transferred date-less.
An example `jrnl` file:
```jrnl
[2021-10-30 10:16] todotxt
This is just a test entry. It shows how tasks in todo blocks are handled.
[ ] This is an incomplete task - it will be transferred directly to tw.
[x] This is a completed task - it will be logged as completed in tw.
[ ] (A) This is an incomplete task which will get assigned high priority in taskwarrior.
x This is also a completed task, as long as the x is the first thing on the line with a space behind.
x All four of these tasks will be transferred to tw, and their lines deleted from this file.
idea: This is an idea and it will be transferred to tw with corresponding tags, then deleted from this file.
This is not a task but a normal line and will remain in the file.
xAs will this,
[ ]As will this.
[ ] This will be transferred again, however.
[2021-10-29 10:16] Another entry
This is a regular jrnl entry, since it is missing the correct title.
By default it looks for `todotxt` titles,
but the title can be freely set for the script (`-b` option).
jrnlwarrior will not look for incomplete or completed tasks within this block at all.
[x] This is a completed task, but it will not be transferred to taskwarrior.
[ ] Neither will this incomplete task.
+idea This idea however *will* be transferred, since ideas are looked for in *all* entries.
```
## Usage
@ -49,21 +88,21 @@ lines to be removed from the file and any to-do entries added to the file.
It will not change your `jrnl` file in any way.
If you want to switch off one of the three ways this script connects the two,
you can use the `-L`, `-I`, `-T` options which turn off logging, adding, or creating entries respectively.
you can use the `-L`, `-I`, `-T`, `-D` options which turn off [*L*]ogging completed to `taskwarrior`, [*I*]nserting incomplete to `taskwarrior`, creating new `jrnl` entries for [*T*]oday's tasks, or moving hatched i[*D*]eas to `taskwarrior` respectively.
```bash
./jrnlwarrior.py -I -T
./jrnlwarrior.py -DIT
```
The above invocation will *only* log completed tasks to `taskwarrior`.
Adding new to-dos and creating today entries is turned off.
Adding new to-dos, ideas and creating today entries is turned off.
```bash
./jrnlwarrior.py -T
```
This will create a one-way connection to `taskwarrior`,
by transferring both new and completed tasks to the program.
by transferring both new and completed tasks and ideas to the program.
However, tasks to be done today will not be transferred back to this file.
## Scope

View file

@ -8,7 +8,7 @@
import re
import os
import sys
from subprocess import Popen, PIPE, check_output
import subprocess
import options as opts
import json
from datetime import datetime
@ -35,11 +35,15 @@ def process_file(options):
fname = options.jrnl_fname
lines_to_delete = []
today_exists = False
curdate = ""
with open(fname) as file:
line_number = 0
for line in file:
line_number += 1
if handle_idea(line, options):
lines_to_delete.append(line_number)
todo_block = get_todo_block_date(line, options.todo_block_title)
if todo_block == False:
curdate = ""
@ -59,11 +63,11 @@ def process_file(options):
if is_today(curdate):
today_exists = True
if lines_to_delete:
delete_lines_from_file(fname, lines_to_delete, options.dryrun)
if lines_to_delete:
delete_lines_from_file(fname, lines_to_delete, options.dryrun)
if not today_exists and options.taskwarrior_fill_today:
add_today(fname, options)
if not today_exists and options.taskwarrior_fill_today:
add_today(fname, options)
def handle_completed_tasks(line, date, options):
@ -91,7 +95,7 @@ def log_completed_to_taskwarrior(task_string, date, options):
if options.dryrun:
print(cmd)
return
Popen(cmd)
subprocess.run(cmd)
def handle_open_tasks(line, date, options):
@ -119,7 +123,34 @@ def add_incomplete_to_taskwarrior(task_string, date, options):
if options.dryrun:
print(cmd)
return
Popen(cmd)
subprocess.run(cmd)
def handle_idea(line, options):
completed_task = re.search(
rf"^{options.regex_task_idea} (.*)",
line,
)
if completed_task and options.taskwarrior_add_ideas:
add_idea_to_taskwarrior(completed_task[1], options)
return True
return False
def add_idea_to_taskwarrior(idea_string, options):
overrides = options.taskwarrior_overrides
cmd = [
"task",
*overrides,
"add",
"+idea",
"+maybe",
idea_string,
]
if options.dryrun:
print(cmd)
return
subprocess.run(cmd)
def get_prio_string(task_string):
@ -160,21 +191,23 @@ def is_today(cur_date):
def add_today(fname, options):
cmd = ["task", "+TODAY or +OVERDUE", "export"]
cmd = ["task", *options.taskwarrior_overrides, "+TODAY or +OVERDUE", "export"]
due_json = json.loads(subprocess.run(cmd, capture_output=True).stdout)
tasks = f"[{TODAY} 09:00] {options.todo_block_title}\n"
for task in json.loads(check_output(cmd)):
for task in due_json:
tasks += f"{task['description']}\n"
tasks += "\n"
if options.dryrun:
print(f"\nWRITING TODAY:\n{tasks}")
return
repl_file = fname + ".bak"
with open(fname) as read_file, open(repl_file, "w") as write_file:
write_file.write(tasks + "\n")
for line in read_file:
write_file.write(line)
os.rename(repl_file, fname)
repl_fname = fname + ".bak"
with open(fname, "r") as read_file, open(repl_fname, "w") as write_file:
write_file.write(tasks)
write_file.write(read_file.read())
os.rename(repl_fname, fname)
if __name__ == "__main__":

View file

@ -1,4 +1,5 @@
import os
import sys
import argparse
from dataclasses import dataclass, field
from typing import List
@ -6,13 +7,15 @@ from typing import List
@dataclass
class Options:
# can be changed
jrnl_fname: str = f"{os.path.expanduser('~')}/documents/records/todo.md"
jrnl_fname: str = field(
default_factory=lambda: f"{os.environ.get('XDG_DATA_HOME', os.environ.get('HOME'))}/jrnl/journal.txt"
)
todo_block_title: str = "todotxt"
dryrun: bool = False
taskwarrior_log_completed: bool = True
taskwarrior_add_incomplete: bool = True
taskwarrior_fill_today: bool = True
taskwarrior_add_ideas: bool = True
# can not yet be changed
taskwarrior_overrides: List = field(
@ -24,13 +27,18 @@ class Options:
)
regex_task_completed: str = r"(?:x|\[x\])"
regex_task_incomplete: str = r"\[ \]"
regex_task_idea: str = r"(?:idea:|\+idea)"
def init():
opt = Options()
parse_cmdline_args(opt)
parser = argparse.ArgumentParser()
create_cmdline_args(parser)
parse_cmdline_args(parser, opt)
if opt.dryrun:
dryrun_show_options(opt)
validate_opts(opt)
return opt
@ -38,8 +46,7 @@ def dryrun_show_options(options):
print(options)
def parse_cmdline_args(options):
parser = argparse.ArgumentParser()
def create_cmdline_args(parser):
parser.add_argument(
"-n", "--dryrun", help="only simulate and print changes", action="store_true"
)
@ -63,6 +70,15 @@ def parse_cmdline_args(options):
help="don't add today's todo items as entry to file",
action="store_true",
)
parser.add_argument(
"-D",
"--noidea",
help="don't add ideas as maybe items to taskwarrior",
action="store_true",
)
def parse_cmdline_args(parser, options):
args = parser.parse_args()
if args.dryrun:
@ -71,6 +87,8 @@ def parse_cmdline_args(options):
options.taskwarrior_log_completed = False
if args.noincomplete:
options.taskwarrior_add_incomplete = False
if args.noidea:
options.taskwarrior_add_ideas = False
if args.nofilltoday:
options.taskwarrior_fill_today = False
if args.file:
@ -79,3 +97,9 @@ def parse_cmdline_args(options):
options.todo_block_title = args.blocktitle
return options
def validate_opts(options):
if not os.path.isfile(options.jrnl_fname):
print(f"{options.jrnl_fname} does not seem to be a file. Aborting.")
sys.exit(1)