Coder Social home page Coder Social logo

thingsapi / things.py Goto Github PK

View Code? Open in Web Editor NEW
115.0 5.0 16.0 1.31 MB

A simple Python 3 library to read your Things app data.

Home Page: https://thingsapi.github.io/things.py/things/api.html

License: Apache License 2.0

Makefile 6.50% Python 93.50%
python database things things3 gtd

things.py's Introduction

Things Python API

things.py is a simple Python 3 library to read data from your Things app.

GitHub Release Build Status Coverage Status Maintainability Rating GitHub Issues License PyPI - Downloads GitHub Download Count

Table of Contents

Install

$ pip3 install things.py
# or
$ git clone https://github.com/thingsapi/things.py && cd things.py && make install

Examples

>>> import things
>>> things.todos()
[{'uuid': '2Ukg8I2nLukhyEM7wYiBeb',
  'type': 'to-do',
  'title': 'Make reservation for dinner',
  'project': 'bNj6TPdKYhY6fScvXWVRDX',
  ...},
 {'uuid': 'RLZroza3jz0XPs3uAlynS7',
  'type': 'to-do',
  'title': 'Buy a whiteboard and accessories',
  'project': 'w8oSP1HjWstPin8RMaJOtB',
  'notes': "Something around 4' x 3' that's free-standing, two-sided, and magnetic.",
  'checklist': True,
  ...
>>> things.todos('RLZroza3jz0XPs3uAlynS7')
{'uuid': 'RLZroza3jz0XPs3uAlynS7',
 'type': 'to-do',
 'title': 'Buy a whiteboard and accessories',
 ...
 'checklist': [
     {'title': 'Cleaning Spray', 'status': 'completed', ...},
     {'title': 'Magnetic Eraser', 'status': 'incomplete', ...},
     {'title': 'Round magnets', 'status': 'incomplete', ...}
 ]
 ...
}

>>> things.projects()
[{'uuid': 'bNj6TPdKYhY6fScvXWVRDX',
  'type': 'project',
  'title': 'Throw Birthday Party',
  'area': 'bNj6TPdKYhY6fScvXWVRDX',
  ...},
 {'uuid': 'w8oSP1HjWstPin8RMaJOtB',
  'type': 'project',
  'title': 'Set Up Home Office',
  'area': 'Gw9QefIdgR6nPEoY5hBNSh',
  ...

>>> things.areas()
[{'uuid': 'ToLxnnBrWkfHC3tkx4vxdV',
  'type': 'area',
  'title': 'Family',
  ...},
 {'uuid': 'Gw9QefIdgR6nPEoY5hBNSh',
  'type': 'area',
  'title': 'Apartment',
  ...

>>> things.tags()
[{'uuid': 'CKILg3kKF2jlCRisNFcqOj',
  'type': 'tag',
  'title': 'Home',
  'shortcut': None},
 {'uuid': 'gfmpz8zxnyfqMDvRi3E8vo',
  'type': 'tag',
  'title': 'Office',
  'shortcut': None},
 ...

>>> things.get('CKILg3kKF2jlCRisNFcqOj')
{'uuid': 'CKILg3kKF2jlCRisNFcqOj',
  'type': 'tag',
  'title': 'Home',
  'shortcut': None}

Background

The task management app Things stores all your to-dos in a SQLite database file (details here). This format is intended to be machine-readable, not human-readable. The aim of this project is to let you access all your Things data in a human-readable way. We thereby stay as true to the database as possible while doing SQL joins and transformations to aid understanding of the data. Note that you can print the SQL used by adding the parameter print_sql=True to most API calls.

If any aspect of the API seems overly complex or doesn't meet your needs, please don't hesitate to add a new issue here.

Terminology

Here are the core technical terms used involving the database:

  • area
  • tag
  • task
    • type
      • 'to-do': can have checklists;
      • 'project': can have to-dos and headings;
      • 'heading': part of a project; groups to-dos.
    • status: 'incomplete', 'canceled', or 'completed'
    • trashed: True or False
    • start: 'Inbox', 'Anytime', or 'Someday'
  • checklist item (contained within a to-do)

Documentation

The full documentation for this library can be found here: https://thingsapi.github.io/things.py/things/api.html

Things URL Scheme

You can make good use of the uuid to link to to-dos, areas, tags, and more from other apps. Also updates are possible. Read an introduction here and see the documentation here.

Contributing

We welcome contributions to things.py! Before submitting a pull request, please take a moment to look over the contributing guidelines first.

Used By

The following open-source projects make use of this library:

things.py's People

Contributors

alexanderwillner avatar bkleinen avatar caalden avatar cato447 avatar chrisgurney avatar github-actions[bot] avatar lmgibson avatar mikez avatar minthemiddle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

things.py's Issues

Comparison between things.sh and things.py

To Reproduce
Steps to reproduce the behavior:

  1. Checkout things.sh
  2. Run the script shown below

Expected behavior

  • Probably same results (or at least double check).
  • An execution time under a second for each command (performance issue logbook, canceled, completed, all).

Current results
Results with my private (large) database:

% ./test.sh
Application: ./things.sh
  - Command: inbox. Results: 4. Duration: 0m0.135s
  - Command: today. Results: 12. Duration: 0m0.127s
  - Command: upcoming. Results: 168. Duration: 0m0.132s
  - Command: areas. Results: 47. Duration: 0m0.102s
  - Command: projects. Results: 82. Duration: 0m0.110s
  - Command: tags. Results: 186. Duration: 0m0.293s
  - Command: deadlines. Results: 47. Duration: 0m0.106s
  - Command: logbook. Results: 1. Duration: 0m0.132s
  - Command: canceled. Results: 47. Duration: 0m0.100s
  - Command: anytime. Results: 47. Duration: 0m0.096s
  - Command: completed. Results: 21138. Duration: 0m0.193s
  - Command: all. Results: 1334. Duration: 0m0.141s
Application: things-cli
  - Command: inbox. Results: 4. Duration: 0m0.444s
  - Command: today. Results: 12. Duration: 0m0.352s
  - Command: upcoming. Results: 52. Duration: 0m0.347s
  - Command: areas. Results: 18. Duration: 0m0.341s
  - Command: projects. Results: 82. Duration: 0m0.333s
  - Command: tags. Results: 186. Duration: 0m0.321s
  - Command: deadlines. Results: 10. Duration: 0m0.330s
  - Command: logbook. Results: 36859. Duration: 0m33.369s
  - Command: canceled. Results: 15030. Duration: 0m39.870s
  - Command: anytime. Results: 150. Duration: 0m0.356s
  - Command: completed. Results: 21829. Duration: 0m33.277s
  - Command: all. Results: 1184. Duration: 0m0.498s

Script

#!/usr/bin/env bash

readonly commands="inbox today upcoming areas projects tags deadlines logbook canceled anytime completed all"

run() {
    bin="$1"
    result="$(mktemp)"
    timing="$(mktemp)"
    echo "Application: $bin"
    for command in $commands; do
      time ($bin $command > $result) > $timing 2>&1
      duration=$(grep real $timing|awk '{print $2}')
      results=$(cat $result|wc -l|xargs)
      echo "  - Command: $command. Results: $results. Duration: $duration"
    done
}

run ./things.sh
run things-cli

Changes in DB version 24

As discussed in #100, #101, #102 and addressed in #103, #105, #106, #107 the Things DB changed on Apr 13, 2023 in several ways:

  • The location changed
  • Some tabled were renamed
  • As discussed in #98 some date formats changed (e.g., startDate)

Due to the change in the date format the tests do not pass anymore:

% make test
.........F..........F.F..F
test_deadlines
AssertionError: 3 != 4

test_tags
AssertionError: 1 != 0

test_today
AssertionError: 4 != 6

test_upcoming
AssertionError: 1 != 0

FAILED (failures=4)

Interoperability with built-in Things API

Goal
I just discovered that the Things.app has a built-in API interface. See here. One might consider making our names consistent to this API. Also, maybe we can make good use of it as well.

Suggested approach
Look more into the provided Things API and how it can improve this library.

Add an option to include "yellow" tasks

Goal

  • As a user
  • I want to receive from the API the same list of tasks that I see in the application
  • so that I don't have to click on the yellow "OK" button

Suggested approach
In order to achieve this goal I suggest to have, at least as a starting point, an experimental flag set for the today method to also include yellow tasks.

Additional context
We've database.was_modified_today and a warning for this issue, as it's not completely trivial to implement (a couple of edge cases). However, at least for the today overview it would be nice to have.

Things Beta date format change

Hey, just a heads up: In mid-March, the beta made several changes in the db. Several seem to just be renamed columns, but I've been banging my head on the encoding of startDate and thought I'd share what I found in case it's helpful when they roll this out to a release version. In Javascript:

// task with a startDate of Apr 9, 2023 = 132596864
const parseStartDate = (startDate) => {
  const DAYS = 128;
  const MONTHS = 32 * DAYS;
  const YEARS = 16 * MONTHS;

  let year = Math.floor(startDate / YEARS);
  startDate -= year * YEARS;
  let month = Math.floor(startDate / MONTHS);
  startDate -= month * MONTHS;
  let day = Math.floor(startDate / DAYS);

  console.log(year, month, day);
  return new Date(year, month - 1, day);
};

Sorry for the issue, feel free to close it. Just wanted to pass this along, as I've used this codebase to understand several things about the Things database.

Split `make_date_filter`

Goal

  • As a developer
  • I want to have small specialized methods
  • so that it's easier to use the API

Suggested approach
In order to achieve this goal I suggest to split the method make_date_filter into two methods.

Additional context
#80

Consider introducing constants instead of hard coded string

Goal

  • As a user of the API
  • I want to use IDE completions
  • so that I can avoid typos and now which values are valid

Suggested approach
In order to achieve this goal I suggest to use constants, ideally with some form of type hinting. E.g. via an enum.

Additional context
For example instead of todos = things.todos(start="Anytime") it would be todos = things.todos(start=StartType.ANYTIME)

class StartType(str, Enum):
    """Valid values for to-do start."""

    INBOX: str = "Inbox"
    ANYTIME: str = "Anytime"
    SOMEDAY: str = "Someday"

Return tagged items that are assigned to a heading

To Reproduce
Steps to reproduce the behavior:
E.g. things.tasks(tag="Errand", project="3x1QqJqfvZyhtw8NSdnZqG")

Expected behavior
Return a task that is assigned to a heading in that project and tagged with Errand

Additional context
Works fine with tasks not assigned to a heading and fine when not filtering by a tag.

Ask others to use (and extend) this library

api.today does not return new to-todos

To Reproduce
Steps to reproduce the behavior:

  1. Schedule a to-do called A for next day
  2. Wait until 00:00
  3. Call api.today

Expected behavior
The to-do called A should be returned.

Clean up issues list

Goal

  • As a things.py developer
  • I want the open issues list to be free of constipation
  • so that I have an overview of new and important open issues.

Suggested approach
In order to achieve this goal I suggest @AlexanderWillner to close all issues that aren't planned to be actively worked on. They can be reopened later if needed. Maybe they can be given the tag "#opportunity" too, which means "opportunity in the future but not actively worked on".

Repeatable to-dos: include next in `things.upcoming()`.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new to-do which repeats monthly (e.g. repeat every month on the 1st day).
  2. Run things.upcoming().
  3. The next upcoming event is not included; however, it is visible if you go to "Upcoming" in Things.app.

Expected behavior
Include the next upcoming event in things.upcoming().

start_date and deadline are off by one day

To Reproduce
Create a task in Things with a start date of December 7th and a deadline of December 10th.
Find the task in things.upcoming() and compare start_date and deadline with what is shown in Things:

>>> pprint(things.upcoming()[12])
{'created': '2021-11-28 18:07:11',
 'deadline': '2021-12-09',
 'index': -1419,
 'modified': '2021-11-28 18:07:39',
 'notes': '',
 'project': '44Sevodr3k4XbXaczpdoeF',
 'project_title': 'misc. personal tasks',
 'start': 'Someday',
 'start_date': '2021-12-06',
 'status': 'incomplete',
 'stop_date': None,
 'title': 'create example for GitHub issue',
 'today_index': 8376659,
 'type': 'to-do',
 'uuid': '7E6YNbvGhuDXTbSEczebx5'}
>>> 

Note that I'm in California, USA, so currently in Pacific Standard Time (UTC - 8), in case that's relevant.

Expected behavior
I expected start_date and deadline to match what is shown in Things.
Interestingly, the created and modified timestamps are correct.

Additional context
Screen shot of task in Things:
Screen Shot 2021-11-28 at 6 11 07 PM

Consider publishing the API documentation via GitHub Pages

Goal

  • As a user of this library
  • I want to easily look at the documentation
  • so that I'm confident it does what I need

Suggested approach
In order to achieve this goal I suggest to run pdoc via GitHub Actions and publish the results via GitHub Pages.

Additional context
We do already have make doc

Did this break with the latest Things 3 release - Version 3.17.4 (31704515)?

Discussed in https://github.com/thingsapi/things.py/discussions/101

Originally posted by jeredactyl April 12, 2023

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/bin/things-cli", line 33, in <module>
    sys.exit(load_entry_point('things-cli==0.1.4', 'console_scripts', 'things-cli')())
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things_cli/cli.py", line 487, in main
    ThingsCLI().main()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things_cli/cli.py", line 406, in main
    self.main(ThingsCLI.get_parser().parse_args())
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things_cli/cli.py", line 422, in main
    self.parse_command(defaults, args)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things_cli/cli.py", line 479, in parse_command
    self.print_tasks(getattr(api, command)(**defaults))
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things/api.py", line 556, in anytime
    return tasks(start="Anytime", **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things/api.py", line 190, in tasks
    database = pop_database(kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things/api.py", line 719, in pop_database
    database = Database(filepath=filepath, print_sql=print_sql)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/things/database.py", line 164, in __init__
    with open(self.filepath) as file:
NotADirectoryError: [Errno 20] Not a directory: '/Users/xxxxxxx/Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/Things `Database.thingsdatabase/main.sqlite'

Return project/area name and project_uuid/area_uuid

Goal

  • As a KanbanView developer
  • I want to receive both project/area and their uuid
  • so that I can reuse most of the old code when using things.py

Suggested approach
In order to achieve this goal I suggest to return "project": "test project", "project_uuid": "Uaxh5ytCho8CSTNsUCxtAv" instead of "project": "Uaxh5ytCho8CSTNsUCxtAv"

Additional context

Crash when searching for `"`

To Reproduce
Steps to reproduce the behavior:

  1. Execute things.search('"')

Expected behavior
Proper search results.

Additional context
N/A

today() fails if today contains todo with start_date == None

To Reproduce
Steps to reproduce the behavior:

see title.

File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/things/api.py", line 526, in today
result.sort(key=lambda task: (task["today_index"], task["start_date"]))
TypeError: '<' not supported between instances of 'NoneType' and 'str'

Expected behavior

not failing :)

will provide pr for fix.

Cleanup to remove lint

Started by #34, the goal is to let the code pass make lint (and potentially modify the checks where needed).

ERROR: pip3 install things.py

To Reproduce
Steps to reproduce the behavior:

  1. Run pip3 install things.py
  2. See error
ERROR: Could not find a version that satisfies the requirement things.py
ERROR: No matching distribution found for things.py

Expected behavior
things.py is installed.

Recent update broke the whole thing

To Reproduce
Steps to reproduce the behavior:

  1. Update Things3 to latest update (Apr 13, 2013)
  2. Errors occur when trying to load the database

Expected behavior

  1. No errors should be thrown

Additional context
There are several changes

  • First, they have moved the database into /Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/ThingsData-UJNOH/, this could be fixed by setting the os environment for THINGSDB
  • However, it seems like there's schema or column name changes, this error is thrown when loading the DB

cursor.execute(sql_query, parameters) sqlite3.OperationalError: no such column: TASK.dueDate

looking into the SQLite tables, it seems like dueDate is now deadline?

Implement getting `recurring` / `repeating` items

Goal

  • As a user
  • I want to get my scheduled items sorted by due date
  • so that I can see what's recurring

Suggested approach
In order to achieve this goal I suggest to either implement things.recurring or probably even better things.upcoming(recurring=True)

Additional context
n/a

timezone of completed tasks not taken into account

To Reproduce
I was looking for all tasks completed on 2024-02-25:

kwargs['status'] = None
kwargs['stop_date'] = f'{DATE}'
tasks = things.tasks(**kwargs)

...but it returns some tasks on the day before, particularly those that were completed close to midnight:

Here's the stopDate for one of the returned tasks: 1708826542.13277

GMT: Sunday, February 25, 2024 2:02:22.132 AM
Your time zone: Saturday, February 24, 2024 9:02:22.132 PM GMT-05:00

Expected behavior
When I supply a stop_date I expect it to be reflective of my local timezone: i.e., get all tasks completed on that day for me.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.