tasklib

repository·develop·Indexed 41 days ago

https://github.com/gothenburgbitfactory/tasklib

A Python library for interacting with Taskwarrior databases, providing a high-level, Django-style ORM interface to manage tasks, tags, and statuses. It supports Taskwarrior 2.4.x or above and Python 3.5+, featuring a TaskQuerySet for filtering, automatic datetime deserialization, and support for User Defined Attributes (UDAs) and hook scripts.

Tokens
3.8K
Snippets
17
Records
18
Agent score
50%

What's inside tasklib

  1. Working with User Defined Attributes (UDAs)

    develop

    Tasklib integrates with TaskWarrior UDAs defined in your .taskrc. If a UDA is defined (e.g., uda.estimate.type = numeric), you can use it immediately when creating tasks, saving them, or filtering.

    tw = TaskWarrior()
    
    # Create a task with a UDA
    task = Task(tw, description="Long task", estimate=1000)
    task.save()
    
    # Filter tasks by a UDA
    long_tasks = tw.tasks.filter(estimate=1000)
  2. Dealing with dates and time in tasks

    develop

    Tasklib automatically converts timestamp-like attributes to timezone-aware datetime objects using the zoneinfo module.

    Timezone Handling

    • Automatic Detection: The TaskWarrior object uses tzlocal to detect your system's timezone. You can pass naive datetime objects, and they will be treated as being in your local timezone.
    • Filtering: You can filter tasks using datetime, date, or even TaskWarrior-formatted time strings (requires TaskWarrior >= 2.4.0).
    • Comparison Warning: Since Python cannot compare offset-naive and offset-aware datetimes, ensure you localize naive objects using .astimezone() before comparing them to task attributes.

    Using TaskWarrior Time Expressions

    For TaskWarrior >= 2.4.0, you can assign strings containing TaskWarrior time expressions (like "now + 1d") directly to datetime attributes. Tasklib will use task calc to convert these into local timezone-aware datetime objects.

    # Automatic timezone detection for filtering
    tw.tasks.filter(due__before=datetime(2015, 2, 2, 0, 0, 0))
    
    # Using TaskWarrior time expressions (TaskWarrior >= 2.4.0)
    t['due'] = "now + 1d"
    
    # Comparing naive vs aware (localize the naive one)
    from datetime import datetime
    now = datetime.now().astimezone()
    t['due'] == now
  3. Sync TaskWarrior data

    develop

    To sync your tasks with a TaskWarrior server, use tw.execute_command(['sync']).

    If you need to use non-standard server credentials or certificates, update the tw.config dictionary with the required settings before running the sync command.

    tw = TaskWarrior()
    
    # Standard sync
    tw.execute_command(['sync'])
    
    # Sync with custom configuration
    tw.config.update({
        'taskd.certificate': '/path/to/cert.pem',
        'taskd.server': 'task.server.com:53589',
        'taskd.ca': '/path/to/ca.pem'
    })
    tw.execute_command(['sync'])
  4. Create TaskWarrior hook scripts

    develop

    Tasklib provides helpers to write TaskWarrior hook scripts (e.g., for on-add or on-modify events).

    Implementation Pattern

    1. Use Task.from_input() to load the task being processed by the hook.
    2. Perform your logic.
    3. Use task.export_data() to print the modified task data to stdout.

    Handling on-modify events

    In on-modify events, Tasklib loads both the original and the modified task. You can access the read-only original data using the original dict-like attribute on the Task object.

    #!/usr/bin/python
    from tasklib.task import Task
    
    task = Task.from_input()
    
    # Access original data in on-modify events
    if task.original.get('description') != task['description']:
        print("Description changed!")
    
    # Modify task
    if "!!!" in task['description']:
        task['priority'] = "H"
    
    # Export for TaskWarrior
    print(task.export_data())
  5. Working with Task Annotations

    develop

    Annotations are represented by TaskAnnotation objects. They are simplified versions of Task objects containing only entry (a datetime) and description (a string).

    Managing Annotations

    1. Retrieve: Access them via the annotations key on a task.
    2. Add: Use task.add_annotation("description"). Note: You must call task.save() before adding an annotation to a new task.
    3. Remove: Use task.remove_annotation("description") or pass the TaskAnnotation object itself.
    # Adding an annotation
    task = Task(tw, description="new task")
    task.save()
    task.add_annotation("we can annotate saved tasks")
    
    # Removing an annotation by description
    task.remove_annotation("we can annotate saved tasks")
    
    # Removing an annotation by object
    annotation = task['annotations'][0]
    task.remove_annotation(annotation)
  6. Initialize the TaskWarrior instance

    develop

    To interact with TaskWarrior, initialize a TaskWarrior instance. By default, it uses the standard TaskWarrior data location and your .taskrc configuration.

    • Use data_location to specify the database directory. If it doesn't exist, it will be created unless create=False is passed.
    • Use taskrc_location to override the location of the .taskrc configuration file.
    >>> tw = TaskWarrior(data_location='~/.task', create=True)
  7. Basic usage of tasklib

    develop

    Use tasklib to interact with your Taskwarrior data. The library provides a TaskWarrior object that acts as an entry point to your database (typically located at ~/.task). It uses a queryset API similar to Django's ORM to filter, retrieve, and modify tasks.

    Key capabilities demonstrated:

    • Initializing a connection to a Taskwarrior directory.
    • Fetching pending tasks using .pending().
    • Filtering tasks using keyword arguments (e.g., tags__contain).
    • Marking tasks as done using .done().
    • Updating task attributes and persisting changes with .save().
    from tasklib import TaskWarrior
    
    # Initialize with the path to your taskwarrior directory
    tw = TaskWarrior('~/.task')
    
    # Get pending tasks
    tasks = tw.tasks.pending()
    
    # Filter tasks by tags
    chores = tasks.filter(tags__contain='chores')
    
    # Mark a task as done
    tasks[0].done()
    
    # Update a task attribute and save
    tasks[0]['tags'] = ['languages']
    tasks[0].save()
  8. Filter and retrieve Tasks using TaskQuerySet

    develop

    The tw.tasks object is a TaskQuerySet that emulates the Django ORM API.

    Common Retrieval Methods

    • .all(): Returns all tasks.
    • .pending(): Returns only pending tasks.
    • .completed(): Returns only completed tasks.
    • .get(**kwargs): Returns the single task matching the query. Raises tasklib.task.DoesNotExist if no task is found, or ValueError if more than one task matches.

    Filtering Syntax

    You can filter using keyword arguments (where __ is used for lookups) or by passing raw TaskWarrior command strings.

    Note: Using raw commands is discouraged as it may reduce portability across TaskWarrior versions, but it is useful for syntax not yet supported by tasklib (e.g., virtual tags).

    # Keyword filtering
    tw.tasks.filter(status='pending', tags__contains=['work'])
    
    # Raw command filtering
    tw.tasks.filter('status:pending +work')
    
    # Mixing raw commands and keywords
    tw.tasks.filter('+BLOCKING', project='Home')
    
    # Stacking filters
    home_tasks = tw.tasks.filter(project='Wife')
    results = home_tasks.filter(due__before=datetime(2015,2,14,14,14,14))