Skip to content

Task lifecycle — create, update, complete, reopen, and delete a task.

A todoTask carries more than a title: a due date, start date, importance, body, categories, a reminder, and an optional recurrence. This script walks the whole lifecycle of one task, printing it at every step, and cleans up unless --keep is given.

python lifecycle.py
python lifecycle.py --title "Water the plants" --due-in 3 --importance high --repeat weekly

Permissions

Requires delegated permission Tasks.ReadWrite for the signed-in user.

Reference

View source

from __future__ import annotations

import argparse
from datetime import date, datetime, timedelta, timezone

from office365.graph_client import GraphClient
from office365.outlook.calendar.dateTimeTimeZone import DateTimeTimeZone
from office365.outlook.calendar.dayofweek import DayOfWeek
from office365.outlook.mail.importance import Importance
from office365.outlook.mail.patterned_recurrence import PatternedRecurrence
from office365.outlook.mail.recurrence_pattern import RecurrencePattern
from office365.outlook.mail.recurrence_range import RecurrenceRange
from office365.outlook.mail.recurrencepatterntype import RecurrencePatternType
from office365.outlook.mail.recurrencerangetype import RecurrenceRangeType
from office365.runtime.client_request_exception import ClientRequestException
from office365.todo.tasks.taskstatus import TaskStatus
from tests.settings import client_id, password, tenant, username

LIST_NAME = "SDK Tasks Demo"
PERMISSION_CODES = {"accessDenied", "Authorization_RequestDenied", "ErrorAccessDenied", "Forbidden"}
_REPEAT_TYPES = {
    "daily": RecurrencePatternType.daily,
    "weekly": RecurrencePatternType.weekly,
    "monthly": RecurrencePatternType.absoluteMonthly,
    "yearly": RecurrencePatternType.absoluteYearly,
}


def _client() -> GraphClient:
    return GraphClient(tenant=tenant).with_username_and_password(client_id, username, password)


def _recurrence(kind: str, start: date) -> PatternedRecurrence:
    pattern = RecurrencePattern(type=_REPEAT_TYPES[kind], interval=1)
    if kind == "weekly":
        pattern.daysOfWeek.add(DayOfWeek(start.strftime("%w")))
    elif kind == "monthly":
        pattern.dayOfMonth = start.day
    elif kind == "yearly":
        pattern.dayOfMonth = start.day
        pattern.month = start.month
    recurrence_range = RecurrenceRange(type=RecurrenceRangeType.noEnd, startDate=start)
    return PatternedRecurrence(pattern=pattern, range=recurrence_range)


def _summarize(task) -> str:
    importance = task.importance.name if task.importance else "-"
    status = task.status.name if task.status else "-"
    due = task.due_date_time.dateTime or "-"
    return f"title={task.title!r} status={status} importance={importance} due={due}"


def main() -> None:
    parser = argparse.ArgumentParser(description="Microsoft To Do task lifecycle")
    parser.add_argument("--title", default="Write the sprint plan", help="task title")
    parser.add_argument("--due-in", type=int, default=2, help="due in this many days (default: 2)")
    parser.add_argument(
        "--importance", choices=[item.name for item in Importance], default="high", help="task importance"
    )
    parser.add_argument("--categories", nargs="*", default=["Work"], help="category labels")
    parser.add_argument("--repeat", choices=[*_REPEAT_TYPES, "none"], default="none", help="recurrence pattern")
    parser.add_argument("--keep", action="store_true", help="keep the list and task instead of deleting them")
    args = parser.parse_args()

    client = _client()
    try:
        task_list = client.me.todo.lists.ensure(LIST_NAME).execute_query()

        now = datetime.now(timezone.utc)
        start = now + timedelta(hours=1)
        extra = {"startDateTime": DateTimeTimeZone.parse(start), "categories": args.categories}
        if args.repeat != "none":
            extra["recurrence"] = _recurrence(args.repeat, start.date())

        task = task_list.tasks.add(
            args.title,
            due_date_time=now + timedelta(days=args.due_in),
            importance=Importance[args.importance],
            body="Draft the goals, then share with the team.",
            **extra,
        ).execute_query()
        print(f"Created: {_summarize(task)}")
        if args.repeat != "none":
            print(f"         repeats {args.repeat}")

        task.title = f"{args.title} (v2)"
        task.set_property("importance", Importance.normal)
        task.update().execute_query()
        print(f"Updated: {_summarize(task)}")

        task.status = TaskStatus.completed
        task.set_property("completedDateTime", DateTimeTimeZone.parse(datetime.now(timezone.utc)))
        task.update().execute_query()
        print(f"Completed: {_summarize(task)}")

        task.status = TaskStatus.notStarted
        task.set_property("completedDateTime", None)
        task.update().execute_query()
        print(f"Reopened: {_summarize(task)}")

        if args.keep:
            print("Keeping the list and task (--keep).")
            return
        task.delete_object().execute_query()
        client.me.todo.lists[task_list.id].delete_object().execute_query()
        print("Cleaned up the demo list and task.")
    except ClientRequestException as exc:
        if exc.code not in PERMISSION_CODES:
            raise
        print(f"To Do request failed: {exc.code}: {exc.message}")
        print("Grant and consent the delegated permission Tasks.ReadWrite for the signed-in user, then re-run.")
        raise SystemExit(0) from None


if __name__ == "__main__":
    main()

← Back to Tasks