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
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,21 @@ Just call mtt_sync script that will do it all for you.
mtt_sync
```

Sample real-life call : ` mtt_sync --mask "documentation/Agenda/*.md" --project paymetrics`
Available options:

| Option | Description |
| ------ | ----------- |
| `--mask PATTERN` | File pattern to filter markdown files (e.g., `"*.md"` or `"tasks/*.md"`) |
| `--project NAME` | Assign tasks to a specific project |
| `--output FILE` | Output file path (default: `tasks.ndjson`) |
| `--tags TAGS` | Comma-separated list of default tags to add to all tasks |

Sample real-life calls:

```bash
mtt_sync --mask "documentation/Agenda/*.md" --project paymetrics
mtt_sync --mask "daily/*.md" --project daily --tags "obsidian,daily" --output "daily_tasks.ndjson"
```

#### more low level

Expand Down
18 changes: 13 additions & 5 deletions mtt_md_add_uuids.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,19 @@ while [[ "$#" -gt 0 ]]; do
shift
done

# Extract directory and pattern from file_pattern (same as mtt_md_to_taskwarrior.sh)
dir=$(dirname "$file_pattern")
pattern=$(basename "$file_pattern")

echo "Directory: $dir"
echo "Pattern: $pattern"

# First pass: build mapping of short IDs to UUIDs
echo "First pass: Building mapping of short IDs to UUIDs..."
echo "Searching for short IDs with pattern: \\[id:: [a-z0-9]{6}\\]"
debug_echo "Running: rg --no-heading --line-number --with-filename \"\\[id:: [a-z0-9]{6}\\]\" $file_pattern"
rg --no-heading --line-number --with-filename "\\[id:: [a-z0-9]{6}\\]" $file_pattern | while IFS=: read -r file line_number line; do
debug_echo "Running: rg --no-heading --line-number --with-filename \"^- \\[ \\] \" --glob \"$pattern\" \"$dir\""

rg --no-heading --line-number --with-filename "^- \[ \] " --glob "$pattern" "$dir" | while IFS=: read -r file line_number line; do
echo "Found line with short ID: $line"
# Extract the short ID using regex
if [[ $line =~ \[id::\ ([a-z0-9]{6})\] ]]; then
Expand All @@ -105,7 +113,7 @@ if [[ -f /tmp/id_mappings.tmp ]]; then
echo "Updating references..."
while IFS=: read -r short_id uuid; do
echo "Updating dependsOn: $short_id -> $uuid in all files"
rg --files-with-matches "\[dependsOn:: $short_id\]" $file_pattern | while read -r file; do
rg --files-with-matches "\[dependsOn:: $short_id\]" --glob "$pattern" "$dir" | while read -r file; do
echo "Updating dependsOn in $file"
sed -i.bak "s/\[dependsOn:: $short_id\]/[dependsOn:: $uuid]/g" "$file"
rm -f "${file}.bak"
Expand All @@ -116,7 +124,7 @@ if [[ -f /tmp/id_mappings.tmp ]]; then
echo "Updating task IDs..."
while IFS=: read -r short_id uuid; do
echo "Updating ID: $short_id -> $uuid in all files"
rg --files-with-matches "\[id:: $short_id\]" $file_pattern | while read -r file; do
rg --files-with-matches "\[id:: $short_id\]" --glob "$pattern" "$dir" | while read -r file; do
echo "Updating ID in $file"
sed -i.bak "s/\[id:: $short_id\]/[id:: $uuid]/g" "$file"
rm -f "${file}.bak"
Expand All @@ -128,7 +136,7 @@ fi
rm -f /tmp/id_mappings.tmp

# Fourth pass: Add UUIDs to tasks without any ID
rg --no-heading --line-number --with-filename "^- \\[ \\] " $file_pattern | while IFS=: read -r file line_number line; do
rg --no-heading --line-number --with-filename "^- \[ \] " --glob "$pattern" "$dir" | while IFS=: read -r file line_number line; do
echo "......................................"
echo "scanning file $file"
echo "scanning line $line"
Expand Down
130 changes: 89 additions & 41 deletions mtt_md_to_taskwarrior.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@ fi

# Show help message
show_help() {
echo "Usage: mtt_md_to_taskwarrior.sh [--help] [--mask PATTERN] [--project NAME]"
echo "Usage: mtt_md_to_taskwarrior.sh [--help] [--mask PATTERN] [--project NAME] [--output FILE] [--tags TAGS]"
echo
echo "Export markdown tasks to TaskWarrior compatible NDJSON format."
echo
echo "The script searches for markdown task items (- [ ]) in files matching the mask pattern"
echo "and extracts task attributes like start date, end date, due date,..."
echo "and task ID into a TaskWarrior import compatible NDJSON file (tasks.ndjson)."
echo "and task ID into a TaskWarrior import compatible NDJSON file."
echo
echo "Options:"
echo " --help Show this help message"
echo " --mask PATTERN File pattern to search (default: *.md)"
echo " --project NAME Assign tasks to a specific project"
echo " --output FILE Output file path (default: tasks.ndjson)"
echo " --tags TAGS Comma-separated list of default tags to add to all tasks"
echo
echo "Environment Variables:"
echo " OE_MASK Alternative to --mask (command line takes precedence)"
echo " OE_PROJECT Alternative to --project (command line takes precedence)"
echo " OE_OUTPUT Alternative to --output (command line takes precedence)"
echo " OE_TAGS Alternative to --tags (command line takes precedence)"
exit 0
}

Expand All @@ -51,31 +55,56 @@ format_date() {
# Set defaults from environment variables or fallback values
file_mask="${OE_MASK:-*.md}"
project_name="${OE_PROJECT:-}"
output_file="${OE_OUTPUT:-tasks.ndjson}"
default_tags="${OE_TAGS:-}"

# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--help) show_help ;;
--mask)
shift
if [[ -n "$1" ]]; then
file_mask="$1"
else
echo "Error: --mask requires a pattern"
show_help
exit 1
fi
;;
--project)
shift
if [[ -n "$1" ]]; then
project_name="$1"
else
echo "Error: --project requires a name"
show_help
fi
;;
*) echo "Unknown parameter: $1"; show_help ;;
--help) show_help ;;
--mask)
shift
if [[ -n "$1" ]]; then
file_mask="$1"
else
echo "Error: --mask requires a pattern"
show_help
exit 1
fi
;;
--project)
shift
if [[ -n "$1" ]]; then
project_name="$1"
else
echo "Error: --project requires a name"
show_help
fi
;;
--output)
shift
if [[ -n "$1" ]]; then
output_file="$1"
else
echo "Error: --output requires a file path"
show_help
exit 1
fi
;;
--tags)
shift
if [[ -n "$1" ]]; then
default_tags="$1"
else
echo "Error: --tags requires a comma-separated list of tags"
show_help
exit 1
fi
;;
*)
echo "Unknown parameter: $1"
show_help
;;
esac
shift
done
Expand All @@ -85,17 +114,21 @@ echo "Current configuration:"
echo "~~~~~~~~~~~~~~~~~~~~"
echo "File mask: $file_mask"
echo "Project: ${project_name:-<none>}"
echo "Output file: tasks.ndjson"
echo "Output file: $output_file"
echo "Default tags: ${default_tags:-<none>}"
echo "~~~~~~~~~~~~~~~~~~~~"
echo

# Create or overwrite the output file
output_file="tasks.ndjson"
> "$output_file"
>"$output_file"

# Use ripgrep (rg) to search all files at once
echo "calling ripgrep with : rg --no-heading --line-number --with-filename \"^- \\[ \\] \" \"$file_mask\""
rg --no-heading --line-number --with-filename "^- \\[ \\] " $file_mask | while IFS=: read -r file line_number line; do
echo "calling ripgrep with : rg --no-heading --line-number --with-filename \"^- \\[ \\] \" --glob \"$file_mask\""

dir=$(dirname "$file_mask")
pattern=$(basename "$file_mask")

rg --no-heading --line-number --with-filename "^- \[ \] " --glob "$pattern" "$dir" | while IFS=: read -r file line_number line; do
echo "......................................................................"
echo "scanning file $file"
echo "scanning line $line"
Expand Down Expand Up @@ -179,12 +212,12 @@ rg --no-heading --line-number --with-filename "^- \\[ \\] " $file_mask | while
echo "found priority : $priority"
# Convert the priority : high to H,...
if [ -n "$priority" ]; then
case "$priority" in # Convert to lowercase for comparison
"highest") priority="H" ;;
"high") priority="H" ;;
"medium") priority="M" ;;
"low") priority="L" ;;
"lowest") priority="L" ;;
case "$priority" in # Convert to lowercase for comparison
"highest") priority="H" ;;
"high") priority="H" ;;
"medium") priority="M" ;;
"low") priority="L" ;;
"lowest") priority="L" ;;
esac
fi
echo "converted priority : $priority"
Expand All @@ -203,14 +236,29 @@ rg --no-heading --line-number --with-filename "^- \\[ \\] " $file_mask | while

# Combine all tags, removing duplicates
all_tags=""
if [ -n "$at_tags" ] || [ -n "$hash_tags" ]; then
# Combine tags with comma only if both are non-empty
combined_tags=""
if [ -n "$at_tags" ] && [ -n "$hash_tags" ]; then
combined_tags="${at_tags},${hash_tags}"
combined_tags=""
# Start with default tags if provided
if [ -n "$default_tags" ]; then
combined_tags="$default_tags"
fi
# Add @ tags if present
if [ -n "$at_tags" ]; then
if [ -n "$combined_tags" ]; then
combined_tags="${combined_tags},${at_tags}"
else
combined_tags="$at_tags"
fi
fi
# Add # tags if present
if [ -n "$hash_tags" ]; then
if [ -n "$combined_tags" ]; then
combined_tags="${combined_tags},${hash_tags}"
else
combined_tags="${at_tags}${hash_tags}"
combined_tags="$hash_tags"
fi
fi
# Remove duplicates
if [ -n "$combined_tags" ]; then
all_tags=$(echo "$combined_tags" | tr ',' '\n' | sort -u | tr '\n' ',' | sed 's/,$//')
fi
if [ -n "$all_tags" ]; then
Expand All @@ -235,7 +283,7 @@ rg --no-heading --line-number --with-filename "^- \\[ \\] " $file_mask | while
json+=",\"annotations\":[{\"description\":\"Source: $abs_file_path\"}]"
json+="}"

echo "$json" >> "$output_file"
echo "$json" >>"$output_file"
echo "......................................................................"
done

Expand Down
32 changes: 29 additions & 3 deletions mtt_sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ show_help() {
echo " --help Show this help message"
echo " --mask PATTERN Specify a file pattern to filter markdown files"
echo " Example: --mask \"*.md\" or --mask \"tasks/*.md\""
echo " --project NAME Assign tasks to a specific project"
echo " --project NAME Assign tasks to a specific project"
echo " --output FILE Output file path (default: tasks.ndjson)"
echo " --tags TAGS Comma-separated list of default tags to add to all tasks"
echo
echo "Description:"
echo " This script performs the following operations:"
Expand All @@ -25,6 +27,8 @@ show_help() {
echo "Note: Make sure TaskWarrior (task) is installed and properly configured."
}

# Default values
output_file="tasks.ndjson"

# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
Expand All @@ -50,6 +54,26 @@ while [[ "$#" -gt 0 ]]; do
exit 1
fi
;;
--output)
shift
if [[ -n "$1" ]]; then
output_file="$1"
else
echo "Error: --output requires a file path"
show_help
exit 1
fi
;;
--tags)
shift
if [[ -n "$1" ]]; then
default_tags="$1"
else
echo "Error: --tags requires a comma-separated list of tags"
show_help
exit 1
fi
;;
*) echo "Unknown parameter: $1"; show_help; exit 1 ;;
esac
shift
Expand All @@ -69,14 +93,16 @@ fi
uuid_args=()
[ -n "$file_pattern" ] && uuid_args+=(--mask "$file_pattern")

# Build command arguments for mtt_md_to_taskwarrior.sh (needs both mask and project)
# Build command arguments for mtt_md_to_taskwarrior.sh (needs mask, project, output, and tags)
export_args=()
[ -n "$file_pattern" ] && export_args+=(--mask "$file_pattern")
[ -n "$project_name" ] && export_args+=(--project "$project_name")
[ -n "$output_file" ] && export_args+=(--output "$output_file")
[ -n "$default_tags" ] && export_args+=(--tags "$default_tags")

# Execute scripts with their respective arguments
"$SCRIPT_DIR/mtt_md_add_uuids.sh" "${uuid_args[@]:-}"
"$SCRIPT_DIR/mtt_md_to_taskwarrior.sh" "${export_args[@]:-}"

echo
task import tasks.ndjson
task import "$output_file"