class TodoistActionAPI(RequiredConfig): """An encapsulation of the Todoist API. It wraps the lower level methods with methods appropriate for this task.""" required_config = Namespace() required_config.add_option( name="api_key", default='', doc="Todoist API Key", ) required_config.add_option( name="delay", default=5, doc="Specify the delay in seconds between syncs" ) required_config.add_option( name="debug", default=False, doc="Enable debugging" ) required_config.add_option( name="one_time", default=False, doc="Update Todoist once and exit" ) def __init__(self, config, logging): super(TodoistActionAPI, self).__init__() self.config = config self.api = TodoistAPI(token=config.api_key) self.api.sync() self.logging = logging
def main(): API_TOKEN = get_token() if not API_TOKEN: logging.warn('Please set the API token in environment variable.') exit() api = TodoistAPI(API_TOKEN) api.sync() return api
def setup(self): self.parse_args() # Run the initial sync logging.debug('Connecting to the Todoist API') self.api = TodoistAPI(token=self.args.api_key) logging.debug('Syncing the current state from the API') self.api.sync() self.next_label_id = self.check_label(self.args.label) self.active_label_id = self.check_label(self.args.active) self.waitfor_label_id = self.check_label(self.args.waitfor)
def __init__(self, token=None, **kwargs): super(ServiceTodoist, self).__init__(token, **kwargs) self.AUTH_URL = 'https://todoist.com/oauth/authorize' self.ACC_TOKEN = 'https://todoist.com/oauth/access_token' self.REQ_TOKEN = 'https://todoist.com/oauth/access_token' self.consumer_key = settings.TH_TODOIST['client_id'] self.consumer_secret = settings.TH_TODOIST['client_secret'] self.scope = 'task:add,data:read,data:read_write' self.service = 'ServiceTodoist' self.oauth = 'oauth2' if token: self.token = token self.todoist = TodoistAPI(token)
#!/usr/local/bin/python # TODOIST from datetime import date, timedelta, datetime from dateutil import parser from todoist.api import TodoistAPI import creds # PROJECTS / IDs todoist_api = TodoistAPI() # GET ALL TASKS WITH DUE DATE def todoist_list(date): # Sync (load) data todoist_api.sync() tasks = [] for item in todoist_api.state['items']: due = item['due_date_utc'] if due: # Slicing :10 gives us the relevant parts if due[:10] == date: tasks.append(item) # Sort by priority
def __init__(self): self.api_key = os.environ.get("API_KEY") self.api_call = TodoistAPI(self.api_key) self.api_call.sync()
def find_project_id(self, project_name): api_call = TodoistAPI(self.api_key) api_call.sync() for project in range(len(api_call.state['projects'])): if api_call.state['projects'][project].data['name'].lower() == project_name.lower(): return api_call.state['projects'][project].data['id']
class ToDoist: __metaclass__ = Singleton def __init__(self): self.api = TodoistAPI(Config.TODOIST_API_KEY) self.api.sync() self.mt = MusicTools() def find_project_id_by_name(self,project_name): projects = self.api.state['projects'] for project in projects: if project['name'] == project_name: return project['id'] def add_item(self, item, project_id=None, project_name = None): if (project_id is None) and (project_name is None): raise ValueError('Either project_id or project_name should be provided') if project_id is None: project_id = self.find_project_id_by_name(project_name) self.api.items.add(item,project_id) def walk_items_in_project(self, function_to_call, project=Config.TODOIST_PROJECT_NAME, commit=True, mark_read = True): items = self.api.state['items'] logger.info("Scanning and Downloading ToDoist List:{}".format(project)) for item in items: logger.debug(u"Scanning {}|{}".format(item['content'], item['id'])) if item and ('checked' in item.data.keys()): if ((item['project_id'] == self.find_project_id_by_name(project)) & (item['checked']==0)): #For Debug & (item['id']==112286022) function_to_call(item, mark_read) if commit: self.api.commit() def download_song(self,item, mark_read): result = 1 song = self.get_song(item['content']) if song: logger.info(u"Scanned Results: {}|{}".format(song.full_title, song.video_link)) result = self.mt.download_music(song.video_link, song.full_title, is_quiet=True) # result = 1 logger.debug("Download Result: {} | 1 is a failure, 0 is a success".format(result)) #1 is a failure, 0 is a success if result == 0: logger.info("Download Success. Marking item as completed...") if mark_read: item.complete() self.mt.fix_id3(song) else: logger.error(u"Downloading of {} failed. Moving to the next item...".format(song.full_title)) def get_song(self,item): #Checking Youtube Links youtube=Youtube() link = youtube.extract_youtube_link_from_text(item) if link: full_title = youtube.get_title_from_youtube_code(get_youtube_code_from_link(link)) song = Song(full_title= full_title, video_link = link, ) else: soundcloud = Soundcloud() link = soundcloud.extract_soundcloud_link_from_text(item) if link: song= Song(full_title=soundcloud.get_title_soundcloud(link), video_link = link) else: #Items without a link songs = youtube.youtube_search(item,1) if len(songs) >0 : song = songs[0] else: return None return song
default='', help='the actual text for the task') inputArgs = parser.parse_args() # Load ~/.cmd_tricks.json config file config = None with open(os.path.join(Path.home(), ".cmd_tricks.json")) as f: config = json.load(f) # Exit if no config is found if config is None: print("Could not load config file (at ~/.cmd_tricks.json)") sys.exit(1) # Read Todoist API key form .cmd_tricks.json api = TodoistAPI(config["todoist"]["api_key"]) # get contents from clipboard cbContents = None if inputArgs.clipboard: cbContents = clipboard.paste() ############################ # Add GUI functions ############################ class UserInterface: def __init__(self, todoistApi, taskName, taskNote): # super().__init__() self.todoistApi = todoistApi
def create_task(key=tovakey): api = TodoistAPI(key) api.sync() payload = request.get_json() task1_to_add = payload["context"]["facts"]["task1_to_add"]["grammar_entry"] no_of_tasks = 1 if "due_date" in payload['context']['facts'].keys(): due_date = payload["context"]["facts"]["due_date"]["grammar_entry"] else: due_date = None if "task2_to_add" in payload['context']['facts'].keys(): task2_to_add = payload["context"]["facts"]["task2_to_add"][ "grammar_entry"] no_of_tasks = 2 if "task3_to_add" in payload['context']['facts'].keys(): task3_to_add = payload["context"]["facts"]["task3_to_add"][ "grammar_entry"] no_of_tasks = 3 if "project_to_add" in payload['context']['facts'].keys(): target_project = payload["context"]["facts"]["project_to_add"][ "grammar_entry"] projects = extract_projects(api) project_found = False for project in projects: if project[0].lower() == target_project.lower(): project_found = True if no_of_tasks == 1: added_task1 = api.items.add(task1_to_add, project_id=project[1], due={"string": due_date}) elif no_of_tasks == 2: added_task1 = api.items.add(task1_to_add, project_id=project[1], due={"string": due_date}) added_task2 = api.items.add(task2_to_add, project_id=project[1], due={"string": due_date}) elif no_of_tasks == 3: added_task1 = api.items.add(task1_to_add, project_id=project[1], due={"string": due_date}) added_task2 = api.items.add(task2_to_add, project_id=project[1], due={"string": due_date}) added_task3 = api.items.add(task3_to_add, project_id=project[1], due={"string": due_date}) if project_found == False: added_project = api.projects.add(target_project, due={"string": due_date}) if no_of_tasks == 1: added_task1 = api.items.add(task1_to_add, project_id=added_project['id'], due={"string": due_date}) elif no_of_tasks == 2: added_task1 = api.items.add(task1_to_add, project_id=added_project['id'], due={"string": due_date}) added_task2 = api.items.add(task2_to_add, project_id=added_project['id'], due={"string": due_date}) elif no_of_tasks == 3: added_task1 = api.items.add(task1_to_add, project_id=added_project['id'], due={"string": due_date}) added_task2 = api.items.add(task2_to_add, project_id=added_project['id'], due={"string": due_date}) added_task3 = api.items.add(task3_to_add, project_id=added_project['id'], due={"string": due_date}) else: if no_of_tasks == 1: added_task1 = api.items.add(task1_to_add, due={"string": due_date}) elif no_of_tasks == 2: added_task1 = api.items.add(task1_to_add, due={"string": due_date}) added_task2 = api.items.add(task2_to_add, due={"string": due_date}) elif no_of_tasks == 3: added_task1 = api.items.add(task1_to_add, due={"string": due_date}) added_task2 = api.items.add(task2_to_add, due={"string": due_date}) added_task3 = api.items.add(task3_to_add, due={"string": due_date}) api.commit() return action_success_response()
def __init__(self, api_token, api_url): # initiate a todoist api instance self.api = TodoistAPI(api_token) self.api_token = api_token self.api_url = api_url
class APIHandler: def __init__(self, api_token, api_url): # initiate a todoist api instance self.api = TodoistAPI(api_token) self.api_token = api_token self.api_url = api_url def get_project_list(self): self.api.sync() project_list = self.api.state['projects'] return project_list def get_tasks_by_project(self, project_id): tasks_list = requests.get("%s/tasks" % self.api_url, params={ "project_id": project_id }, headers={ "Authorization": "Bearer %s" % self.api_token }).json() return tasks_list def create_project(self, project_name): self.api.projects.add(project_name) self.api.commit() return True def get_all_tasks(self): tasks_list = requests.get("%s/tasks" % self.api_url, headers={ "Authorization": "Bearer %s" % self.api_token }).json() return tasks_list def get_today_tasks(self): all_tasks = self.get_all_tasks() today_tasks = [] today = datetime.today().date() for task in all_tasks: task_due = task.get('due') if task_due: task_due_date_string = task_due.get('date') task_due_date = datetime.strptime(task_due_date_string, '%Y-%m-%d').date() if task_due_date == today: today_tasks.append(task) return today_tasks def create_task(self, task_content): result = requests.post("%s/tasks" % self.api_url, data=json.dumps({ "content": task_content, }), headers={ "Content-Type": "application/json", "X-Request-Id": str(uuid.uuid4()), "Authorization": "Bearer %s" % self.api_token }).json() return result
def __init__(self, config, logging): super(TodoistActionAPI, self).__init__() self.config = config self.api = TodoistAPI(token=config.api_key) self.api.sync() self.logging = logging
class NextAction(object): def __init__(self): self.args = None self.api = None self.next_label_id = None self.waitfor_label_id = None self.active_label_id = None def main(self): self.setup() self.loop() def check_label(self, label): # Check if the label exists labels = self.api.labels.all(lambda x: x['name'] == label) if len(labels) > 0: label_id = labels[0]['id'] logging.debug('Label %s found as label id %d', label, label_id) return label_id else: logging.error( "Label %s doesn't exist.", label) sys.exit(1) def setup(self): self.parse_args() # Run the initial sync logging.debug('Connecting to the Todoist API') self.api = TodoistAPI(token=self.args.api_key) logging.debug('Syncing the current state from the API') self.api.sync() self.next_label_id = self.check_label(self.args.label) self.active_label_id = self.check_label(self.args.active) self.waitfor_label_id = self.check_label(self.args.waitfor) def loop(self): """ Main loop """ while True: try: self.api.sync() except Exception as exc: logging.exception('Error trying to sync with Todoist API: %s', exc) else: self.process(self.api.projects.all()) logging.debug( '%d changes queued for sync... committing if needed', len(self.api.queue)) if len(self.api.queue): self.api.commit() if self.args.onetime: break logging.debug('Sleeping for %d seconds', self.args.delay) time.sleep(self.args.delay) def process(self, projects, parent_indent=0, parent_type=None): """ Process all projects """ current_type = parent_type while projects and parent_indent < projects[0]["indent"]: # dig deeper if projects[0]["indent"] > parent_indent + 1: self.process(projects, parent_indent + 1, current_type) continue project = projects.pop(0) current_type = self.get_project_type(project, parent_type) if not current_type: # project not marked - not touching continue logging.debug('Project %s being processed as %s', project['name'], current_type) def item_filter(x): return x['project_id'] == project['id'] all_items = self.api.items.all(item_filter) items = sorted(all_items, key=lambda x: x['item_order']) item_objs = [] while items: item_objs.append(Item(items)) self.process_items(item_objs, current_type) self.activate(item_objs) def process_items(self, items, parent_type, not_in_first=False): """ Process all tasks in project """ # get the first item for serial first = None if parent_type == "serial": not_checked = [item for item in items if not item.checked] if not_checked and not self.is_waitfor(not_checked[0]): first = not_checked[0] # process items parent_active = False for item in items: current_type = self.get_item_type(item) if current_type: logging.debug('Identified %s as %s type', item.content, current_type) else: current_type = parent_type if item.children: active = self.process_items(item.children, current_type, not_in_first or first and item != first) else: active = False if self.check_future(item): continue active |= self.process_item(item, parent_type, first, not_in_first) item.active = active parent_active |= active return parent_active def process_item(self, item, type, first=None, not_in_first=False): """ Process single item """ # untag if checked if item.checked or not_in_first: return self.remove_label(item, self.next_label_id) # don't tag if parent with unchecked child elif [child for child in item.children if not child.checked]: return self.remove_label(item, self.next_label_id) # tag all parallel but not waitfors elif type == "parallel" and not self.is_waitfor(item): return self.add_label(item, self.next_label_id) # tag the first serial elif type == "serial" and item == first: return self.add_label(item, self.next_label_id) # untag otherwise else: return self.remove_label(item, self.next_label_id) def activate(self, items): """ Mark indent 1 items as active if supposed to be """ for item in items: if item.active: self.add_label(item, self.active_label_id) else: self.remove_label(item, self.active_label_id) def check_future(self, item): """ If its too far in the future, remove the next_action tag and skip """ if self.args.hide_future > 0 and item.due_date_utc: due_date = datetime.strptime(item.due_date_utc, '%a %d %b %Y %H:%M:%S +0000') future_diff = (due_date - datetime.utcnow()).total_seconds() if future_diff >= (self.args.hide_future * 86400): self.remove_label(item, self.next_label_id) return True def get_project_type(self, project_object, parent_type): """ Identifies how a project should be handled """ name = project_object['name'].strip() if name == 'Inbox': return self.args.inbox elif name[-1] == self.args.parallel_suffix: return 'parallel' elif name[-1] == self.args.serial_suffix: return 'serial' elif parent_type: return parent_type def get_item_type(self, item): """ Identifies how a item with sub items should be handled """ name = item.content.strip() if name[-1] == self.args.parallel_suffix: return 'parallel' elif name[-1] == self.args.serial_suffix: return 'serial' def add_label(self, item, label): if label not in item.labels: labels = item.labels logging.debug('Updating %s with label %s', item.content, label) labels.append(label) self.api.items.update(item.id, labels=labels) return True def remove_label(self, item, label): if label in item.labels: labels = item.labels logging.debug('Updating %s without label %s', item.content, label) labels.remove(label) self.api.items.update(item.id, labels=labels) return False def is_waitfor(self, item): return self.waitfor_label_id in item.labels @staticmethod def get_subitems(items, parent_item=None): """ Search a flat item list for child items """ result_items = [] found = False if parent_item: required_indent = parent_item['indent'] + 1 else: required_indent = 1 for item in items: if parent_item: if not found and item['id'] != parent_item['id']: continue else: found = True if item['indent'] == parent_item['indent'] and item['id'] != \ parent_item['id']: return result_items elif item['indent'] == required_indent and found: result_items.append(item) elif item['indent'] == required_indent: result_items.append(item) return result_items def parse_args(self): """ Parse command-line arguments """ parser = argparse.ArgumentParser() parser.add_argument('-a', '--api_key', help='Todoist API Key') parser.add_argument('-l', '--label', help='The next action label to use', default='next_action') parser.add_argument('-c', '--active', help='The active level1 parent label', default='active') parser.add_argument('-w', '--waitfor', help='The waitfor label', default='waitfor') parser.add_argument('-d', '--delay', help='Specify the delay in seconds between syncs', default=5, type=int) parser.add_argument('--debug', help='Enable debugging', action='store_true') parser.add_argument('--inbox', help='The method the Inbox project should ' 'be processed', default='parallel', choices=['parallel', 'serial']) parser.add_argument('--parallel_suffix', default='.') parser.add_argument('--serial_suffix', default='_') parser.add_argument('--hide_future', help='Hide future dated next actions until the ' 'specified number of days', default=7, type=int) parser.add_argument('--onetime', help='Update Todoist once and exit', action='store_true') self.args = parser.parse_args() # Set debug if self.args.debug: log_level = logging.DEBUG else: log_level = logging.INFO logging.basicConfig(level=log_level) # Check we have a API key if not self.args.api_key: logging.error('No API key set, exiting...') sys.exit(1)
def __init__(self): self.token = os.getenv('TOKEN') self.api = TodoistAPI(self.token) self.api.sync() self.project_id = None
47: "#808080", 38: "#158fad", 48: "#b8b8b8", 39: "#14aaf5", 49: "#ccac93" } print( "Esse script gera um gráfico com suas tarefas completadas por dia e coloridas\n" "de acordo com o projeto a que pertenciam em um dado período de interesse") # Recebe token do usuário token = input("\nInsira seu token de acesso: ") # Autentica e sincroniza os dados do Todoist api = TodoistAPI("a24619b4c0cae7ad1c1bf43bf9bd81a0588fdd76" if len(token) == 0 else token) api.sync() # Ensina o Python a processar as datas def process(date): words = [s.strip() for s in date.split("de")] day, month, year = int(words[0]), month_number[words[1].lower()[:3]], \ "20" + words[2][-2:] if len(words) == 3 else "2020" return "{}-{}-{}T00:00".format(year, month, day) # Recebe e processa o intervalo desejado since = process(input("> Desde a data: ")) until = process(input("> Até a data : "))[:-5] + "23:59"
from todoist.api import TodoistAPI from datetime import date api = TodoistAPI('API KEY') api.sync() current_date = str(date.today()) items = api.state["items"] def CheckDateLess(specDate): splitDate = specDate.split('-') today = date.today() if((int(splitDate[0]) <= today.year) and ( int(splitDate[1]) <= today.month) and (int(splitDate[2]) < today.day)): return 1 else: return 0 def ReadAllCurrent(): current_task_name= [] for i in range(len(items)): if (items[i]['due'] != None): if (items[i]['due']['date'] == current_date): current_task_name.append(items[i]['content']) return current_task_name def ReadAllOverdue(): overdue_task_name= [] for i in range(len(items)): if (items[i]['due'] != None):
import logging import pandas as pd from todoist.api import TodoistAPI logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger("main") TODOIST_TOKEN = os.getenv('TODOIST_TOKEN') if TODOIST_TOKEN is None: LOGGER.error("TODOIST_TOKEN env is not set") exit(1) LOGGER.info("Syncing Todoist ...") api = TodoistAPI(TODOIST_TOKEN) api.sync() LOGGER.info("Synced...") # The API limits 100 activities to be retrieved per call, so a loop is needed # Items are retrieved in descending order by date. # offset indicates how many items should be skipped activity_list = [] limit = 100 offset = 0 while True: LOGGER.info(f"Getting activities, batch {int((offset+100)/100)}") # API call, retrieving between 0 and 100 activities activities = api.activity.get(limit=limit, offset=offset)
def main(): parser = argparse.ArgumentParser() parser.add_argument('-a', '--api_key', help='Todoist API Key') parser.add_argument('-l', '--label', help='The next action label to use', default='next_action') parser.add_argument('-d', '--delay', help='Specify the delay in seconds between syncs', default=5, type=int) parser.add_argument('--debug', help='Enable debugging', action='store_true') parser.add_argument('--inbox', help='The method the Inbox project should be processed', default='parallel', choices=['parallel', 'serial']) parser.add_argument('--parallel_suffix', default='.') parser.add_argument('--serial_suffix', default='_') parser.add_argument('--hide_future', help='Hide future dated next actions until the specified number of days', default=7, type=int) parser.add_argument('--onetime', help='Update Todoist once and exit', action='store_true') args = parser.parse_args() # Set debug if args.debug: log_level = logging.DEBUG else: log_level = logging.INFO logging.basicConfig(level=log_level) # Check we have a API key if not args.api_key: logging.error('No API key set, exiting...') sys.exit(1) # Run the initial sync logging.debug('Connecting to the Todoist API') api = TodoistAPI(token=args.api_key) logging.debug('Syncing the current state from the API') api.sync(resource_types=['projects', 'labels', 'items']) # Check the next action label exists labels = api.labels.all(lambda x: x['name'] == args.label) if len(labels) > 0: label_id = labels[0]['id'] logging.debug('Label %s found as label id %d', args.label, label_id) else: logging.error("Label %s doesn't exist, please create it or change TODOIST_NEXT_ACTION_LABEL.", args.label) sys.exit(1) def get_project_type(project_object): """Identifies how a project should be handled""" name = project_object['name'].strip() if project['name'] == 'Inbox': return args.inbox elif name[-1] == args.parallel_suffix: return 'parallel' elif name[-1] == args.serial_suffix: return 'serial' def get_item_type(item): """Identifies how a item with sub items should be handled""" name = item['content'].strip() if name[-1] == args.parallel_suffix: return 'parallel' elif name[-1] == args.serial_suffix: return 'serial' def add_label(item, label): if label not in item['labels']: labels = item['labels'] logging.debug('Updating %s with label', item['content']) labels.append(label) api.items.update(item['id'], labels=labels) def remove_label(item, label): if label in item['labels']: labels = item['labels'] logging.debug('Updating %s without label', item['content']) labels.remove(label) api.items.update(item['id'], labels=labels) # Main loop while True: try: api.sync(resource_types=['projects', 'labels', 'items']) except Exception as e: logging.exception('Error trying to sync with Todoist API: %s' % str(e)) else: for project in api.projects.all(): project_type = get_project_type(project) if project_type: logging.debug('Project %s being processed as %s', project['name'], project_type) items = sorted(api.items.all(lambda x: x['project_id'] == project['id']), key=lambda x: x['item_order']) for item in items: # If its too far in the future, remove the next_action tag and skip if args.hide_future > 0 and 'due_date_utc' in item.data and item['due_date_utc'] is not None: due_date = datetime.strptime(item['due_date_utc'], '%a %d %b %Y %H:%M:%S +0000') future_diff = (due_date - datetime.utcnow()).total_seconds() if future_diff >= (args.hide_future * 86400): remove_label(item, label_id) continue item_type = get_item_type(item) child_items = get_subitems(items, item) if item_type: logging.debug('Identified %s as %s type', item['content'], item_type) if item_type or len(child_items) > 0: # Process serial tagged items if item_type == 'serial': for idx, child_item in enumerate(child_items): if idx == 0: add_label(child_item, label_id) else: remove_label(child_item, label_id) # Process parallel tagged items or untagged parents else: for child_item in child_items: add_label(child_item, label_id) # Remove the label from the parent remove_label(item, label_id) # Process items as per project type on indent 1 if untagged else: if item['indent'] == 1: if project_type == 'serial': if item['item_order'] == 1: add_label(item, label_id) else: remove_label(item, label_id) elif project_type == 'parallel': add_label(item, label_id) logging.debug('%d changes queued for sync... commiting if needed', len(api.queue)) if len(api.queue): api.commit() if args.onetime: break logging.debug('Sleeping for %d seconds', args.delay) time.sleep(args.delay)
class Task: """Overall class to manage behavior of pushing generated tasks to Todoist.""" def __init__(self): self.api = TodoistAPI(self.get_api()) # Get a start time and slice the string into two variables to use work_start. self.start_working_time = input( "What time will you start studying? Please input as hh:mm\n") self.desired_cycles = int( input("How many study cycles would you like to do?\n")) hours = self.start_working_time[:2] minutes = self.start_working_time[3:5] # Get length of each cycle to calculate timedelta from start time. month_of_task = int( input("What month? Enter as a number. Leave empty for current\n") or datetime.now().month) day_of_task = int( input("On what day? Enter as a number. Leave empty for current\n") or datetime.now().day) # Calc using user values for work/break length. self.work_start = datetime(datetime.now().year, month_of_task, day_of_task, int(hours), int(minutes)) self.work_duration = timedelta( minutes=int(input("How long will you study each cycle?\n"))) self.break_duration = timedelta( minutes=int(input("How long will your breaks be?\n"))) @staticmethod def get_api(): """Either uses input api and saves it for next time or reads api used last time from file.""" user_api = input("Paste api token.\nLeave empty for last used:\n") while True: if len(user_api) == 0: with open('api_token.txt', 'r') as file_object: return file_object.read() elif len(user_api) == 40: with open('api_token.txt', "w") as file_object: file_object.write(user_api) return user_api else: print( "Please paste the token as raw data with no quotation marks or whitespace." ) def find_inbox_id(self): """Finds the id of the default 'Inbox' project.""" for project in self.api.state['projects']: if project['name'] == 'Inbox': return project['id'] def make_task(self): """Run loop that adds a new task into Todoist for as many times as user specified.""" current_cycles = 0 while current_cycles < self.desired_cycles: work_cycle = self.work_start + ( self.work_duration + self.break_duration) * current_cycles print(f"{work_cycle} - {work_cycle + self.work_duration} WORK") self.api.items.add( f"WORK - {work_cycle:%H:%M} - {work_cycle + self.work_duration:%H:%M}", project_id=self.find_inbox_id(), due={"string": work_cycle}) break_cycle = self.work_start + self.work_duration + ( self.work_duration + self.break_duration) * current_cycles print(f"{break_cycle} - {break_cycle + self.break_duration} BREAK") self.api.items.add( f"BREAK - {break_cycle:%H:%M} - {break_cycle + self.break_duration:%H:%M}", project_id=self.find_inbox_id(), due={"string": break_cycle}) current_cycles += 1 self.api.commit()
def main(): """Main process function.""" parser = argparse.ArgumentParser() parser.add_argument('-a', '--api_key', help='Todoist API Key') parser.add_argument('-l', '--label', help='The next action label to use', default='next_action') parser.add_argument('-d', '--delay', help='Specify the delay in seconds between syncs', default=5, type=int) parser.add_argument('--debug', help='Enable debugging', action='store_true') parser.add_argument('--inbox', help='The method the Inbox project should be processed', default='parallel', choices=['parallel', 'serial', 'none']) parser.add_argument('--parallel_suffix', default='.') parser.add_argument('--serial_suffix', default='_') parser.add_argument('--hide_future', help='Hide future dated next actions until the specified number of days', default=7, type=int) parser.add_argument('--hide_scheduled', help='', action='store_true') # TODO: help parser.add_argument('--remove_label', help='Remove next action label from unmarked projects', action='store_true') parser.add_argument('--onetime', help='Update Todoist once and exit', action='store_true') parser.add_argument('--nocache', help='Disables caching data to disk for quicker syncing', action='store_true') args = parser.parse_args() # Set debug if args.debug: log_level = logging.DEBUG else: log_level = logging.INFO logging.basicConfig(level=log_level) # Check we have a API key if not args.api_key: logging.error('No API key set, exiting...') sys.exit(1) # Run the initial sync logging.debug('Connecting to the Todoist API') api_arguments = {'token': args.api_key} if args.nocache: logging.debug('Disabling local caching') api_arguments['cache'] = None api = TodoistAPI(**api_arguments) logging.debug('Syncing the current state from the API') api.sync() # Check the next action label exists labels = api.labels.all(lambda x: x['name'] == args.label) if len(labels) > 0: label_id = labels[0]['id'] logging.debug('Label %s found as label id %d', args.label, label_id) else: logging.error("Label %s doesn't exist, please create it or change TODOIST_NEXT_ACTION_LABEL.", args.label) sys.exit(1) def get_project_type(project_object): """Identifies how a project should be handled.""" name = project_object['name'].strip() if name == 'Inbox' and args.inbox != 'none': return args.inbox elif name[-1] == args.parallel_suffix: return 'parallel' elif name[-1] == args.serial_suffix: return 'serial' def get_item_type(item): """Identifies how a item with sub items should be handled.""" name = item['content'].strip() if name[-1] == args.parallel_suffix: return 'parallel' elif name[-1] == args.serial_suffix: return 'serial' def add_label(item, label): if label not in item['labels']: logging.debug('Updating %s (%d) with label', item['content'], item['id']) labels = item['labels'] labels.append(label) api.items.update(item['id'], labels=labels) def remove_label(item, label): if label in item['labels']: logging.debug('Updating %s (%d) without label', item['content'], item['id']) labels = item['labels'] labels.remove(label) api.items.update(item['id'], labels=labels) # Main loop while True: try: api.sync() except Exception as e: logging.exception('Error trying to sync with Todoist API: %s' % str(e)) else: for project in api.projects.all(lambda x: not x['is_deleted'] and not x['is_archived']): project_type = get_project_type(project) items = api.items.all( lambda x: x['project_id'] == project['id'] and not (x['checked'] or x['is_deleted'] or x['is_archived']) ) if project_type: logging.debug('Project %s being processed as %s', project['name'], project_type) # Get all items for the project, sort by the item_order field. items = sorted( items, key=lambda x: x['item_order'] ) for real_order, item in enumerate(filter(lambda x: x['indent'] == 1, items)): item_type = get_item_type(item) if item.data.get('due_date_utc'): if args.hide_scheduled and not item_type: remove_label(item, label_id) continue # If its too far in the future, remove the next_action tag and skip if args.hide_future > 0: due_date = datetime.strptime(item['due_date_utc'], '%a %d %b %Y %H:%M:%S +0000') future_diff = (due_date - datetime.utcnow()).total_seconds() if future_diff >= (args.hide_future * 86400): remove_label(item, label_id) continue child_items = get_subitems(items, item) def add_indent1_label(): if child_items: remove_label(item, label_id) add_label(child_items[0], label_id) func = remove_label if item_type == 'serial' else add_label for child_item in child_items[1:]: func(child_item, label_id) else: add_label(item, label_id) def remove_indent1_label(): remove_label(item, label_id) for child_item in child_items: remove_label(child_item, label_id) if project_type == 'serial': if real_order == 0: add_indent1_label() else: remove_indent1_label() elif project_type == 'parallel': add_indent1_label() elif args.remove_label: for item in items: remove_label(item, label_id) if len(api.queue): logging.debug('%d changes queued for sync... commiting to Todoist.', len(api.queue)) api.commit() else: logging.debug('No changes queued, skipping sync.') # If onetime is set, exit after first execution. if args.onetime: break logging.debug('Sleeping for %d seconds', args.delay) time.sleep(args.delay)
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Todoist platform.""" # Check token: token = config.get(CONF_TOKEN) # Look up IDs based on (lowercase) names. project_id_lookup = {} label_id_lookup = {} from todoist.api import TodoistAPI api = TodoistAPI(token) api.sync() # Setup devices: # Grab all projects. projects = api.state[PROJECTS] # Grab all labels labels = api.state[LABELS] # Add all Todoist-defined projects. project_devices = [] for project in projects: # Project is an object, not a dict! # Because of that, we convert what we need to a dict. project_data = {CONF_NAME: project[NAME], CONF_ID: project[ID]} project_devices.append( TodoistProjectDevice(hass, project_data, labels, api)) # Cache the names so we can easily look up name->ID. project_id_lookup[project[NAME].lower()] = project[ID] # Cache all label names for label in labels: label_id_lookup[label[NAME].lower()] = label[ID] # Check config for more projects. extra_projects = config.get(CONF_EXTRA_PROJECTS) for project in extra_projects: # Special filter: By date project_due_date = project.get(CONF_PROJECT_DUE_DATE) # Special filter: By label project_label_filter = project.get(CONF_PROJECT_LABEL_WHITELIST) # Special filter: By name # Names must be converted into IDs. project_name_filter = project.get(CONF_PROJECT_WHITELIST) project_id_filter = [ project_id_lookup[project_name.lower()] for project_name in project_name_filter ] # Create the custom project and add it to the devices array. project_devices.append( TodoistProjectDevice(hass, project, labels, api, project_due_date, project_label_filter, project_id_filter)) add_devices(project_devices) # Services: descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def handle_new_task(call): """Called when a user creates a new Todoist Task from HASS.""" project_name = call.data[PROJECT_NAME] project_id = project_id_lookup[project_name] # Create the task item = api.items.add(call.data[CONTENT], project_id) if LABELS in call.data: task_labels = call.data[LABELS] label_ids = [ label_id_lookup[label.lower()] for label in task_labels ] item.update(labels=label_ids) if PRIORITY in call.data: item.update(priority=call.data[PRIORITY]) if DUE_DATE in call.data: due_date = dt.parse_datetime(call.data[DUE_DATE]) if due_date is None: due = dt.parse_date(call.data[DUE_DATE]) due_date = datetime(due.year, due.month, due.day) # Format it in the manner Todoist expects due_date = dt.as_utc(due_date) date_format = '%Y-%m-%dT%H:%M' due_date = datetime.strftime(due_date, date_format) item.update(due_date_utc=due_date) # Commit changes api.commit() _LOGGER.debug("Created Todoist task: %s", call.data[CONTENT]) hass.services.register(DOMAIN, SERVICE_NEW_TASK, handle_new_task, descriptions[DOMAIN][SERVICE_NEW_TASK], schema=NEW_TASK_SERVICE_SCHEMA)
class Sorter: def __init__(self, api_token, project_id): self.token = api_token self.api = TodoistAPI(api_token) self.api.sync() self.project_id = project_id self.dbfilename = 'Todoist.db' self.dbtableprefix = 'Sections_' self.dbtablename = self.dbtableprefix + str(project_id) def initialize_db(self): conn = sqlite3.connect(self.dbfilename) db = conn.cursor() query = 'CREATE TABLE IF NOT EXISTS "{}" ("item_project" INT, "item_content" TEXT, "item_section" INT, "last_updated" TEXT)'.format( self.dbtablename) db.execute(query) db.close() def get_section_name(self, section_id): sectionList = self.api.state['sections'] for section in sectionList: if section['id'] == section_id: return section['name'] # RETURN FALSE IF NO MATCH IS FOUND return False def capitalize_item(self, item_id): item_content = self.api.items.get_by_id(item_id)['content'] if not item_content[0].isupper(): new_content = item_content[0].upper() + item_content[1:] # WRITE UPDATED CONTENT TO TODOIST item = self.api.items.get_by_id(item_id) item.update(content=new_content) self.api.commit() def get_historic_section(self, item_id): item = self.api.items.get_by_id(item_id) self.initialize_db() conn = sqlite3.connect(self.dbfilename) selectQuery = "SELECT item_content, item_section FROM {} WHERE item_content = '{}' LIMIT 1".format( self.dbtablename, item['content'].lower()) db = conn.cursor() result = db.execute(selectQuery).fetchone() db.close() conn.commit() if result is not None: return result[1] else: return None def learn(self): self.initialize_db() conn = sqlite3.connect(self.dbfilename) query = "" itemList = self.api.state['items'] for item in itemList: timestamp = datetime.datetime.now().strftime( "%Y-%m-%d %H:%M") # USED TO INSERT INTO DB WHEN UPDATING if item['project_id'] == self.project_id and item[ 'section_id'] is not None: # GET HISTORIC SECTION historic_section = self.get_historic_section(item['id']) if historic_section == item['section_id']: # NO UPDATE NEEDED pass if historic_section is None: # ADD ITEM TO DB query = "INSERT INTO {} (item_project, item_content, item_section, last_updated) VALUES ({},'{}',{}, '{}')".format( self.dbtablename, item['project_id'], item['content'].lower(), item['section_id'], timestamp) if historic_section is not None and historic_section != item[ 'section_id']: # UPDATE CURRENT SECTION query = "UPDATE {} SET item_section = {}, last_updated = '{}' WHERE item_content = '{}'".format( self.dbtablename, item['section_id'], timestamp, item['content'].lower()) db = conn.cursor() db.execute(query) db.close() query = "" conn.commit() # def adjust_item_section_UNSUPPORTED(self, item_id): # # TODO WRITE UPDATED CONTENT TO TODOIST # if self.get_historic_section(item_id) is not None: # item = self.api.items.get_by_id(item_id) # item.move(parent_id=item_id) # self.api.commit() def adjust_item_section(self, item_id): # USING MANUAL REQUESTS METHOD AS SECTIONS ARENT SUPPORTED IN CURRENT VERSION OF TODOIST SYNC API-LIBRARY state = uuid.uuid4() apiUrlSync = "https://api.todoist.com/sync/v8/sync" commands = '[{"type": "item_move", "uuid": "' + str( state) + '", "args": {"id": ' + str( item_id) + ', "section_id": ' + str( self.get_historic_section(item_id)) + '}}]' payload = {"token": str(self.token), 'commands': commands} requests.post(apiUrlSync, data=payload).json()
def get_task(self, id): api = TodoistAPI('313f6bf203b35e7ac56e39561a80633e459c9c54') if api.items.get_by_id(id) != None: return True else: return False
def __init__(self): self.api = TodoistAPI(Config.TODOIST_API_KEY) self.api.sync() self.mt = MusicTools()
class ServiceTodoist(ServicesMgr): """ service Todoist """ def __init__(self, token=None, **kwargs): super(ServiceTodoist, self).__init__(token, **kwargs) self.AUTH_URL = 'https://todoist.com/oauth/authorize' self.ACC_TOKEN = 'https://todoist.com/oauth/access_token' self.REQ_TOKEN = 'https://todoist.com/oauth/access_token' self.consumer_key = settings.TH_TODOIST_KEY['client_id'] self.consumer_secret = settings.TH_TODOIST_KEY['client_secret'] self.scope = 'task:add,data:read,data:read_write' self.service = 'ServiceTodoist' self.oauth = 'oauth2' if token: self.token = token self.todoist = TodoistAPI(token) def read_data(self, **kwargs): """ get the data from the service as the pocket service does not have any date in its API linked to the note, add the triggered date to the dict data thus the service will be triggered when data will be found :param kwargs: contain keyword args : trigger_id at least :type kwargs: dict :rtype: list """ trigger_id = kwargs.get('trigger_id') date_triggered = kwargs.get('date_triggered') data = [] project_name = 'Main Project' items = self.todoist.sync() try: for item in items.get('items'): date_added = arrow.get(item.get('date_added'), 'ddd DD MMM YYYY HH:mm:ss ZZ') if date_added > date_triggered: for project in items.get('projects'): if item.get('project_id') == project.get('id'): project_name = project.get('name') title = 'From TodoIst Project {0}:'.format(project_name) data.append({ 'title': title, 'content': item.get('content') }) # digester self.send_digest_event(trigger_id, title, '') cache.set('th_todoist_' + str(trigger_id), data) except AttributeError: logger.error(items) return data def save_data(self, trigger_id, **data): """ let's save the data :param trigger_id: trigger ID from which to save data :param data: the data to check to be used and save :type trigger_id: int :type data: dict :return: the status of the save statement :rtype: boolean """ title, content = super(ServiceTodoist, self).save_data(trigger_id, **data) if self.token: if title or content or data.get('link'): content = title + ' ' + content + ' ' + data.get('link') self.todoist.add_item(content) sentence = str('todoist {} created').format(data.get('link')) logger.debug(sentence) status = True else: status = False else: logger.critical("no token or link provided for " "trigger ID {} ".format(trigger_id)) status = False return status
def list_project_tasks(self, project_id): api_call = TodoistAPI(self.api_key) api_call.sync() for item in range(len(api_call.state['items'])): if api_call.state['items'][item].data['project_id'] == project_id: print(api_call.state['items'][item].data['content'])
class syncManager: def __init__(self, todoist_token, notion_token, notion_settings_url): self.api = TodoistAPI(todoist_token) self.client = NotionClient(token_v2=notion_token) self.settings = self.client.get_collection_view(notion_settings_url) # extract and parse settings self.config = {} self.use_groups = False self.sync_completed_tasks = False self.sync_config() self.tasks = self.client.get_collection_view( "https://www.notion.so/" + self.config["Link to task database"]) self.projects = self.client.get_collection_view( "https://www.notion.so/" + self.config["Link to project database"]) self.labels = self.client.get_collection_view( "https://www.notion.so/" + self.config["Link to label database"]) # initialize todoist api jsons self.old_sync = None self.new_sync = None self.old_commit = None self.new_commit = None self.last_notion_sync_time = helper.add_local_tz( datetime(2020, 1, 1, 0, 0, 0, 0)) # taskManager.sync_notion_to_todoist(self, full_sync=True) def sync_todoist_api(self): self.old_sync = self.new_sync self.new_sync = self.api.sync() def commit_todoist_api(self): self.old_commit = self.new_commit self.new_commit = self.api.commit() def update_notion_sync_time(self): self.last_notion_sync_time = helper.add_local_tz(datetime.now()) def sync_notion_tasks(self): taskManager.sync_notion_to_todoist(self) def _parse_label_columns(self): column_list = self.config['Label column name'] column_dict = {} for item in column_list.replace(" ", "").split(","): key, value = item.split("=") column_dict[key] = value self.config["Input label string"] = self.config['Label column name'] self.config['Label column name'] = column_dict def sync_config(self): self.config = { row.title: row.value for row in self.settings.collection.get_rows() } # extract settings self._parse_label_columns() self.use_groups = True if self.config["Use groups"].lower() in [ "true", "yes", "y", "t" ] else False self.sync_completed_tasks = True if self.config["Sync completed tasks"].lower() in \ ["true", "yes", "y", "t"] else False self.config['Sync completed tasks'] = self.sync_completed_tasks self.config['Use groups'] = self.use_groups print("Sync configuration updated.")
import pandas as pd from todoist.api import TodoistAPI api = TodoistAPI('df864d9cbf7b4e669e50bf626fa088eef855ff5e') api.sync() # .data attribute retrieves a python dictionary rather than todoist.models.Project projects = [project.data for project in api.state['projects']] # I can easily create a DataFrame on the 'projects' list of dicts df_projects = pd.DataFrame(projects) df_projects.head() tasks = [task.data for task in api.state['items']] df_tasks = pd.DataFrame(tasks) df_tasks.head() df_tasks['date_added'] = pd.to_datetime(( pd.to_datetime(df_tasks['date_added'], utc=True).dt.tz_convert( 'Europe/Budapest') # my current timezone .dt.strftime("%Y-%m-%d %H:%M:%S"))) # easier to handle format df_tasks['due_date_utc'] = pd.to_datetime((pd.to_datetime( df_tasks['due_date_utc'], utc=True).dt.tz_convert('Europe/Budapest').dt.strftime("%Y-%m-%d %H:%M:%S") )) df_tasks['date_completed'] = pd.to_datetime((pd.to_datetime( df_tasks['date_completed'], utc=True).dt.tz_convert('Europe/Budapest').dt.strftime("%Y-%m-%d %H:%M:%S")
def __init__(self): self.api = TodoistAPI(get_token()) self.api.sync()
class Project: def __init__(self): self.token = os.getenv('TOKEN') self.api = TodoistAPI(self.token) self.api.sync() self.project_id = None def create_project(self, project_name: str): """ Creates a new project on todoist :param project_name: The name of the project """ project = self.api.projects.add(project_name) self.api.commit() print('Created project:') print(project) self.project_id = project.data['id'] def remove_project(self): """ Removes the current project """ if self.project_id is None: print('No project Created or project has been deleted') return project = self.api.projects.get_by_id(self.project_id) project.delete() self.api.commit() def contains_task_with_message(self, message): """ Checks if the current project has a task the given message :param message: The message to look for :return: True if it does, false otherwise """ print(self.api.items.all()) print('filtered') print(self.project_id) print( list( filter(lambda task: task['content'] == message, self.api.items.all()))) return len( list( filter(lambda task: task['content'] == message, self.api.items.all()))) > 0 def reopen_task_with_message(self, message): """ Reopens the task with the given message :param message: The message to look for, assumes it exists """ item_id = list( filter( lambda task: task['content'] == message and task['project_id'] == self.project_id, self.api.items.all()))[0]['id'] item = self.api.items.get_by_id(item_id) item.uncomplete() self.api.commit() def sync(self): """ Syncs the API """ self.api.sync() time.sleep(API_WAIT_TIME)
unix_lib_path = os.path.expanduser("~/work/pa/PersonalAssistant") win_lib_path = 'C:\\Users\\Petr\\Desktop\\HomeAutomation\\PersonalAssistant' def is_unix(): val = os.path.join('_', '_') return val == "_/_" if is_unix(): if unix_lib_path not in sys.path: sys.path.append(unix_lib_path) else: if win_lib_path not in sys.path: sys.path.append(win_lib_path) import lib.stack as palib import pyutils import resources # ---------------------------------------------------------------------------------------------------------------------- # 2) class TodoistAPI from todoist.api import TodoistAPI api = TodoistAPI(pyutils.get_token()) # ---------------------------------------------------------------------------------------------------------------------- # 3) class PersonalAssistant pa = palib.PersonalAssistant()
from dev.tasks import create_jira_admin_task from dev.tasks import create_branch_subtask from dev.tasks import create_merge_subtask from dev.tasks import create_subtask from dev.tasks import create_terraform_merge_subtask from dev.ui import print_heading from dev.ui import ui_create_root_dev_task from dev.ui import ui_create_subtasks from dev.ui import ui_get_jira_reference from todoist.api import TodoistAPI from labels import get_label_ids from projects import ui_select_project api_token = os.getenv('TODOIST_API_TOKEN') api = TodoistAPI(api_token) api.sync() def ui_get_terraform_modules(): print_heading("Module Selection") modules = [ "terraform_cms_dns", "terraform_dcf_entrypoint", "terraform_dcf_networking" ] print("The following modules are available to work with:") count = 1 for module in modules: print("{num}. {name}".format(num=count, name=module)) count += 1 print("Select a module or use a comma separated list to select multiple")
from todoist.api import TodoistAPI import sys token = sys.argv[1] api = TodoistAPI(token) api.sync() api.user.update(theme=3) def enable_dark_mode(): api.user.update(theme=11) def enable_light_mode(): api.user.update(theme=6) def choose_theme(): themeName = sys.argv[2] if themeName == 'dark': enable_dark_mode() else: enable_light_mode() if __name__ == '__main__': choose_theme() api.commit()
def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Todoist platform.""" token = config.get(CONF_TOKEN) # Look up IDs based on (lowercase) names. project_id_lookup = {} label_id_lookup = {} api = TodoistAPI(token) api.sync() # Setup devices: # Grab all projects. projects = api.state[PROJECTS] # Grab all labels labels = api.state[LABELS] # Add all Todoist-defined projects. project_devices = [] for project in projects: # Project is an object, not a dict! # Because of that, we convert what we need to a dict. project_data = {CONF_NAME: project[NAME], CONF_ID: project[ID]} project_devices.append( TodoistProjectDevice(hass, project_data, labels, api)) # Cache the names so we can easily look up name->ID. project_id_lookup[project[NAME].lower()] = project[ID] # Cache all label names for label in labels: label_id_lookup[label[NAME].lower()] = label[ID] # Check config for more projects. extra_projects = config[CONF_EXTRA_PROJECTS] for project in extra_projects: # Special filter: By date project_due_date = project.get(CONF_PROJECT_DUE_DATE) # Special filter: By label project_label_filter = project[CONF_PROJECT_LABEL_WHITELIST] # Special filter: By name # Names must be converted into IDs. project_name_filter = project[CONF_PROJECT_WHITELIST] project_id_filter = [ project_id_lookup[project_name.lower()] for project_name in project_name_filter ] # Create the custom project and add it to the devices array. project_devices.append( TodoistProjectDevice( hass, project, labels, api, project_due_date, project_label_filter, project_id_filter, )) add_entities(project_devices) def handle_new_task(call): """Call when a user creates a new Todoist Task from Home Assistant.""" project_name = call.data[PROJECT_NAME] project_id = project_id_lookup[project_name] # Create the task item = api.items.add(call.data[CONTENT], project_id=project_id) if LABELS in call.data: task_labels = call.data[LABELS] label_ids = [ label_id_lookup[label.lower()] for label in task_labels ] item.update(labels=label_ids) if PRIORITY in call.data: item.update(priority=call.data[PRIORITY]) _due: dict = {} if DUE_DATE_STRING in call.data: _due["string"] = call.data[DUE_DATE_STRING] if DUE_DATE_LANG in call.data: _due["lang"] = call.data[DUE_DATE_LANG] if DUE_DATE in call.data: due_date = dt.parse_datetime(call.data[DUE_DATE]) if due_date is None: due = dt.parse_date(call.data[DUE_DATE]) due_date = datetime(due.year, due.month, due.day) # Format it in the manner Todoist expects due_date = dt.as_utc(due_date) date_format = "%Y-%m-%dT%H:%M%S" due_date = datetime.strftime(due_date, date_format) _due["date"] = due_date if _due: item.update(due=_due) _reminder_due: dict = {} if REMINDER_DATE_STRING in call.data: _reminder_due["string"] = call.data[REMINDER_DATE_STRING] if REMINDER_DATE_LANG in call.data: _reminder_due["lang"] = call.data[REMINDER_DATE_LANG] if REMINDER_DATE in call.data: due_date = dt.parse_datetime(call.data[REMINDER_DATE]) if due_date is None: due = dt.parse_date(call.data[REMINDER_DATE]) due_date = datetime(due.year, due.month, due.day) # Format it in the manner Todoist expects due_date = dt.as_utc(due_date) date_format = "%Y-%m-%dT%H:%M:%S" due_date = datetime.strftime(due_date, date_format) _reminder_due["date"] = due_date if _reminder_due: api.reminders.add(item["id"], due=_reminder_due) # Commit changes api.commit() _LOGGER.debug("Created Todoist task: %s", call.data[CONTENT]) hass.services.register(DOMAIN, SERVICE_NEW_TASK, handle_new_task, schema=NEW_TASK_SERVICE_SCHEMA)
import argparse import io import json import os import requests import sys from todoist.api import TodoistAPI from os.path import join, dirname from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) api = TodoistAPI(os.environ.get("APITOKEN")) api.sync() def projects(): list = api.state['projects'] print('### プロジェクト一覧') for name in list: print(name['name']) def tasks(args): list = api.state['projects'] for projects_id in list: if projects_id['name'] == args.tasks: tasks_project_id = projects_id['id'] break ## 例外キャッチ: tasks_project_idがセットされていなければ終了する
from todoist.api import TodoistAPI api = TodoistAPI('d9deec7ba39852d370168e80ebb66e4b60d927df') api.sync() #print(api.state['projects']) # project1 = api.projects.add('Project1') """ api.commit() print(project1) """ project1 = api.projects.add('Project1') task1 = api.items.add('Task1', project_id=project1['id']) task2 = api.items.add('Task2', project_id=project1['id']) api.commit()
from todoist.api import TodoistAPI # Load environment variables load_dotenv() # Initialize app app = Flask(__name__) # Set SQLAlchemy databse URI app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL") # Initialize db db = SQLAlchemy(app) # Create Todoist API client api = TodoistAPI(os.environ.get("TODOIST_API_TOKEN")) class Item(db.Model): __tablename__ = "items" project_id: int = db.Column(db.Integer) responsible_uid: int = db.Column(db.Integer) section_id: int = db.Column(db.Integer) sync_id: int = db.Column(db.Integer) user_id: int = db.Column(db.Integer) added_by_uid: int = db.Column(db.Integer) assigned_by_uid: int = db.Column(db.Integer) checked: int = db.Column(db.Integer) child_order: int = db.Column(db.Integer) collapsed: int = db.Column(db.Integer) content: str = db.Column(db.String)
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Todoist platform.""" token = config.get(CONF_TOKEN) # Look up IDs based on (lowercase) names. project_id_lookup = {} label_id_lookup = {} from todoist.api import TodoistAPI api = TodoistAPI(token) api.sync() # Setup devices: # Grab all projects. projects = api.state[PROJECTS] # Grab all labels labels = api.state[LABELS] # Add all Todoist-defined projects. project_devices = [] for project in projects: # Project is an object, not a dict! # Because of that, we convert what we need to a dict. project_data = { CONF_NAME: project[NAME], CONF_ID: project[ID] } project_devices.append( TodoistProjectDevice(hass, project_data, labels, api) ) # Cache the names so we can easily look up name->ID. project_id_lookup[project[NAME].lower()] = project[ID] # Cache all label names for label in labels: label_id_lookup[label[NAME].lower()] = label[ID] # Check config for more projects. extra_projects = config.get(CONF_EXTRA_PROJECTS) for project in extra_projects: # Special filter: By date project_due_date = project.get(CONF_PROJECT_DUE_DATE) # Special filter: By label project_label_filter = project.get(CONF_PROJECT_LABEL_WHITELIST) # Special filter: By name # Names must be converted into IDs. project_name_filter = project.get(CONF_PROJECT_WHITELIST) project_id_filter = [ project_id_lookup[project_name.lower()] for project_name in project_name_filter] # Create the custom project and add it to the devices array. project_devices.append( TodoistProjectDevice( hass, project, labels, api, project_due_date, project_label_filter, project_id_filter ) ) add_devices(project_devices) def handle_new_task(call): """Call when a user creates a new Todoist Task from HASS.""" project_name = call.data[PROJECT_NAME] project_id = project_id_lookup[project_name] # Create the task item = api.items.add(call.data[CONTENT], project_id) if LABELS in call.data: task_labels = call.data[LABELS] label_ids = [ label_id_lookup[label.lower()] for label in task_labels] item.update(labels=label_ids) if PRIORITY in call.data: item.update(priority=call.data[PRIORITY]) if DUE_DATE in call.data: due_date = dt.parse_datetime(call.data[DUE_DATE]) if due_date is None: due = dt.parse_date(call.data[DUE_DATE]) due_date = datetime(due.year, due.month, due.day) # Format it in the manner Todoist expects due_date = dt.as_utc(due_date) date_format = '%Y-%m-%dT%H:%M' due_date = datetime.strftime(due_date, date_format) item.update(due_date_utc=due_date) # Commit changes api.commit() _LOGGER.debug("Created Todoist task: %s", call.data[CONTENT]) hass.services.register(DOMAIN, SERVICE_NEW_TASK, handle_new_task, schema=NEW_TASK_SERVICE_SCHEMA)
class ServiceTodoist(ServicesMgr): def __init__(self, token=None, **kwargs): super(ServiceTodoist, self).__init__(token, **kwargs) self.AUTH_URL = 'https://todoist.com/oauth/authorize' self.ACC_TOKEN = 'https://todoist.com/oauth/access_token' self.REQ_TOKEN = 'https://todoist.com/oauth/access_token' self.consumer_key = settings.TH_TODOIST['client_id'] self.consumer_secret = settings.TH_TODOIST['client_secret'] self.scope = 'task:add,data:read,data:read_write' self.service = 'ServiceTodoist' self.oauth = 'oauth2' if token: self.token = token self.todoist = TodoistAPI(token) def read_data(self, **kwargs): """ get the data from the service as the pocket service does not have any date in its API linked to the note, add the triggered date to the dict data thus the service will be triggered when data will be found :param kwargs: contain keyword args : trigger_id at least :type kwargs: dict :rtype: list """ trigger_id = kwargs.get('trigger_id') date_triggered = kwargs.get('date_triggered') data = [] project_name = 'Main Project' items = self.todoist.sync() for item in items.get('items'): date_added = arrow.get(item.get('date_added'), 'ddd DD MMM YYYY HH:mm:ss ZZ') if date_added > date_triggered: for project in items.get('projects'): if item.get('project_id') == project.get('id'): project_name = project.get('name') data.append({'title': "From TodoIst Project {0}" ":".format(project_name), 'content': item.get('content')}) cache.set('th_todoist_' + str(trigger_id), data) return data def save_data(self, trigger_id, **data): """ let's save the data :param trigger_id: trigger ID from which to save data :param data: the data to check to be used and save :type trigger_id: int :type data: dict :return: the status of the save statement :rtype: boolean """ kwargs = {} title, content = super(ServiceTodoist, self).save_data(trigger_id, data, **kwargs) if self.token: if title or content or \ (data.get('link') and len(data.get('link'))) > 0: content = title + ' ' + content + ' ' + data.get('link') self.todoist.add_item(content) sentence = str('todoist {} created').format(data.get('link')) logger.debug(sentence) status = True else: status = False else: logger.critical("no token or link provided for " "trigger ID {} ".format(trigger_id)) status = False return status