Advertisement
Guest User

Todoist overdue + beeminder

a guest
Apr 10th, 2020
22
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. """
  2. Counts overdue tasks on Todoist.
  3. Works for tasks that have only a "due date".
  4. Get an API token from todoist and set your environment variable
  5. TODOIST_API_TOKEN to it. Also set the variable BEEMINDER_AUTH_TOKEN.
  6. Set up a cron job for this script and run it at night.
  7. (E.g. every day at 4am.)
  8. """
  9.  
  10. import http
  11. import json
  12. import os
  13. import time
  14. from datetime import datetime
  15. from datetime import timedelta
  16.  
  17. import pytz
  18. from dateutil import parser
  19. from todoist.api import TodoistAPI
  20.  
  21. BEEMINDER_GOAL_NAME = 'todoist-overdue'
  22.  
  23.  
  24. def count_overdue():
  25.     api_token = os.getenv('TODOIST_API_TOKEN', None)
  26.     if api_token is None:
  27.         raise ValueError('Please set environment variable '
  28.                          'TODOIST_API_TOKEN')
  29.  
  30.     api = TodoistAPI(token=api_token)
  31.     api.sync()
  32.  
  33.     user_tz = get_todoist_user_timezone(api)
  34.  
  35.     overdue_counter = 0
  36.  
  37.     for item in api['items']:
  38.         if 'due' in item and item['due'] is not None:
  39.             due_datetime = parser.parse(item['due']['date'])
  40.             due_datetime = user_tz.localize(due_datetime)
  41.             now = datetime.now(tz=user_tz)
  42.             # We define "overdue" as: it is at least 24 hours past
  43.             # the due datetime. E.g. if the due date is given as
  44.             # 2020-04-10 then it becomes overdue at 2020-04-11 00:00
  45.             # It would be more accurate to
  46.             # check whether the due date is provided as a date or
  47.             # a date+time and then compare individually, but for
  48.             # my usage, this is good enough.
  49.             overdue = now >= (due_datetime + timedelta(hours=24))
  50.  
  51.             # Count "unchecked" (i.e. uncompleted) overdue tasks
  52.             if 'checked' in item and not item['checked'] and overdue:
  53.                 overdue_counter += 1
  54.                 print(due_datetime)
  55.                 print(item['content'])
  56.                 print('---------')
  57.  
  58.     return overdue_counter
  59.  
  60.  
  61. def get_todoist_user_timezone(api: TodoistAPI) -> pytz.timezone:
  62.     tz_str = api.user.get('tz_info')['timezone']
  63.     return pytz.timezone(tz_str)
  64.  
  65.  
  66. def enter_beeminder_value(value, goal_name, timestamp=None, request_id=None,
  67.                           daystamp=None):
  68.     auth_token = os.getenv('BEEMINDER_AUTH_TOKEN', None)
  69.     if auth_token is None:
  70.         raise ValueError('Please set environment variable '
  71.                          'BEEMINDER_AUTH_TOKEN')
  72.  
  73.     username = os.getenv('BEEMINDER_USERNAME', None)
  74.     if username is None:
  75.         raise ValueError('Please set environment variable '
  76.                          'BEEMINDER_USERNAME')
  77.  
  78.     if daystamp is not None:
  79.         time_or_date_stamp = f'daystamp={daystamp}'
  80.     elif timestamp is not None:
  81.         time_or_date_stamp = f'timestamp={timestamp}'
  82.     else: # timestamp is None and daystamp is None:
  83.         time_or_date_stamp = f'timestamp={int(time.time())}'
  84.  
  85.     comment = 'python'
  86.     query_str = (
  87.         f'/api/v1/users/{username}/'
  88.         f'goals/{goal_name}/'
  89.         f'datapoints.json?'
  90.         f'auth_token={auth_token}&'
  91.         f'{time_or_date_stamp}&'
  92.         f'value={value}&'
  93.         f'comment={comment}')
  94.  
  95.     if request_id is not None:
  96.         query_str += f'&requestid={request_id}'
  97.  
  98.     connection = http.client.HTTPSConnection('www.beeminder.com')
  99.     connection.request('POST', query_str)
  100.     reply = json.loads(connection.getresponse().read())
  101.     return reply
  102.  
  103.  
  104. if __name__ == '__main__':
  105.     n_overdue = count_overdue()
  106.     print(f'n_overdue: {n_overdue}')
  107.     reply = enter_beeminder_value(n_overdue,
  108.                                   goal_name=BEEMINDER_GOAL_NAME)
  109.     print(reply)
Advertisement
RAW Paste Data Copied
Advertisement