Created
April 21, 2020 15:31
-
-
Save lfcipriani/be87cc2c066ff34e9c69c6232cdd1fec to your computer and use it in GitHub Desktop.
Todoist Standup update generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# | |
# This script generates a basic standup update out of completed and due tasks in Todoist. | |
# It considers business days only and holidays (if configured) | |
# | |
# Setup: | |
# | |
# Use python >= 3 | |
# | |
# pip install todoist-python | |
# pip install workdays | |
# | |
# Check config below | |
# | |
# Usage: | |
# | |
# python todoist_standup.py [YYYY-MM-DD] | |
# - defaults to today's date if you omit the date | |
# | |
import sys | |
import datetime | |
import json | |
import requests | |
import todoist | |
import workdays | |
# -------------- Config -------------- | |
# Todoist token, change the path to a file with the todoist API token as content | |
token = open("/path/to/secret.txt", "r").read().strip() | |
# Holidays to take into account. Use YYYY-MM-DD format. | |
holidays = [ | |
'2020-01-01', | |
'2020-05-01', | |
'2020-05-21', | |
'2020-06-01', | |
'2020-10-03', | |
'2020-12-25', | |
'2020-12-26' | |
] | |
# Include only the following project IDs, use empty array to include all projects | |
projects = [ | |
1111111, # project 1 | |
2222222 # project 2 | |
] | |
# Ignore unlabeled tasks? | |
ignore_unlabeled_tasks = False | |
# Ignore tasks with following labels IDs, use empty array to not ignore any label | |
labels_to_ignore = [ | |
111111, # label 1 | |
222222, # label 2 | |
333333 # label 3 | |
] | |
# Ignore tasks with the following titles, matches the exact string. Use empty array to not ignore any task | |
task_titles_to_ignore = [ | |
'Daily Dev Standup', | |
'Routine Task' | |
] | |
# Feel free to change the standup output | |
# 'yesterday' and 'today' params are array of strings | |
def print_standup(yesterday,today): | |
print("My standup update:") | |
print("") | |
print("*What I did in the previous work day?*") | |
for item in yesterday: | |
print(f' - {item}') | |
print("") | |
print("*What I will do today?*") | |
for item in today: | |
print(f' - {item}') | |
# -------------- End of Config -------------- | |
def main(day): | |
yesterday = what_you_did_yesterday(day) | |
today = what_you_will_do_today(day) | |
print_standup(yesterday, today) | |
def get_date(str_date): | |
return datetime.datetime.strptime(str_date, '%Y-%m-%d') | |
def init_holidays(days): | |
return map(lambda h: get_date(h), days) | |
def last_business_day_interval(day): | |
business_day = workdays.workday(day, -1, init_holidays(holidays)) | |
return business_day.strftime("%Y-%m-%dT%H:%M"), (business_day + datetime.timedelta(days=1)).strftime("%Y-%m-%dT%H:%M") | |
def ignore_labels(labels): | |
ignore = False | |
if ignore_unlabeled_tasks and len(labels) == 0: | |
return True | |
for label in labels: | |
if label in labels_to_ignore: | |
ignore = True | |
break | |
return ignore | |
def what_you_did_yesterday(day): | |
api = todoist.TodoistAPI(token) | |
items = [] | |
since, util = last_business_day_interval(day) | |
response = api.completed.get_all(limit=200,since=since,until=util) | |
if 'items' in response: | |
for item in response['items']: | |
if item['project_id'] not in projects: | |
continue | |
rich_item = api.items.get(item['task_id']) | |
if not rich_item: | |
continue | |
rich_item = rich_item['item'] | |
if ignore_labels(rich_item['labels']): | |
continue | |
if rich_item['content'] in task_titles_to_ignore: | |
continue | |
items.append(rich_item['content']) | |
return items | |
def what_you_will_do_today(day): | |
# Initializing items with completed tasks of today | |
items = what_you_did_yesterday(day + datetime.timedelta(days=1)) | |
response = requests.get( | |
"https://api.todoist.com/rest/v1/tasks", | |
params={"filter": day.strftime('%Y-%m-%d')}, | |
headers={"Authorization": "Bearer %s" % token} | |
).json() | |
for item in response: | |
if item['project_id'] not in projects: | |
continue | |
if ignore_labels(item['label_ids']): | |
continue | |
if item['content'] in task_titles_to_ignore: | |
continue | |
items.append(item['content']) | |
return items | |
if __name__ == '__main__': | |
day = None | |
day_arg = sys.argv[1:] | |
if len(day_arg) != 1: | |
day = datetime.datetime.today().strftime('%Y-%m-%d') | |
else: | |
day = day_arg[0] | |
day = get_date(day) | |
main(day) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment