Core labelling functionality now seems to function. Still need to fix one bug with cleaning children if task_type is set.

pull/30/head
Hoffelhas 2023-01-04 21:12:39 +01:00
parent 4a236153d0
commit 0c64e99455
1 changed files with 128 additions and 122 deletions

View File

@ -45,10 +45,15 @@ def close_connection(connection):
# Execute any SQLite query passed to it in the form of string # Execute any SQLite query passed to it in the form of string
def execute_query(connection, query): def execute_query(connection, query, *args):
cursor = connection.cursor() cursor = connection.cursor()
try: try:
value = args[0]
cursor.execute(query,(value,)) # Useful to pass None/NULL value correctly
except:
cursor.execute(query) cursor.execute(query)
try:
connection.commit() connection.commit()
logging.debug("Query executed: {}".format(query)) logging.debug("Query executed: {}".format(query))
except Exception as e: except Exception as e:
@ -111,22 +116,15 @@ def db_update_value(connection, model, column, value):
db_name = 'projects' db_name = 'projects'
goal = 'project_id' goal = 'project_id'
query = """ query = """UPDATE %s SET %s = ? WHERE %s = %r""" % (db_name, column, goal, model.id)
UPDATE
%s
SET
%s = %r
WHERE
%s = %r
""" % (db_name, column, value, goal, model.id)
result = execute_query(connection, query) result = execute_query(connection, query, value)
return result
except Exception as e: except Exception as e:
logging.debug(f"The error '{e}' occurred") logging.debug(f"The error '{e}' occurred")
return result
# Check if the id of a model exists, if not, add to database # Check if the id of a model exists, if not, add to database
@ -159,10 +157,10 @@ def db_check_existance(connection, model):
if isinstance(model, Section): if isinstance(model, Section):
q_create = """ q_create = """
INSERT INTO INSERT INTO
sections (section_id, project_type, section_type) sections (section_id, section_type)
VALUES VALUES
(%r, %s, %s); (%r, %s);
""" % (model.id, 'NULL', 'NULL') """ % (model.id, 'NULL')
if isinstance(model, Project): if isinstance(model, Project):
q_create = """ q_create = """
@ -198,8 +196,7 @@ def initialise_sqlite():
q_create_sections_table = """ q_create_sections_table = """
CREATE TABLE IF NOT EXISTS sections ( CREATE TABLE IF NOT EXISTS sections (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
sections_id INTEGER, section_id INTEGER,
project_type TEXT,
section_type section_type
); );
""" """
@ -571,8 +568,6 @@ def add_label(connection, task, dominant_type, label, overview_task_ids, overvie
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
@ -851,6 +846,14 @@ def autodoist_magic(args, api, connection):
except Exception as error: except Exception as error:
print(error) print(error)
# If a project type has changed, clean all tasks in this project for good measure
if next_action_label is not None:
if project_type_changed == 1:
for task in project_tasks:
remove_label(task, next_action_label, overview_task_ids, overview_task_labels)
db_update_value(connection, task, 'task_type', None)
db_update_value(connection, task, 'parent_type', None)
# Run for both non-sectioned and sectioned tasks # Run for both non-sectioned and sectioned tasks
# Get completed tasks: # Get completed tasks:
@ -890,46 +893,39 @@ def autodoist_magic(args, api, connection):
args, connection, section) args, connection, section)
# Get all tasks for the section # Get all tasks for the section
tasks = [x for x in project_tasks if x.section_id section_tasks = [x for x in project_tasks if x.section_id
== section.id] == section.id]
# Change top tasks parents_id from 'None' to '0' in order to numerically sort later on # Change top tasks parents_id from 'None' to '0' in order to numerically sort later on
for task in tasks: for task in section_tasks:
if not task.parent_id: if not task.parent_id:
task.parent_id = 0 task.parent_id = 0
# Sort by parent_id and child order # Sort by parent_id and child order
# In the past, Todoist used to screw up the tasks orders, so originally I processed parentless tasks first such that children could properly inherit porperties. # In the past, Todoist used to screw up the tasks orders, so originally I processed parentless tasks first such that children could properly inherit porperties.
# With the new API this seems to be in order, but I'm keeping this just in case for now. TODO: Could be used for optimization in the future. # With the new API this seems to be in order, but I'm keeping this just in case for now. TODO: Could be used for optimization in the future.
tasks = sorted(tasks, key=lambda x: ( section_tasks = sorted(section_tasks, key=lambda x: (
int(x.parent_id), x.order)) int(x.parent_id), x.order))
# If a type has changed, clean all tasks in this section 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 section_type_changed == 1:
# Remove labels for task in section_tasks:
[remove_label(task, next_action_label, overview_task_ids, remove_label(task, next_action_label, overview_task_ids, overview_task_labels)
overview_task_labels) for task in tasks] db_update_value(connection, task, 'task_type', None)
# Remove parent types db_update_value(connection, task, 'parent_type', None)
# for task in tasks:
# task.parent_type = None #TODO: METADATA
# For all tasks in this section # For all tasks in this section
for task in tasks: for task in section_tasks:
dominant_type = None # Reset dominant_type = None # Reset
db_check_existance(connection, task) db_check_existance(connection, task)
# Possible nottes routine for the future
# notes = api.notes.all() TODO: Quick notes test to see what the impact is?
# note_content = [x['content'] for x in notes if x['item_id'] == item['id']]
# print(note_content)
# Determine which child_tasks exist, both all and the ones that have not been checked yet # Determine which child_tasks exist, both all and the ones that have not been checked yet
non_completed_tasks = list( non_completed_tasks = list(
filter(lambda x: not x.is_completed, tasks)) filter(lambda x: not x.is_completed, section_tasks))
child_tasks_all = list( child_tasks_all = list(
filter(lambda x: x.parent_id == task.id, tasks)) filter(lambda x: x.parent_id == task.id, section_tasks))
child_tasks = list( child_tasks = list(
filter(lambda x: x.parent_id == task.id, non_completed_tasks)) filter(lambda x: x.parent_id == task.id, non_completed_tasks))
@ -971,13 +967,21 @@ def autodoist_magic(args, api, connection):
task_type, task_type_changed = get_task_type( task_type, task_type_changed = get_task_type(
args, connection, task) args, connection, task)
# If task type has changed, clean all of its children for good measure
if next_action_label is not None:
if task_type_changed == 1:
for child_task in child_tasks:
remove_label(child_task, next_action_label, overview_task_ids, overview_task_labels)
db_update_value(connection, child_task, 'task_type', None)
db_update_value(connection, child_task, 'parent_type', None)
# Determine hierarchy types for logic # Determine hierarchy types for logic
hierarchy_types = [task_type, hierarchy_types = [task_type,
section_type, project_type] section_type, project_type]
hierarchy_boolean = [type(x) != type(None) hierarchy_boolean = [type(x) != type(None)
for x in hierarchy_types] for x in hierarchy_types]
# If it is a parentless task # If it is a parentless task, set task type based on hierarchy
if task.parent_id == 0: if task.parent_id == 0:
if hierarchy_boolean[0]: if hierarchy_boolean[0]:
# Inherit task type # Inherit task type
@ -1011,6 +1015,9 @@ def autodoist_magic(args, api, connection):
elif project_type == 'parallel' or project_type == 'p-s': elif project_type == 'parallel' or project_type == 'p-s':
add_label( add_label(
connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels) connection, task, dominant_type, next_action_label, overview_task_ids, overview_task_labels)
else:
# Parentless task has no type, so skip any children.
continue
# 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]:
@ -1018,17 +1025,16 @@ def autodoist_magic(args, api, connection):
if first_found_project is False and hierarchy_boolean[2]: if first_found_project is False and hierarchy_boolean[2]:
first_found_project = True first_found_project = True
# If there are children # If a parentless or sub-task which has children
if len(child_tasks) > 0: if len(child_tasks) > 0:
#TODO: is this still needed?
# # Check if task state has changed, if so clean children for good measure # # Check if task state has changed, if so clean children for good measure
# if task_type_changed == 1: # if task_type_changed == 1:
# [remove_label(child_task, next_action_label, overview_task_ids, overview_task_labels) # [remove_label(child_task, next_action_label, overview_task_ids, overview_task_labels)
# for child_task in child_tasks] # for child_task in child_tasks]
#If a sub-task, inherit parent task type #If it is a sub-task with no own type, inherit the parent task type instead
if task.parent_id != 0: if task.parent_id != 0 and task_type == None:
# dominant_type = task.parent_type # TODO: METADATA # dominant_type = task.parent_type # TODO: METADATA
dominant_type = db_read_value( dominant_type = db_read_value(
connection, task, 'parent_type')[0][0] connection, task, 'parent_type')[0][0]
@ -1058,7 +1064,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(connection, task, 'task_type', None) #TODO: integrate in remove_label funcionality, else a lot of duplicates. #TODO: None not registered, fix bug. # db_update_value(connection, task, 'task_type', None) #TODO: integrate in remove_label funcionality, else a lot of duplicates.
for child_task in child_tasks: for child_task in child_tasks:
@ -1077,94 +1083,94 @@ def autodoist_magic(args, api, connection):
# Remove labels based on start / due dates # Remove labels based on start / due dates
# If task is too far in the future, remove the next_action tag and skip #TODO: FIX THIS # If task is too far in the future, remove the next_action tag and skip #TODO: FIX THIS
try: # try:
if args.hide_future > 0 and 'due' in task.data and task.due is not None: # if args.hide_future > 0 and 'due' in task.data and task.due is not None:
due_date = datetime.strptime( # due_date = datetime.strptime(
task.due['date'][:10], "%Y-%m-%d") # task.due['date'][:10], "%Y-%m-%d")
future_diff = ( # future_diff = (
due_date - datetime.today()).days # due_date - datetime.today()).days
if future_diff >= args.hide_future: # if future_diff >= args.hide_future:
remove_label( # remove_label(
task, next_action_label, overview_task_ids, overview_task_labels) # task, next_action_label, overview_task_ids, overview_task_labels)
continue # continue
except: # except:
# Hide-future not set, skip # # Hide-future not set, skip
continue # continue
# If start-date has not passed yet, remove label # If start-date has not passed yet, remove label #TODO: FIX THIS
try: # try:
f1 = task.content.find('start=') # f1 = task.content.find('start=')
f2 = task.content.find('start=due-') # f2 = task.content.find('start=due-')
if f1 > -1 and f2 == -1: # if f1 > -1 and f2 == -1:
f_end = task.content[f1+6:].find(' ') # f_end = task.content[f1+6:].find(' ')
if f_end > -1: # if f_end > -1:
start_date = task.content[f1 + # start_date = task.content[f1 +
6:f1+6+f_end] # 6:f1+6+f_end]
else: # else:
start_date = task.content[f1+6:] # start_date = task.content[f1+6:]
# If start-date hasen't passed, remove all labels # # If start-date hasen't passed, remove all labels
start_date = datetime.strptime( # start_date = datetime.strptime(
start_date, args.dateformat) # start_date, args.dateformat)
future_diff = ( # future_diff = (
datetime.today()-start_date).days # datetime.today()-start_date).days
if future_diff < 0: # if future_diff < 0:
remove_label( # remove_label(
task, next_action_label, overview_task_ids, overview_task_labels) # task, next_action_label, overview_task_ids, overview_task_labels)
[remove_label(child_task, next_action_label, overview_task_ids, # [remove_label(child_task, next_action_label, overview_task_ids,
overview_task_labels) for child_task in child_tasks] # overview_task_labels) for child_task in child_tasks]
continue # continue
except: # except:
logging.warning( # logging.warning(
'Wrong start-date format for task: "%s". Please use "start=<DD-MM-YYYY>"', task.content) # 'Wrong start-date format for task: "%s". Please use "start=<DD-MM-YYYY>"', task.content)
continue # continue
# Recurring task friendly - remove label with relative change from due date #TODO Fix this logic # Recurring task friendly - remove label with relative change from due date #TODO FIX THIS
try: # try:
f = task.content.find('start=due-') # f = task.content.find('start=due-')
if f > -1: # if f > -1:
f1a = task.content.find( # f1a = task.content.find(
'd') # Find 'd' from 'due' # 'd') # Find 'd' from 'due'
f1b = task.content.rfind( # f1b = task.content.rfind(
'd') # Find 'd' from days # 'd') # Find 'd' from days
f2 = task.content.find('w') # f2 = task.content.find('w')
f_end = task.content[f+10:].find(' ') # f_end = task.content[f+10:].find(' ')
if f_end > -1: # if f_end > -1:
offset = task.content[f+10:f+10+f_end-1] # offset = task.content[f+10:f+10+f_end-1]
else: # else:
offset = task.content[f+10:-1] # offset = task.content[f+10:-1]
try: # try:
task_due_date = task.due['date'][:10] # task_due_date = task.due['date'][:10]
task_due_date = datetime.strptime( # task_due_date = datetime.strptime(
task_due_date, '%Y-%m-%d') # task_due_date, '%Y-%m-%d')
except: # except:
logging.warning( # logging.warning(
'No due date to determine start date for task: "%s".', task.content) # 'No due date to determine start date for task: "%s".', task.content)
continue # continue
if f1a != f1b and f1b > -1: # To make sure it doesn't trigger if 'w' is chosen # if f1a != f1b and f1b > -1: # To make sure it doesn't trigger if 'w' is chosen
td = timedelta(days=int(offset)) # td = timedelta(days=int(offset))
elif f2 > -1: # elif f2 > -1:
td = timedelta(weeks=int(offset)) # td = timedelta(weeks=int(offset))
# If we're not in the offset from the due date yet, remove all labels # # If we're not in the offset from the due date yet, remove all labels
start_date = task_due_date - td # start_date = task_due_date - td
future_diff = ( # future_diff = (
datetime.today()-start_date).days # datetime.today()-start_date).days
if future_diff < 0: # if future_diff < 0:
remove_label( # remove_label(
task, next_action_label, overview_task_ids, overview_task_labels) # task, next_action_label, overview_task_ids, overview_task_labels)
[remove_label(child_task, next_action_label, overview_task_ids, # [remove_label(child_task, next_action_label, overview_task_ids,
overview_task_labels) for child_task in child_tasks] # overview_task_labels) for child_task in child_tasks]
continue # continue
except: # except:
logging.warning( # logging.warning(
'Wrong start-date format for task: %s. Please use "start=due-<NUM><d or w>"', task.content) # 'Wrong start-date format for task: %s. Please use "start=due-<NUM><d or w>"', task.content)
continue # continue
# Return all ids and corresponding labels that need to be modified # Return all ids and corresponding labels that need to be modified
return overview_task_ids, overview_task_labels return overview_task_ids, overview_task_labels