mirror of https://github.com/Hoffelhas/autodoist
Added 'get_all_data' function to use the SYNC API v9 to obtain all the data in your Todoist. Needed for seeing e.g. completed tasks, which the REST API v2 does not provide.
parent
374d216a83
commit
4a236153d0
109
autodoist.py
109
autodoist.py
|
@ -4,6 +4,8 @@ from todoist_api_python.api import TodoistAPI
|
||||||
from todoist_api_python.models import Task
|
from todoist_api_python.models import Task
|
||||||
from todoist_api_python.models import Section
|
from todoist_api_python.models import Section
|
||||||
from todoist_api_python.models import Project
|
from todoist_api_python.models import Project
|
||||||
|
from todoist_api_python.http_requests import get
|
||||||
|
from urllib.parse import urljoin
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import requests
|
import requests
|
||||||
|
@ -12,7 +14,6 @@ import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import time
|
import time
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from sqlite3 import Error
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Connect to SQLite database
|
# Connect to SQLite database
|
||||||
|
@ -429,6 +430,18 @@ def check_for_update(current_version):
|
||||||
logging.error("Error while checking for updates: {}".format(e))
|
logging.error("Error while checking for updates: {}".format(e))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Get all data through the SYNC API. Needed to see e.g. any completed tasks.
|
||||||
|
|
||||||
|
def get_all_data(self, api):
|
||||||
|
BASE_URL = "https://api.todoist.com"
|
||||||
|
SYNC_VERSION = "v9"
|
||||||
|
SYNC_API = urljoin(BASE_URL, f"/sync/{SYNC_VERSION}/")
|
||||||
|
COMPLETED_GET_ALL = "completed/get_all"
|
||||||
|
endpoint = urljoin(SYNC_API, COMPLETED_GET_ALL)
|
||||||
|
data = get(api._session, endpoint, api._token)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
# Assign current type based on settings
|
# Assign current type based on settings
|
||||||
|
|
||||||
|
|
||||||
|
@ -500,53 +513,55 @@ def get_type(args, connection, model, key):
|
||||||
# Determine a project type
|
# Determine a project type
|
||||||
|
|
||||||
|
|
||||||
def get_project_type(args, connection, project_model):
|
def get_project_type(args, connection, project):
|
||||||
"""Identifies how a project should be handled."""
|
"""Identifies how a project should be handled."""
|
||||||
project_type, project_type_changed = get_type(
|
project_type, project_type_changed = get_type(
|
||||||
args, connection, project_model, 'project_type')
|
args, connection, project, 'project_type')
|
||||||
|
|
||||||
|
if project_type is not None:
|
||||||
|
logging.debug('Identified \'%s\' as %s type',
|
||||||
|
project.name, project_type)
|
||||||
|
|
||||||
return project_type, project_type_changed
|
return project_type, project_type_changed
|
||||||
|
|
||||||
# Determine a section type
|
# Determine a section type
|
||||||
|
|
||||||
|
|
||||||
def get_section_type(args, connection, section_object):
|
def get_section_type(args, connection, section):
|
||||||
"""Identifies how a section should be handled."""
|
"""Identifies how a section should be handled."""
|
||||||
if section_object is not None:
|
if section is not None:
|
||||||
section_type, section_type_changed = get_type(
|
section_type, section_type_changed = get_type(
|
||||||
args, connection, section_object, 'section_type')
|
args, connection, section, 'section_type')
|
||||||
else:
|
else:
|
||||||
section_type = None
|
section_type = None
|
||||||
section_type_changed = 0
|
section_type_changed = 0
|
||||||
|
|
||||||
|
if section_type is not None:
|
||||||
|
logging.debug('Identified \'%s\' as %s type',
|
||||||
|
section.name, section_type)
|
||||||
|
|
||||||
return section_type, section_type_changed
|
return section_type, section_type_changed
|
||||||
|
|
||||||
# Determine an task type
|
# Determine an task type
|
||||||
|
|
||||||
|
|
||||||
def get_task_type(args, connection, task, section_type, project_type):
|
def get_task_type(args, connection, task):
|
||||||
"""Identifies how a task with sub tasks should be handled."""
|
"""Identifies how a task with sub tasks should be handled."""
|
||||||
|
|
||||||
if project_type is None and section_type is None and task.parent_id != 0: #TODO: project type and section type, no?
|
task_type, task_type_changed = get_type(
|
||||||
try:
|
args, connection, task, 'task_type')
|
||||||
task_type = task.parent_type # TODO: METADATA
|
|
||||||
task_type_changed = 1
|
if task_type is not None:
|
||||||
task.task_type = task_type #TODO: METADATA
|
logging.debug('Identified \'%s\' as %s type', task.content, task_type)
|
||||||
except:
|
|
||||||
task_type, task_type_changed = get_type(
|
|
||||||
args, connection, task, 'task_type') # TODO: METADATA
|
|
||||||
else:
|
|
||||||
task_type, task_type_changed = get_type(
|
|
||||||
args, connection, task, 'task_type') # TODO: METADATA
|
|
||||||
|
|
||||||
return task_type, task_type_changed
|
return task_type, task_type_changed
|
||||||
|
|
||||||
# Logic to track addition of a label to a task
|
# Logic to track addition of a label to a task
|
||||||
|
|
||||||
|
|
||||||
def add_label(task, label, overview_task_ids, overview_task_labels):
|
def add_label(connection, task, dominant_type, label, overview_task_ids, overview_task_labels):
|
||||||
if label not in task.labels:
|
if label not in task.labels:
|
||||||
labels = task.labels # Copy other existing labels
|
labels = task.labels # To also copy other existing labels
|
||||||
logging.debug('Updating \'%s\' with label', task.content)
|
logging.debug('Updating \'%s\' with label', task.content)
|
||||||
labels.append(label)
|
labels.append(label)
|
||||||
|
|
||||||
|
@ -556,6 +571,8 @@ def add_label(task, label, overview_task_ids, overview_task_labels):
|
||||||
overview_task_ids[task.id] = 1
|
overview_task_ids[task.id] = 1
|
||||||
overview_task_labels[task.id] = labels
|
overview_task_labels[task.id] = labels
|
||||||
|
|
||||||
|
db_update_value(connection, task, 'task_type', dominant_type)
|
||||||
|
|
||||||
# Logic to track removal of a label from a task
|
# Logic to track removal of a label from a task
|
||||||
|
|
||||||
|
|
||||||
|
@ -828,10 +845,6 @@ def autodoist_magic(args, api, connection):
|
||||||
project_type, project_type_changed = get_project_type(
|
project_type, project_type_changed = get_project_type(
|
||||||
args, connection, project)
|
args, connection, project)
|
||||||
|
|
||||||
if project_type is not None:
|
|
||||||
logging.debug('Identified \'%s\' as %s type',
|
|
||||||
project.name, project_type)
|
|
||||||
|
|
||||||
# Get all tasks for the project
|
# Get all tasks for the project
|
||||||
try:
|
try:
|
||||||
project_tasks = api.get_tasks(project_id=project.id)
|
project_tasks = api.get_tasks(project_id=project.id)
|
||||||
|
@ -875,9 +888,6 @@ def autodoist_magic(args, api, connection):
|
||||||
# Get section type
|
# Get section type
|
||||||
section_type, section_type_changed = get_section_type(
|
section_type, section_type_changed = get_section_type(
|
||||||
args, connection, section)
|
args, connection, section)
|
||||||
if section_type is not None:
|
|
||||||
logging.debug('Identified \'%s\' as %s type',
|
|
||||||
section.name, section_type)
|
|
||||||
|
|
||||||
# Get all tasks for the section
|
# Get all tasks for the section
|
||||||
tasks = [x for x in project_tasks if x.section_id
|
tasks = [x for x in project_tasks if x.section_id
|
||||||
|
@ -894,7 +904,7 @@ def autodoist_magic(args, api, connection):
|
||||||
tasks = sorted(tasks, key=lambda x: (
|
tasks = sorted(tasks, key=lambda x: (
|
||||||
int(x.parent_id), x.order))
|
int(x.parent_id), x.order))
|
||||||
|
|
||||||
# If a type has changed, clean all task labels for good measure
|
# If a type has changed, clean all tasks in this section for good measure
|
||||||
if next_action_label is not None:
|
if next_action_label is not None:
|
||||||
if project_type_changed == 1 or section_type_changed == 1:
|
if project_type_changed == 1 or section_type_changed == 1:
|
||||||
# Remove labels
|
# Remove labels
|
||||||
|
@ -930,7 +940,7 @@ def autodoist_magic(args, api, connection):
|
||||||
# TODO: DISABLED FOR NOW, FIX LATER
|
# TODO: DISABLED FOR NOW, FIX LATER
|
||||||
# modify_headers(header_all_in_p, unheader_all_in_p, header_all_in_s, unheader_all_in_s, header_all_in_t, unheader_all_in_t)
|
# modify_headers(header_all_in_p, unheader_all_in_p, header_all_in_s, unheader_all_in_s, header_all_in_t, unheader_all_in_t)
|
||||||
|
|
||||||
# TODO: Check is regeneration is still needed, now that it's part of core Todoist. Disabled for now.
|
# TODO: Check is regeneration is still needed, now that it's part of core Todoist. Disabled for now.
|
||||||
# Logic for recurring lists
|
# Logic for recurring lists
|
||||||
# if not args.regeneration:
|
# if not args.regeneration:
|
||||||
# try:
|
# try:
|
||||||
|
@ -959,11 +969,7 @@ def autodoist_magic(args, api, connection):
|
||||||
|
|
||||||
# Check task type
|
# Check task type
|
||||||
task_type, task_type_changed = get_task_type(
|
task_type, task_type_changed = get_task_type(
|
||||||
args, connection, task, section_type, project_type)
|
args, connection, task)
|
||||||
|
|
||||||
if task_type is not None:
|
|
||||||
logging.debug('Identified \'%s\' as %s type',
|
|
||||||
task.content, task_type)
|
|
||||||
|
|
||||||
# Determine hierarchy types for logic
|
# Determine hierarchy types for logic
|
||||||
hierarchy_types = [task_type,
|
hierarchy_types = [task_type,
|
||||||
|
@ -977,7 +983,7 @@ def autodoist_magic(args, api, connection):
|
||||||
# Inherit task type
|
# Inherit task type
|
||||||
dominant_type = task_type
|
dominant_type = task_type
|
||||||
add_label(
|
add_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
|
|
||||||
elif hierarchy_boolean[1]:
|
elif hierarchy_boolean[1]:
|
||||||
# Inherit section type
|
# Inherit section type
|
||||||
|
@ -986,11 +992,11 @@ def autodoist_magic(args, api, connection):
|
||||||
if section_type == 'sequential' or section_type == 's-p':
|
if section_type == 'sequential' or section_type == 's-p':
|
||||||
if not first_found_section:
|
if not first_found_section:
|
||||||
add_label(
|
add_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
first_found_section = True
|
first_found_section = True
|
||||||
elif section_type == 'parallel' or section_type == 'p-s':
|
elif section_type == 'parallel' or section_type == 'p-s':
|
||||||
add_label(
|
add_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
|
|
||||||
elif hierarchy_boolean[2]:
|
elif hierarchy_boolean[2]:
|
||||||
# Inherit project type
|
# Inherit project type
|
||||||
|
@ -999,12 +1005,12 @@ def autodoist_magic(args, api, connection):
|
||||||
if project_type == 'sequential' or project_type == 's-p':
|
if project_type == 'sequential' or project_type == 's-p':
|
||||||
if not first_found_project:
|
if not first_found_project:
|
||||||
add_label(
|
add_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
first_found_project = True
|
first_found_project = True
|
||||||
|
|
||||||
elif project_type == 'parallel' or project_type == 'p-s':
|
elif project_type == 'parallel' or project_type == 'p-s':
|
||||||
add_label(
|
add_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
|
|
||||||
# Mark other conditions too
|
# Mark other conditions too
|
||||||
if first_found_section == False and hierarchy_boolean[1]:
|
if first_found_section == False and hierarchy_boolean[1]:
|
||||||
|
@ -1014,16 +1020,18 @@ def autodoist_magic(args, api, connection):
|
||||||
|
|
||||||
# If there are children
|
# If there are children
|
||||||
if len(child_tasks) > 0:
|
if len(child_tasks) > 0:
|
||||||
|
|
||||||
# Check if task state has changed, if so clean children for good measure
|
|
||||||
if task_type_changed == 1:
|
|
||||||
[remove_label(child_task, next_action_label, overview_task_ids, overview_task_labels)
|
|
||||||
for child_task in child_tasks]
|
|
||||||
|
|
||||||
# If a sub-task, inherit parent task type
|
#TODO: is this still needed?
|
||||||
|
# # Check if task state has changed, if so clean children for good measure
|
||||||
|
# if task_type_changed == 1:
|
||||||
|
# [remove_label(child_task, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
|
# for child_task in child_tasks]
|
||||||
|
|
||||||
|
#If a sub-task, inherit parent task type
|
||||||
if task.parent_id != 0:
|
if task.parent_id != 0:
|
||||||
# dominant_type = task.parent_type # TODO: METADATA
|
# dominant_type = task.parent_type # TODO: METADATA
|
||||||
dominant_type = db_read_value(connection, task, 'parent_type')[0][0]
|
dominant_type = db_read_value(
|
||||||
|
connection, task, 'parent_type')[0][0]
|
||||||
|
|
||||||
# Process sequential tagged tasks (task_type can overrule project_type)
|
# Process sequential tagged tasks (task_type can overrule project_type)
|
||||||
if dominant_type == 'sequential' or dominant_type == 'p-s':
|
if dominant_type == 'sequential' or dominant_type == 'p-s':
|
||||||
|
@ -1038,7 +1046,7 @@ def autodoist_magic(args, api, connection):
|
||||||
# Pass label down to the first child
|
# Pass label down to the first child
|
||||||
if not child_task.is_completed and next_action_label in task.labels:
|
if not child_task.is_completed and next_action_label in task.labels:
|
||||||
add_label(
|
add_label(
|
||||||
child_task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, child_task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
remove_label(
|
remove_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
task, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
else:
|
else:
|
||||||
|
@ -1050,7 +1058,7 @@ def autodoist_magic(args, api, connection):
|
||||||
elif dominant_type == 'parallel' or (dominant_type == 's-p' and next_action_label in task.labels):
|
elif dominant_type == 'parallel' or (dominant_type == 's-p' and next_action_label in task.labels):
|
||||||
remove_label(
|
remove_label(
|
||||||
task, next_action_label, overview_task_ids, overview_task_labels)
|
task, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
db_update_value(task, 'task_type', 'NULL')
|
db_update_value(connection, task, 'task_type', None) #TODO: integrate in remove_label funcionality, else a lot of duplicates. #TODO: None not registered, fix bug.
|
||||||
|
|
||||||
for child_task in child_tasks:
|
for child_task in child_tasks:
|
||||||
|
|
||||||
|
@ -1059,11 +1067,12 @@ def autodoist_magic(args, api, connection):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# child_task.parent_type = dominant_type # TODO: METADATA
|
# child_task.parent_type = dominant_type # TODO: METADATA
|
||||||
db_update_value(connection, child_task, 'parent_type', dominant_type)
|
db_update_value(
|
||||||
|
connection, child_task, 'parent_type', dominant_type)
|
||||||
|
|
||||||
if not child_task.is_completed:
|
if not child_task.is_completed:
|
||||||
add_label(
|
add_label(
|
||||||
child_task, next_action_label, overview_task_ids, overview_task_labels)
|
connection, child_task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
|
||||||
|
|
||||||
# Remove labels based on start / due dates
|
# Remove labels based on start / due dates
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue