コード例 #1
0
def deauthorise():
    try:
        workflow().delete_password(config.KC_OAUTH_TOKEN)
        workflow().delete_password(config.KC_REFRESH_TOKEN)
        log.debug('Deauthorising')
    except PasswordNotFound:
        pass
コード例 #2
0
ファイル: taskfolder.py プロジェクト: oscu0/orpheus
    def sync(cls, background=False):
        from mstodo.api import taskfolders
        start = time.time()

        taskfolders_data = taskfolders.taskFolders()
        instances = []

        log.info('Retrieved all %d task folders in %s', len(taskfolders_data), time.time() - start)
        start = time.time()

        # Hacky translation of mstodo data model to wunderlist data model to avoid changing naming in rest of the files
        for taskfolder in taskfolders_data:
            for (k,v) in taskfolder.copy().iteritems():
                if k == "name":
                    taskfolder['title'] = v

        workflow().store_data('taskfolders', taskfolders_data)

        try:
            instances = cls.select(cls.id, cls.changeKey, cls.title)
        except PeeweeException:
            pass

        log.info('Loaded all %d task folders from the database in %s', len(instances), time.time() - start)

        return cls._perform_updates(instances, taskfolders_data)
コード例 #3
0
ファイル: logout.py プロジェクト: oscu0/orpheus
def filter(args):
    util.workflow().add_item(
        'Are you sure?',
        'You will need to log in to a Microsoft account to continue using the workflow',
        arg=' '.join(args),
        valid=True,
        icon=icons.CHECKMARK)

    util.workflow().add_item('Nevermind', autocomplete='', icon=icons.CANCEL)
コード例 #4
0
    def _set(self, key, value):
        if value is None and key in self._data:
            del self._data[key]
        elif self._data.get(key) != value:
            self._data[key] = value
        else:
            return

        workflow().store_data('prefs', self._data)
コード例 #5
0
ファイル: taskfolder.py プロジェクト: oscu0/orpheus
def filter(args):
    taskfolder_name = _taskfolder_name(args)
    subtitle = taskfolder_name if taskfolder_name else 'Type the name of the task folder'

    util.workflow().add_item('New folder...', subtitle, arg='--stored-query', valid=taskfolder_name != '', icon=icons.LIST_NEW)

    util.workflow().add_item(
        'Main menu',
        autocomplete='', icon=icons.BACK
    )
コード例 #6
0
    def __init__(self, data):
        self._data = data or {}

        # Clean up old prerelease preference
        if 'prerelease_channel' in self._data:
            # Migrate to the alfred-workflow preference
            self.prerelease_channel = self._data['prerelease_channel']
            del self._data['prerelease_channel']

            workflow().store_data('prefs', self._data)
コード例 #7
0
def new_oauth_state():
    log.debug('Creating new OAuth state')
    import random
    import string
    state_length = 20
    state = ''.join(random.SystemRandom().choice(string.ascii_uppercase +
                                                 string.digits)
                    for _ in range(state_length))
    workflow().save_password(config.KC_OAUTH_STATE, state)
    return state
コード例 #8
0
ファイル: auth.py プロジェクト: oscu0/orpheus
def is_authorised():
    if oauth_token() is None:
        log.debug('Not authorised')
        return False
    else:
        if workflow().cached_data('query_event', max_age=3600) is None:
            log.debug('No auth in last 3600s, refreshing token')
            return resolve_oauth_token(refresh_token=workflow().get_password(config.KC_REFRESH_TOKEN))
        else: 
            log.debug('Using cached OAuth token')
            return True
コード例 #9
0
def alfred_is_dark():
    # Formatted rgba(255,255,255,0.90)
    background_rgba = workflow().alfred_env['theme_background']
    if background_rgba:
        rgb = [int(x) for x in background_rgba[5:-6].split(',')]
        return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255 < 0.5
    return False
コード例 #10
0
def background_sync():
    from workflow.background import run_in_background
    task_id = 'sync'

    # Only runs if another sync is not already in progress
    run_in_background(task_id, [
        '/usr/bin/env', 'python',
        workflow().workflowfile('alfred-mstodo-workflow.py'),
        'pref sync background', '--commit'
    ])
コード例 #11
0
def authorise():
    from multiprocessing import Process
    import webbrowser
    workflow().store_data('auth', 'started')

    # construct auth request url. Replace quote_plus with parse.quote for Py3
    state = new_oauth_state()
    auth_url = (config.MS_TODO_AUTH_ROOT + "/authorize"
                "?client_id=" + config.MS_TODO_CLIENT_ID +
                "&response_type=code&response_mode=query" + "&redirect_uri=" +
                quote_plus(config.MS_TODO_REDIRECT_URL) + "&scope=" +
                quote_plus((' '.join(config.MS_TODO_SCOPE)).lower()) +
                "&state=" + state)
    log.debug(auth_url)
    # start server to handle response
    server = Process(target=await_token)
    server.start()
    # open browser to auth
    webbrowser.open(auth_url)
コード例 #12
0
def filter(args):
    task_id = args[1]
    wf = workflow()
    task = None

    try:
        task = Task.get(Task.id == task_id)
    except Task.DoesNotExist:
        pass

    if not task:
        wf.add_item('Unknown task',
                    'The ID does not match a task',
                    autocomplete='',
                    icon=icons.BACK)
    else:
        subtitle = task.subtitle()

        if task.status == 'completed':
            wf.add_item('Mark task not completed',
                        subtitle,
                        modifier_subtitles={},
                        arg=' '.join(args + ['toggle-completion']),
                        valid=True,
                        icon=icons.TASK_COMPLETED)
        else:
            wf.add_item('Complete this task',
                        subtitle,
                        modifier_subtitles={
                            'alt': u'…and set due today    %s' % subtitle
                        },
                        arg=' '.join(args + ['toggle-completion']),
                        valid=True,
                        icon=icons.TASK)

        wf.add_item('View in ToDo',
                    'View and edit this task in the ToDo app',
                    arg=' '.join(args + ['view']),
                    valid=True,
                    icon=icons.OPEN)

        if task.recurrence_type and not task.status == 'completed':
            wf.add_item('Delete',
                        'Delete this task and cancel recurrence',
                        arg=' '.join(args + ['delete']),
                        valid=True,
                        icon=icons.TRASH)
        else:
            wf.add_item('Delete',
                        'Delete this task',
                        arg=' '.join(args + ['delete']),
                        valid=True,
                        icon=icons.TRASH)

        wf.add_item('Main menu', autocomplete='', icon=icons.BACK)
コード例 #13
0
def resolve_oauth_token(code=None, refresh_token=None):
    token_url = config.MS_TODO_AUTH_ROOT + "/token"
    scope = config.MS_TODO_SCOPE
    data = {
        "client_id": config.MS_TODO_CLIENT_ID,
        "redirect_uri": config.MS_TODO_REDIRECT_URL,
        "scope": ' '.join(scope)
    }

    if code is not None:
        log.debug('first grant')
        data['grant_type'] = "authorization_code"
        data['code'] = code
    elif refresh_token is not None:
        log.debug('refeshing token')
        data['grant_type'] = "refresh_token"
        data['refresh_token'] = refresh_token

    if 'grant_type' in data:
        log.debug('Getting token from: ' + token_url)
        result = requests.post(token_url, data=data)
        log.debug('Auth response status: ' + str(result.status_code))
        if 'access_token' in result.text:
            log.debug('Saving access token in keychain')
            workflow().save_password(config.KC_OAUTH_TOKEN,
                                     result.json()['access_token'])
            workflow().save_password(config.KC_REFRESH_TOKEN,
                                     result.json()['refresh_token'])
            workflow().cache_data('query_event', True)
            return True

    return False
コード例 #14
0
def commit(args, modifier=None):
    if 'update' in args:
        if workflow().start_update():
            print('The workflow is being updated')
        else:
            print('You already have the latest workflow version')
    else:
        import webbrowser

        if 'changelog' in args:
            webbrowser.open(
                'https://github.com/johandebeurs/alfred-mstodo-workflow/releases/tag/0.1.2'
            )
        elif 'mstodo' in args:
            webbrowser.open('https://www.todo.microsoft.com/')
        elif 'issues' in args:
            webbrowser.open(
                'https://github.com/johandebeurs/alfred-mstodo-workflow/issues'
            )
コード例 #15
0
def filter(args):
    workflow().add_item(
        'New in this version',
        'Installed: 0.1.2   See the changes from the previous version',
        arg='-about changelog',
        valid=True,
        icon=icons.INFO)

    workflow().add_item(
        'Questions or concerns?',
        'See outstanding issues and report your own bugs or feedback',
        arg='-about issues',
        valid=True,
        icon=icons.HELP)

    workflow().add_item(
        'Update workflow',
        'Check for updates to the workflow (automatically checked periodically)',
        arg='-about update',
        valid=True,
        icon=icons.DOWNLOAD)

    workflow().add_item('Main menu', autocomplete='', icon=icons.BACK)
コード例 #16
0
def handle_authorisation_url(url):
    # Parse query data & params to find out what was passed
    # parsed_url = parse.urlparse(url)
    # params = parse.parse_qs(parsed_url.query)
    params = parse_qs(urlparse(url).query)
    if 'code' in params and validate_oauth_state(params['state'][0]):
        log.debug('Valid OAuth response and state matches')
        # Request a token based on the code
        resolve_oauth_token(code=params['code'][0])
        workflow().store_data('auth', None)
        workflow().delete_password(config.KC_OAUTH_STATE)
        print('You are now logged in')
        return True
    elif 'error' in params:
        workflow().store_data('auth', 'Error: %s' % params['error'])
        print('Please try again later')
        return params['error']

    # Not a valid URL
    return False
コード例 #17
0
def filter(args):
    wf = workflow()
    prefs = Preferences.current_prefs()
    command = args[1] if len(args) > 1 else None
    duration_info = _duration_info(prefs.completed_duration)

    if command == 'duration':
        selected_duration = prefs.completed_duration

        # Apply selected duration option
        if len(args) > 2:
            try:
                selected_duration = int(args[2])
            except:
                pass

        duration_info = _duration_info(selected_duration)

        if 'custom' in duration_info:
            wf.add_item(duration_info['label'],
                        duration_info['subtitle'],
                        arg='-completed duration %d' % (duration_info['days']),
                        valid=True,
                        icon=icons.RADIO_SELECTED if duration_info['days']
                        == selected_duration else icons.RADIO)

        for duration_info in _durations:
            wf.add_item(duration_info['label'],
                        duration_info['subtitle'],
                        arg='-completed duration %d' % (duration_info['days']),
                        valid=True,
                        icon=icons.RADIO_SELECTED if duration_info['days']
                        == selected_duration else icons.RADIO)

        wf.add_item('Back', autocomplete='-completed ', icon=icons.BACK)

        return

    # Force a sync if not done recently or join if already running
    if not prefs.last_sync or \
       datetime.utcnow() - prefs.last_sync > timedelta(seconds=30) or \
       is_running('sync'):
        sync()

    wf.add_item(duration_info['label'],
                subtitle='Change the duration for completed tasks',
                autocomplete='-completed duration ',
                icon=icons.YESTERDAY)

    conditions = True

    # Build task title query based on the args
    for arg in args[1:]:
        if len(arg) > 1:
            conditions = conditions & (Task.title.contains(arg)
                                       | TaskFolder.title.contains(arg))

    if conditions is None:
        conditions = True

    tasks = Task.select().join(TaskFolder).where(
        (Task.completedDateTime > date.today() - timedelta(days=duration_info['days'])) &
        Task.list.is_null(False) &
        conditions
    )\
        .order_by(Task.completedDateTime.desc(), Task.reminderDateTime.asc(), Task.changeKey.asc())

    try:
        for t in tasks:
            wf.add_item(u'%s – %s' % (t.list_title, t.title),
                        t.subtitle(),
                        autocomplete='-task %s ' % t.id,
                        icon=icons.TASK_COMPLETED
                        if t.status == 'completed' else icons.TASK)
    except OperationalError:
        background_sync()

    wf.add_item('Main menu', autocomplete='', icon=icons.BACK)

    # Make sure tasks stay up-to-date
    background_sync_if_necessary(seconds=2)
コード例 #18
0
ファイル: search.py プロジェクト: oscu0/orpheus
def filter(args):
    query = ' '.join(args[1:])
    wf = workflow()
    prefs = Preferences.current_prefs()
    matching_hashtags = []

    if not query:
        wf.add_item('Begin typing to search tasks', '', icon=icons.SEARCH)

    hashtag_match = re.search(_hashtag_prompt_pattern, query)
    if hashtag_match:
        from mstodo.models.hashtag import Hashtag

        hashtag_prompt = hashtag_match.group().lower()
        hashtags = Hashtag.select().where(
            Hashtag.id.contains(hashtag_prompt)).order_by(
                fn.Lower(Hashtag.tag).asc())

        for hashtag in hashtags:
            # If there is an exact match, do not show hashtags
            if hashtag.id == hashtag_prompt:
                matching_hashtags = []
                break

            matching_hashtags.append(hashtag)

    # Show hashtag prompt if there is more than one matching hashtag or the
    # hashtag being typed does not exactly match the single matching hashtag
    if len(matching_hashtags) > 0:
        for hashtag in matching_hashtags:
            wf.add_item(hashtag.tag[1:],
                        '',
                        autocomplete=u'-search %s%s ' %
                        (query[:hashtag_match.start()], hashtag.tag),
                        icon=icons.HASHTAG)

    else:
        conditions = True
        taskfolders = workflow().stored_data('taskfolders')
        matching_taskfolders = None
        query = ' '.join(args[1:]).strip()
        taskfolder_query = None

        # Show all task folders on the main search screen
        if not query:
            matching_taskfolders = taskfolders
        # Filter task folders when colon is used
        if ':' in query:
            matching_taskfolders = taskfolders
            components = re.split(r':\s*', query, 1)
            taskfolder_query = components[0]
            if taskfolder_query:
                matching_taskfolders = workflow().filter(
                    taskfolder_query,
                    taskfolders if taskfolders else [],
                    lambda f: f['title'],
                    # Ignore MATCH_ALLCHARS which is expensive and inaccurate
                    match_on=MATCH_ALL ^ MATCH_ALLCHARS)

                # If no matching task folder search against all tasks
                if matching_taskfolders:
                    query = components[1] if len(components) > 1 else ''

                # If there is a task folder exactly matching the query ignore
                # anything else. This takes care of taskfolders that are substrings
                # of other taskfolders
                if len(matching_taskfolders) > 1:
                    for f in matching_taskfolders:
                        if f['title'].lower() == taskfolder_query.lower():
                            matching_taskfolders = [f]
                            break

        if matching_taskfolders:
            if not taskfolder_query:
                wf.add_item('Browse by hashtag',
                            autocomplete='-search #',
                            icon=icons.HASHTAG)

            if len(matching_taskfolders) > 1:
                for f in matching_taskfolders:
                    icon = icons.INBOX if f['isDefaultFolder'] else icons.LIST
                    wf.add_item(f['title'],
                                autocomplete='-search %s: ' % f['title'],
                                icon=icon)
            else:
                conditions = conditions & (Task.list
                                           == matching_taskfolders[0]['id'])

        if not matching_taskfolders or len(matching_taskfolders) <= 1:
            for arg in query.split(' '):
                if len(arg) > 1:
                    conditions = conditions & (Task.title.contains(arg) |
                                               TaskFolder.title.contains(arg))

            if conditions:
                if not prefs.show_completed_tasks:
                    conditions = (Task.status != 'completed') & conditions

                tasks = Task.select().where(
                    Task.list.is_null(False) & conditions)

                tasks = tasks.join(TaskFolder).order_by(
                    Task.lastModifiedDateTime.desc(),
                    TaskFolder.changeKey.asc())

                # Avoid excessive results
                tasks = tasks.limit(50)

                try:
                    for t in tasks:
                        wf.add_item(u'%s – %s' % (t.list_title, t.title),
                                    t.subtitle(),
                                    autocomplete='-task %s  ' % t.id,
                                    icon=icons.TASK_COMPLETED
                                    if t.status == 'completed' else icons.TASK)
                except OperationalError:
                    background_sync()

            if prefs.show_completed_tasks:
                wf.add_item('Hide completed tasks',
                            arg='-pref show_completed_tasks --alfred %s' %
                            ' '.join(args),
                            valid=True,
                            icon=icons.HIDDEN)
            else:
                wf.add_item('Show completed tasks',
                            arg='-pref show_completed_tasks --alfred %s' %
                            ' '.join(args),
                            valid=True,
                            icon=icons.VISIBLE)

        wf.add_item('New search', autocomplete='-search ', icon=icons.CANCEL)
        wf.add_item('Main menu', autocomplete='', icon=icons.BACK)

        # Make sure tasks are up-to-date while searching
        background_sync()
コード例 #19
0
ファイル: route.py プロジェクト: oscu0/orpheus
def route(args):
    handler = None
    command = []
    command_string = ''
    action = 'none'
    logged_in = is_authorised()

    # Read the stored query, which will correspond to the user's alfred query
    # as of the very latest keystroke. This may be different than the query
    # when this script was launched due to the startup latency.
    if args[0] == '--stored-query':
        query_file = workflow().workflowfile('.query')
        with open(query_file, 'r') as f:
            command_string = workflow().decode(f.read())
        os.remove(query_file)
    # Otherwise take the command from the first command line argument
    elif args:
        command_string = args[0]

    command_string = re.sub(COMMAND_PATTERN, '', command_string)
    command = re.split(r' +', command_string)

    if command:
        action = re.sub(ACTION_PATTERN, '', command[0]) or 'none'

    if 'about'.find(action) == 0:
        from mstodo.handlers import about
        handler = about
    elif not logged_in:
        from mstodo.handlers import login
        handler = login
    elif 'folder'.find(action) == 0:
        from mstodo.handlers import taskfolder
        handler = taskfolder
    elif 'task'.find(action) == 0:
        from mstodo.handlers import task
        handler = task
    elif 'search'.find(action) == 0:
        from mstodo.handlers import search
        handler = search
    elif 'due'.find(action) == 0:
        from mstodo.handlers import due
        handler = due
    elif 'upcoming'.find(action) == 0:
        from mstodo.handlers import upcoming
        handler = upcoming
    elif 'completed'.find(action) == 0:
        from mstodo.handlers import completed
        handler = completed
    elif 'logout'.find(action) == 0:
        from mstodo.handlers import logout
        handler = logout
    elif 'pref'.find(action) == 0:
        from mstodo.handlers import preferences
        handler = preferences
    # If the command starts with a space (no special keywords), the workflow
    # creates a new task
    elif not command_string:
        from mstodo.handlers import welcome
        handler = welcome
    else:
        from mstodo.handlers import new_task
        handler = new_task

    if handler:
        if '--commit' in args:
            modifier = re.search(r'--(alt|cmd|ctrl|fn)\b', ' '.join(args))

            if modifier:
                modifier = modifier.group(1)

            handler.commit(command, modifier)
        else:
            handler.filter(command)

            if workflow().update_available:
                update_data = workflow().cached_data('__workflow_update_status', max_age=0)

                if update_data.get('version') != '0.1.2':
                    workflow().add_item('An update is available!', 'Update the ToDo workflow from version 0.1.2 to %s' % update_data.get('version'), arg='-about update', valid=True, icon=icons.DOWNLOAD)

            workflow().send_feedback()
    
    if logged_in:
        background_sync_if_necessary()
コード例 #20
0
ファイル: login.py プロジェクト: oscu0/orpheus
def filter(args):
    getting_help = False

    if len(args) > 0:
        action = re.sub(ACTION_PATTERN, '', args[0])
        getting_help = action and 'help'.find(action) == 0

    if not getting_help:
        workflow().add_item(
            'Please log in',
            'Authorise Alfred ToDo Workflow to use your Microsoft account',
            valid=True,
            icon=icons.ACCOUNT)

    # If the auth process has started, allow user to paste a key manually
    if getting_help:
        workflow().add_item(
            'A "localhost" page appeared in my web browser',
            u'Paste the full link from your browser above then press return, td:help http://localhost:6200/…',
            arg=' '.join(args),
            valid=True,
            icon=icons.LINK)
        workflow().add_item(
            'I need to log in to a different account',
            'Go to microsoft.com in your browser and sign out of your account first',
            arg='-about mstodo',
            valid=True,
            icon=icons.ACCOUNT)
        workflow().add_item(
            'Other issues?',
            'See outstanding issues and report your own bugs or feedback',
            arg='-about issues',
            valid=True,
            icon=icons.HELP)
    else:
        workflow().add_item('Having trouble?',
                            autocomplete='-help ',
                            valid=False,
                            icon=icons.HELP)

    if not getting_help:
        workflow().add_item('About',
                            'Learn about the workflow and get support',
                            autocomplete='-about ',
                            icon=icons.INFO)
コード例 #21
0
def sync(background=False):
    log.info('running mstodo/sync')
    from mstodo.models import base, task, user, taskfolder, hashtag
    from peewee import OperationalError

    # If a sync is already running, wait for it to finish. Otherwise, store
    # the current pid in alfred-workflow's pid cache file
    if not background:
        if is_running('sync'):
            wait_count = 0
            while is_running('sync'):
                time.sleep(.25)
                wait_count += 1

                if wait_count == 2:
                    notify(
                        'Please wait...',
                        'The workflow is making sure your tasks are up-to-date'
                    )

            return False

        pidfile = workflow().cachefile('sync.pid')
        with open(pidfile, 'wb') as file_obj:
            file_obj.write('{0}'.format(os.getpid()))

    base.BaseModel._meta.database.create_tables(
        [taskfolder.TaskFolder, task.Task, user.User, hashtag.Hashtag],
        safe=True)

    # Perform a query that requires the latest schema; if it fails due to a
    # mismatched scheme, delete the old database and re-sync
    try:
        task.Task.select().where(task.Task.recurrence_count > 0).count()
        hashtag.Hashtag.select().where(hashtag.Hashtag.tag == '').count()
    except OperationalError:
        base.BaseModel._meta.database.close()
        workflow().clear_data(lambda f: 'mstodo.db' in f)

        # Make sure that this sync does not try to wait until its own process
        # finishes
        sync(background=True)
        return

    first_sync = False

    try:
        # get root item from DB. If it doesn't exist then make this the first sync.
        user.User.get()
    except user.User.DoesNotExist:
        first_sync = True
        Preferences.current_prefs().last_sync = datetime.utcnow()
        notify('Please wait...',
               'The workflow is syncing tasks for the first time')

    user.User.sync(background=background)
    taskfolder.TaskFolder.sync(background=background)
    if first_sync:
        task.Task.sync_all_tasks(background=background)
    else:
        task.Task.sync_modified_tasks(background=background)

    if background:
        if first_sync:
            notify('Initial sync has completed',
                   'All of your tasks are now available for browsing')

        # If executed manually, this will pass on to the post notification action
        print('Sync completed successfully')

    log.debug('First sync: ' + str(first_sync))
    log.debug('Last sync time: ' + str(Preferences.current_prefs().last_sync))
    Preferences.current_prefs().last_sync = datetime.utcnow()
    log.debug('This sync time: ' + str(Preferences.current_prefs().last_sync))
    return True
コード例 #22
0
ファイル: due.py プロジェクト: oscu0/orpheus
def filter(args):
    wf = workflow()
    prefs = Preferences.current_prefs()
    command = args[1] if len(args) > 1 else None

    # Show sort options
    if command == 'sort':
        for i, order_info in enumerate(_due_orders):
            wf.add_item(order_info['title'],
                        order_info['subtitle'],
                        arg='-due sort %d' % (i + 1),
                        valid=True,
                        icon=icons.RADIO_SELECTED if order_info['due_order']
                        == prefs.due_order else icons.RADIO)

        wf.add_item(
            'Highlight skipped recurring tasks',
            'Hoists recurring tasks that have been missed multiple times over to the top',
            arg='-due sort toggle-skipped',
            valid=True,
            icon=icons.CHECKBOX_SELECTED
            if prefs.hoist_skipped_tasks else icons.CHECKBOX)

        wf.add_item('Back', autocomplete='-due ', icon=icons.BACK)

        return

    # Force a sync if not done recently or wait on the current sync
    if not prefs.last_sync or \
       datetime.utcnow() - prefs.last_sync > timedelta(seconds=30) or \
       is_running('sync'):
        sync()

    conditions = True

    # Build task title query based on the args
    for arg in args[1:]:
        if len(arg) > 1:
            conditions = conditions & (Task.title.contains(arg)
                                       | TaskFolder.title.contains(arg))

    if conditions is None:
        conditions = True

    tasks = Task.select().join(TaskFolder).where(
        (Task.status != 'completed')
        & (Task.dueDateTime < datetime.now() + timedelta(days=1))
        & Task.list_id.is_null(False) & conditions)

    # Sort the tasks according to user preference
    for key in prefs.due_order:
        order = 'asc'
        field = None
        if key[0] == '-':
            order = 'desc'
            key = key[1:]

        if key == 'due_date':
            field = Task.dueDateTime
        elif key == 'taskfolder.id':
            field = TaskFolder.id
        elif key == 'order':
            field = Task.lastModifiedDateTime

        if field:
            if order == 'asc':
                tasks = tasks.order_by(field.asc())
            else:
                tasks = tasks.order_by(field.desc())

    try:
        if prefs.hoist_skipped_tasks:
            log.debug('hoisting skipped tasks')
            tasks = sorted(tasks, key=lambda t: -t.overdue_times)

        for t in tasks:
            wf.add_item(u'%s – %s' % (t.list_title, t.title),
                        t.subtitle(),
                        autocomplete='-task %s ' % t.id,
                        icon=icons.TASK_COMPLETED
                        if t.status == 'completed' else icons.TASK)
    except OperationalError:
        background_sync()

    wf.add_item(u'Sort order',
                'Change the display order of due tasks',
                autocomplete='-due sort',
                icon=icons.SORT)

    wf.add_item('Main menu', autocomplete='', icon=icons.BACK)

    # Make sure tasks stay up-to-date
    background_sync_if_necessary(seconds=2)
コード例 #23
0
ファイル: logout.py プロジェクト: oscu0/orpheus
def commit(args, modifier=None):
    auth.deauthorise()
    util.workflow().clear_data()
    util.workflow().clear_cache()

    print('You are now logged out')
コード例 #24
0
def commit(args, modifier=None):
    prefs = Preferences.current_prefs()
    relaunch_command = '-pref'

    if '--alfred' in args:
        relaunch_command = ' '.join(args[args.index('--alfred') + 1:])

    if 'sync' in args:
        from mstodo.sync import sync
        sync('background' in args)

        relaunch_command = None
    elif 'show_completed_tasks' in args:
        prefs.show_completed_tasks = not prefs.show_completed_tasks

        if prefs.show_completed_tasks:
            print('Completed tasks are now visible in the workflow')
        else:
            print('Completed tasks will not be visible in the workflow')
    elif 'default_folder' in args:
        default_taskfolder_id = None
        taskfolders = workflow().stored_data('taskfolders')

        if len(args) > 2:
            default_taskfolder_id = args[2]

        prefs.default_taskfolder_id = default_taskfolder_id

        if default_taskfolder_id:
            default_folder_name = next(
                (f['title']
                 for f in taskfolders if f['id'] == default_taskfolder_id),
                'most recent')
            print('Tasks will be added to your %s folder by default' %
                  default_folder_name)
        else:
            print('Tasks will be added to the Tasks folder by default')
    elif 'explicit_keywords' in args:
        prefs.explicit_keywords = not prefs.explicit_keywords

        if prefs.explicit_keywords:
            print('Remember to use the "due" keyword')
        else:
            print('Implicit due dates enabled (e.g. "Recycling tomorrow")')
    elif 'reminder' in args:
        reminder_time = _parse_time(' '.join(args))

        if reminder_time is not None:
            prefs.reminder_time = reminder_time

            print('Reminders will now default to %s' %
                  format_time(reminder_time, 'short'))
    elif 'reminder_today' in args:
        reminder_today_offset = None

        if not 'disabled' in args:
            reminder_today_offset = _parse_time(' '.join(args))

        prefs.reminder_today_offset = reminder_today_offset

        print('The offset for current-day reminders is now %s' %
              _format_time_offset(reminder_today_offset))
    elif 'automatic_reminders' in args:
        prefs.automatic_reminders = not prefs.automatic_reminders

        if prefs.automatic_reminders:
            print('A reminder will automatically be set for due tasks')
        else:
            print('A reminder will not be added automatically')
    elif 'retheme' in args:
        prefs.icon_theme = 'light' if icons.icon_theme() == 'dark' else 'dark'

        print('The workflow is now using the %s icon theme' %
              (prefs.icon_theme))
    elif 'prerelease_channel' in args:

        prefs.prerelease_channel = not prefs.prerelease_channel

        # Update the workflow settings and reverify the update data
        workflow().check_update(True)

        if prefs.prerelease_channel:
            print(
                'The workflow will prompt you to update to experimental pre-releases'
            )
        else:
            print(
                'The workflow will only prompt you to update to final releases'
            )
    elif 'force_en_US' in args:
        if prefs.date_locale:
            prefs.date_locale = None
            print(
                'The workflow will expect your local language and date format')
        else:
            prefs.date_locale = 'en_US'
            print('The workflow will expect dates in US English')

    if relaunch_command:
        relaunch_alfred('td%s' % relaunch_command)
コード例 #25
0
def filter(args):
    prefs = Preferences.current_prefs()

    if 'reminder' in args:
        reminder_time = _parse_time(' '.join(args))

        if reminder_time is not None:
            workflow().add_item('Change default reminder time',
                                u'⏰ %s' % format_time(reminder_time, 'short'),
                                arg=' '.join(args),
                                valid=True,
                                icon=icons.REMINDER)
        else:
            workflow().add_item(
                'Type a new reminder time',
                'Date offsets like the morning before the due date are not supported yet',
                valid=False,
                icon=icons.REMINDER)

        workflow().add_item('Cancel', autocomplete='-pref', icon=icons.BACK)
    elif 'reminder_today' in args:
        reminder_today_offset = _parse_time(' '.join(args))

        if reminder_today_offset is not None:
            workflow().add_item('Set a custom reminder offset',
                                u'⏰ now + %s' %
                                _format_time_offset(reminder_today_offset),
                                arg=' '.join(args),
                                valid=True,
                                icon=icons.REMINDER)
        else:
            workflow().add_item('Type a custom reminder offset',
                                'Use the formats hh:mm or 2h 5m',
                                valid=False,
                                icon=icons.REMINDER)

        workflow().add_item('30 minutes',
                            arg='-pref reminder_today 30m',
                            valid=True,
                            icon=icons.REMINDER)

        workflow().add_item('1 hour',
                            '(default)',
                            arg='-pref reminder_today 1h',
                            valid=True,
                            icon=icons.REMINDER)

        workflow().add_item('90 minutes',
                            arg='-pref reminder_today 90m',
                            valid=True,
                            icon=icons.REMINDER)

        workflow().add_item(
            'Always use the default reminder time',
            'Avoids adjusting the reminder based on the current date',
            arg='-pref reminder_today disabled',
            valid=True,
            icon=icons.CANCEL)

        workflow().add_item('Cancel', autocomplete='-pref', icon=icons.BACK)
    elif 'default_folder' in args:
        taskfolders = workflow().stored_data('taskfolders')
        matching_taskfolders = taskfolders

        if len(args) > 2:
            taskfolder_query = ' '.join(args[2:])
            if taskfolder_query:
                matching_taskfolders = workflow().filter(
                    taskfolder_query,
                    taskfolders,
                    lambda f: f['title'],
                    # Ignore MATCH_ALLCHARS which is expensive and inaccurate
                    match_on=MATCH_ALL ^ MATCH_ALLCHARS)

        for i, f in enumerate(matching_taskfolders):
            if i == 1:
                workflow().add_item(
                    'Most recently used folder',
                    'Default to the last folder to which a task was added',
                    arg='-pref default_folder %s' %
                    DEFAULT_TASKFOLDER_MOST_RECENT,
                    valid=True,
                    icon=icons.RECURRENCE)
            icon = icons.INBOX if f['isDefaultFolder'] else icons.LIST
            workflow().add_item(f['title'],
                                arg='-pref default_folder %s' % f['id'],
                                valid=True,
                                icon=icon)

        workflow().add_item('Cancel', autocomplete='-pref', icon=icons.BACK)
    else:
        current_user = None
        taskfolders = workflow().stored_data('taskfolders')
        loc = user_locale()
        default_folder_name = 'Tasks'

        try:
            current_user = User.get()
        except User.DoesNotExist:
            pass
        except OperationalError:
            from mstodo.sync import background_sync
            background_sync()

        if prefs.default_taskfolder_id == DEFAULT_TASKFOLDER_MOST_RECENT:
            default_folder_name = 'Most recent folder'
        else:
            default_taskfolder_id = prefs.default_taskfolder_id
            default_folder_name = next(
                (f['title']
                 for f in taskfolders if f['id'] == default_taskfolder_id),
                'Tasks')

        if current_user and current_user.name:
            workflow().add_item('Sign out',
                                'You are logged in as ' + current_user.name,
                                autocomplete='-logout',
                                icon=icons.CANCEL)

        workflow().add_item('Show completed tasks',
                            'Includes completed tasks in search results',
                            arg='-pref show_completed_tasks',
                            valid=True,
                            icon=icons.TASK_COMPLETED
                            if prefs.show_completed_tasks else icons.TASK)

        workflow().add_item(
            'Default reminder time',
            u'⏰ %s    Reminders without a specific time will be set to this time'
            % format_time(prefs.reminder_time, 'short'),
            autocomplete='-pref reminder ',
            icon=icons.REMINDER)

        workflow().add_item(
            'Default reminder when due today',
            u'⏰ %s    Default reminder time for tasks due today is %s' %
            (_format_time_offset(prefs.reminder_today_offset),
             'relative to the current time' if prefs.reminder_today_offset else
             'always %s' % format_time(prefs.reminder_time, 'short')),
            autocomplete='-pref reminder_today ',
            icon=icons.REMINDER)

        workflow().add_item(
            'Default folder',
            u'%s    Change the default folder when creating new tasks' %
            default_folder_name,
            autocomplete='-pref default_folder ',
            icon=icons.LIST)

        workflow().add_item(
            'Automatically set a reminder on the due date',
            u'Sets a default reminder for tasks with a due date.',
            arg='-pref automatic_reminders',
            valid=True,
            icon=icons.TASK_COMPLETED
            if prefs.automatic_reminders else icons.TASK)

        if loc != 'en_US' or prefs.date_locale:
            workflow().add_item('Force US English for dates',
                                'Rather than the current locale (%s)' % loc,
                                arg='-pref force_en_US',
                                valid=True,
                                icon=icons.TASK_COMPLETED if prefs.date_locale
                                == 'en_US' else icons.TASK)

        workflow().add_item(
            'Require explicit due keyword',
            'Requires the due keyword to avoid accidental due date extraction',
            arg='-pref explicit_keywords',
            valid=True,
            icon=icons.TASK_COMPLETED
            if prefs.explicit_keywords else icons.TASK)

        workflow().add_item(
            'Check for experimental updates to this workflow',
            'The workflow automatically checks for updates; enable this to include pre-releases',
            arg=':pref prerelease_channel',
            valid=True,
            icon=icons.TASK_COMPLETED
            if prefs.prerelease_channel else icons.TASK)

        workflow().add_item(
            'Force sync',
            'The workflow syncs automatically, but feel free to be forcible.',
            arg='-pref sync',
            valid=True,
            icon=icons.SYNC)

        workflow().add_item('Switch theme',
                            'Toggle between light and dark icons',
                            arg='-pref retheme',
                            valid=True,
                            icon=icons.PAINTBRUSH)

        workflow().add_item('Main menu', autocomplete='', icon=icons.BACK)
コード例 #26
0
 def prerelease_channel(self):
     return workflow().settings.get(PRERELEASES_KEY, False)
コード例 #27
0
def filter(args):
    task = _task(args)
    subtitle = task_subtitle(task)
    wf = workflow()
    matching_hashtags = []

    if not task.title:
        subtitle = 'Begin typing to add a new task'

    # Preload matching hashtags into a list so that we can get the length
    if task.has_hashtag_prompt:
        from mstodo.models.hashtag import Hashtag

        hashtags = Hashtag.select().where(
            Hashtag.id.contains(task.hashtag_prompt.lower())).order_by(
                fn.Lower(Hashtag.tag).asc())

        for hashtag in hashtags:
            matching_hashtags.append(hashtag)

    # Show hashtag prompt if there is more than one matching hashtag or the
    # hashtag being typed does not exactly match the single matching hashtag
    if task.has_hashtag_prompt and len(matching_hashtags) > 0 and (
            len(matching_hashtags) > 1
            or task.hashtag_prompt != matching_hashtags[0].tag):
        for hashtag in matching_hashtags:
            wf.add_item(hashtag.tag[1:],
                        '',
                        autocomplete=' ' +
                        task.phrase_with(hashtag=hashtag.tag) + ' ',
                        icon=icons.HASHTAG)

    elif task.has_list_prompt:
        taskfolders = wf.stored_data('taskfolders')
        if taskfolders:
            for taskfolder in taskfolders:
                # Show some full list names and some concatenated in command
                # suggestions
                sample_command = taskfolder['title']
                if random() > 0.5:
                    sample_command = sample_command[:int(
                        len(sample_command) * .75)]
                icon = icons.INBOX if taskfolder[
                    'isDefaultFolder'] else icons.LIST
                wf.add_item(taskfolder['title'],
                            'Assign task to this folder, e.g. %s: %s' %
                            (sample_command.lower(), task.title),
                            autocomplete=' ' +
                            task.phrase_with(list_title=taskfolder['title']),
                            icon=icon)
            wf.add_item('Remove folder',
                        'Tasks without a folder are added to the Inbox',
                        autocomplete=' ' + task.phrase_with(list_title=False),
                        icon=icons.CANCEL)
        elif is_running('sync'):
            wf.add_item('Your folders are being synchronized',
                        'Please try again in a few moments',
                        autocomplete=' ' + task.phrase_with(list_title=False),
                        icon=icons.BACK)

    # Task has an unfinished recurrence phrase
    elif task.has_recurrence_prompt:
        wf.add_item('Every month',
                    'Same day every month, e.g. every mo',
                    uid="recurrence_1m",
                    autocomplete=' %s ' %
                    task.phrase_with(recurrence='every month'),
                    icon=icons.RECURRENCE)
        wf.add_item('Every week',
                    'Same day every week, e.g. every week, every Tuesday',
                    uid="recurrence_1w",
                    autocomplete=' %s ' %
                    task.phrase_with(recurrence='every week'),
                    icon=icons.RECURRENCE)
        wf.add_item('Every year',
                    'Same date every year, e.g. every 1 y, every April 15',
                    uid="recurrence_1y",
                    autocomplete=' %s ' %
                    task.phrase_with(recurrence='every year'),
                    icon=icons.RECURRENCE)
        wf.add_item('Every 3 months',
                    'Same day every 3 months, e.g. every 3 months',
                    uid="recurrence_3m",
                    autocomplete=' %s ' %
                    task.phrase_with(recurrence='every 3 months'),
                    icon=icons.RECURRENCE)
        wf.add_item('Remove recurrence',
                    autocomplete=' ' + task.phrase_with(recurrence=False),
                    icon=icons.CANCEL)

    # Task has an unfinished due date phrase
    elif task.has_due_date_prompt:
        wf.add_item('Today',
                    'e.g. due today',
                    autocomplete=' %s ' %
                    task.phrase_with(due_date='due today'),
                    icon=icons.TODAY)
        wf.add_item('Tomorrow',
                    'e.g. due tomorrow',
                    autocomplete=' %s ' %
                    task.phrase_with(due_date='due tomorrow'),
                    icon=icons.TOMORROW)
        wf.add_item('Next Week',
                    'e.g. due next week',
                    autocomplete=' %s ' %
                    task.phrase_with(due_date='due next week'),
                    icon=icons.NEXT_WEEK)
        wf.add_item('Next Month',
                    'e.g. due next month',
                    autocomplete=' %s ' %
                    task.phrase_with(due_date='due next month'),
                    icon=icons.CALENDAR)
        wf.add_item('Next Year',
                    'e.g. due next year, due April 15',
                    autocomplete=' %s ' %
                    task.phrase_with(due_date='due next year'),
                    icon=icons.CALENDAR)
        wf.add_item('Remove due date',
                    'Add "not due" to fix accidental dates, or see td-pref',
                    autocomplete=' ' + task.phrase_with(due_date=False),
                    icon=icons.CANCEL)

    # Task has an unfinished reminder phrase
    elif task.has_reminder_prompt:
        prefs = Preferences.current_prefs()
        default_reminder_time = format_time(prefs.reminder_time, 'short')
        due_date_hint = ' on the due date' if task.due_date else ''
        wf.add_item(
            'Reminder at %s%s' % (default_reminder_time, due_date_hint),
            'e.g. r %s' % default_reminder_time,
            autocomplete=' %s ' %
            task.phrase_with(reminder_date='remind me at %s' %
                             format_time(prefs.reminder_time, 'short')),
            icon=icons.REMINDER)
        wf.add_item('At noon%s' % due_date_hint,
                    'e.g. reminder noon',
                    autocomplete=' %s ' %
                    task.phrase_with(reminder_date='remind me at noon'),
                    icon=icons.REMINDER)
        wf.add_item('At 8:00 PM%s' % due_date_hint,
                    'e.g. remind at 8:00 PM',
                    autocomplete=' %s ' %
                    task.phrase_with(reminder_date='remind me at 8:00pm'),
                    icon=icons.REMINDER)
        wf.add_item('At dinner%s' % due_date_hint,
                    'e.g. alarm at dinner',
                    autocomplete=' %s ' %
                    task.phrase_with(reminder_date='remind me at dinner'),
                    icon=icons.REMINDER)
        wf.add_item(
            'Today at 6:00 PM',
            'e.g. remind me today at 6pm',
            autocomplete=' %s ' %
            task.phrase_with(reminder_date='remind me today at 6:00pm'),
            icon=icons.REMINDER)
        wf.add_item('Remove reminder',
                    autocomplete=' ' + task.phrase_with(reminder_date=False),
                    icon=icons.CANCEL)

    # Main menu for tasks
    else:
        wf.add_item(task.list_title + u' – create a new task...',
                    subtitle,
                    modifier_subtitles={
                        'alt':
                        u'…then edit it in the ToDo app    %s' % subtitle
                    },
                    arg='--stored-query',
                    valid=task.title != '',
                    icon=icons.TASK)

        title = 'Change folder' if task.list_title else 'Select a folder'
        wf.add_item(title,
                    'Prefix the task, e.g. Automotive: ' + task.title,
                    autocomplete=' ' + task.phrase_with(list_title=True),
                    icon=icons.LIST)

        title = 'Change the due date' if task.due_date else 'Set a due date'
        wf.add_item(
            title,
            '"due" followed by any date-related phrase, e.g. due next Tuesday; due May 4',
            autocomplete=' ' + task.phrase_with(due_date=True),
            icon=icons.CALENDAR)

        title = 'Change the recurrence' if task.recurrence_type else 'Make it a recurring task'
        wf.add_item(
            title,
            '"every" followed by a unit of time, e.g. every 2 months; every year; every 4w',
            autocomplete=' ' + task.phrase_with(recurrence=True),
            icon=icons.RECURRENCE)

        title = 'Change the reminder' if task.reminder_date else 'Set a reminder'
        wf.add_item(
            title,
            '"remind me" followed by a time and/or date, e.g. remind me at noon; r 10am; alarm 8:45p',
            autocomplete=' ' + task.phrase_with(reminder_date=True),
            icon=icons.REMINDER)

        if task.starred:
            wf.add_item('Remove star',
                        'Remove * from the task',
                        autocomplete=' ' + task.phrase_with(starred=False),
                        icon=icons.STAR_REMOVE)
        else:
            wf.add_item('Star',
                        'End the task with * (asterisk)',
                        autocomplete=' ' + task.phrase_with(starred=True),
                        icon=icons.STAR)

        wf.add_item('Main menu', autocomplete='', icon=icons.BACK)
コード例 #28
0
 def prerelease_channel(self, prerelease_channel):
     workflow().settings[PRERELEASES_KEY] = prerelease_channel
コード例 #29
0
#!/usr/bin/python
# encoding: utf-8

import logging
from logging.config import fileConfig
import sys

# fileConfig('/Users/johan/Documents/Programming/Alfred workflows/alfred-mstodo-workflow/src/logging_config.ini') #@TODO switch this before pushing to Github
fileConfig('logging_config.ini')

from mstodo.handlers.route import route
from mstodo.util import workflow

log = logging.getLogger('mstodo')

def main(wf):
    route(wf.args)
    log.info('Workflow response complete')

if __name__ == '__main__':
    wf = workflow()
    sys.exit(wf.run(main, text_errors='--commit' in wf.args))
コード例 #30
0
 def current_prefs(cls):
     if not cls._current_prefs:
         cls._current_prefs = Preferences(workflow().stored_data('prefs'))
     if not cls._current_prefs:
         cls._current_prefs = Preferences({})
     return cls._current_prefs