Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Use `dataclass-wizard` for object mapping
- Update SDK to use the Todoist API v1 and corresponding changes
- Remove deprecated `Task.sync_id`, `Task.comment_count`, and `Project.comment_count`
- Replace `Task.is_completed` with `Task.completed_at`
- Add support for `calendar` in `Project.view_style`
- Rename `quick_add_task` to `add_task_quick`
- Add `filter_tasks`, extracting that workflow from `get_tasks`
- Paginate results via an `Iterator` in `get_tasks`, `filter_task`, `get_projects`,
`get_collaborators`, `get_sections`, `get_comments`, `get_labels`, `get_shared_labels`
- Remove support for `X-Request-Id` header, unused on the API level
- Improve type hints and documentation
- Hide internal modules and functions
- Support for `note`, `reminder`, and `auto_reminder` in `add_task_quick`

### Fixes

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ It is best to integrate to a release tag to ensure a stable dependency:
```toml
dependencies = [
...
"todoist-api-python>=8.0.0,<9",
"todoist-api-python>=3.0.0,<4",
...
]
```
Expand All @@ -27,20 +27,20 @@ An example of initializing the API client and fetching a user's tasks:
from todoist_api_python.api_async import TodoistAPIAsync
from todoist_api_python.api import TodoistAPI

# Fetch tasks asynchronously
async def get_tasks_async():
api = TodoistAPIAsync("YOURTOKEN")
# Fetch tasks synchronously
def get_tasks_sync():
api = TodoistAPI("my token")
try:
tasks = await api.get_tasks()
tasks = api.get_tasks()
print(tasks)
except Exception as error:
print(error)

# Fetch tasks synchronously
def get_tasks_sync():
api = TodoistAPI("my token")
# Fetch tasks asynchronously
async def get_tasks_async():
api = TodoistAPIAsync("YOURTOKEN")
try:
tasks = api.get_tasks()
tasks = await api.get_tasks()
print(tasks)
except Exception as error:
print(error)
Expand Down
20 changes: 11 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
]

dependencies = ["requests>=2.32.3,<3", "dataclass-wizard>=0.35.0,<1.0"]
dependencies = [
"requests>=2.32.3,<3",
"dataclass-wizard>=0.35.0,<1.0",
"annotated-types",
]

[project.urls]
Homepage = "https://github.com/Doist/todoist-api-python"
Expand Down Expand Up @@ -118,16 +122,14 @@ ignore = [
"D203", # incorrect-blank-line-before-class
"D212", # multi-line-summary-first-line
"PLR0913", # too-many-arguments
"TRY00", # raise-vanilla-args

# All listed below are not intentional and should be fixed
"D100", # undocumented-public-module
"D101", # undocumented-public-class
"D102", # undocumented-public-method
"D103", # undocumented-public-function
"D104", # undocumented-public-package
"D105", # undocumented-magic-method
"D107", # undocumented-public-init
"ANN003", # missing-type-kwargs
"D100", # undocumented-public-module
"D101", # undocumented-public-class
"D102", # undocumented-public-method
"D103", # undocumented-public-function
"D104", # undocumented-public-package
]

[tool.ruff.lint.extend-per-file-ignores]
Expand Down
86 changes: 51 additions & 35 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,31 @@
import pytest
import responses

from tests.data.quick_add_responses import QUICK_ADD_RESPONSE_FULL
from tests.data.test_defaults import (
DEFAULT_AUTH_RESPONSE,
DEFAULT_COLLABORATORS_RESPONSE,
DEFAULT_COMMENT_RESPONSE,
DEFAULT_COMMENTS_RESPONSE,
DEFAULT_COMPLETED_ITEMS_RESPONSE,
DEFAULT_LABEL_RESPONSE,
DEFAULT_LABELS_RESPONSE,
DEFAULT_PROJECT_RESPONSE,
DEFAULT_PROJECTS_RESPONSE,
DEFAULT_SECTION_RESPONSE,
DEFAULT_SECTIONS_RESPONSE,
DEFAULT_TASK_META_RESPONSE,
DEFAULT_TASK_RESPONSE,
DEFAULT_TASKS_RESPONSE,
DEFAULT_TOKEN,
PaginatedResults,
)
from todoist_api_python.api import TodoistAPI
from todoist_api_python.api_async import TodoistAPIAsync
from todoist_api_python.models import (
AuthResult,
Collaborator,
Comment,
CompletedItems,
Label,
Project,
QuickAddResult,
Section,
Task,
)
Expand Down Expand Up @@ -61,19 +59,32 @@ def default_task_response() -> dict[str, Any]:
return DEFAULT_TASK_RESPONSE


@pytest.fixture
def default_task_meta() -> Task:
return Task.from_dict(DEFAULT_TASK_META_RESPONSE)


@pytest.fixture
def default_task_meta_response() -> dict[str, Any]:
return DEFAULT_TASK_META_RESPONSE


@pytest.fixture
def default_task() -> Task:
return Task.from_dict(DEFAULT_TASK_RESPONSE)


@pytest.fixture
def default_tasks_response() -> list[dict[str, Any]]:
def default_tasks_response() -> list[PaginatedResults]:
return DEFAULT_TASKS_RESPONSE


@pytest.fixture
def default_tasks_list() -> list[Task]:
return [Task.from_dict(obj) for obj in DEFAULT_TASKS_RESPONSE]
def default_tasks_list() -> list[list[Task]]:
return [
[Task.from_dict(result) for result in response["results"]]
for response in DEFAULT_TASKS_RESPONSE
]


@pytest.fixture
Expand All @@ -87,23 +98,29 @@ def default_project() -> Project:


@pytest.fixture
def default_projects_response() -> list[dict[str, Any]]:
def default_projects_response() -> list[PaginatedResults]:
return DEFAULT_PROJECTS_RESPONSE


@pytest.fixture
def default_projects_list() -> list[Project]:
return [Project.from_dict(obj) for obj in DEFAULT_PROJECTS_RESPONSE]
def default_projects_list() -> list[list[Project]]:
return [
[Project.from_dict(result) for result in response["results"]]
for response in DEFAULT_PROJECTS_RESPONSE
]


@pytest.fixture
def default_collaborators_response() -> list[dict[str, Any]]:
def default_collaborators_response() -> list[PaginatedResults]:
return DEFAULT_COLLABORATORS_RESPONSE


@pytest.fixture
def default_collaborators_list() -> list[Collaborator]:
return [Collaborator.from_dict(obj) for obj in DEFAULT_COLLABORATORS_RESPONSE]
def default_collaborators_list() -> list[list[Collaborator]]:
return [
[Collaborator.from_dict(result) for result in response["results"]]
for response in DEFAULT_COLLABORATORS_RESPONSE
]


@pytest.fixture
Expand All @@ -117,13 +134,16 @@ def default_section() -> Section:


@pytest.fixture
def default_sections_response() -> list[dict[str, Any]]:
def default_sections_response() -> list[PaginatedResults]:
return DEFAULT_SECTIONS_RESPONSE


@pytest.fixture
def default_sections_list() -> list[Section]:
return [Section.from_dict(obj) for obj in DEFAULT_SECTIONS_RESPONSE]
def default_sections_list() -> list[list[Section]]:
return [
[Section.from_dict(result) for result in response["results"]]
for response in DEFAULT_SECTIONS_RESPONSE
]


@pytest.fixture
Expand All @@ -137,13 +157,16 @@ def default_comment() -> Comment:


@pytest.fixture
def default_comments_response() -> list[dict[str, Any]]:
def default_comments_response() -> list[PaginatedResults]:
return DEFAULT_COMMENTS_RESPONSE


@pytest.fixture
def default_comments_list() -> list[Comment]:
return [Comment.from_dict(obj) for obj in DEFAULT_COMMENTS_RESPONSE]
def default_comments_list() -> list[list[Comment]]:
return [
[Comment.from_dict(result) for result in response["results"]]
for response in DEFAULT_COMMENTS_RESPONSE
]


@pytest.fixture
Expand All @@ -157,23 +180,26 @@ def default_label() -> Label:


@pytest.fixture
def default_labels_response() -> list[dict[str, Any]]:
def default_labels_response() -> list[PaginatedResults]:
return DEFAULT_LABELS_RESPONSE


@pytest.fixture
def default_labels_list() -> list[Label]:
return [Label.from_dict(obj) for obj in DEFAULT_LABELS_RESPONSE]
def default_labels_list() -> list[list[Label]]:
return [
[Label.from_dict(result) for result in response["results"]]
for response in DEFAULT_LABELS_RESPONSE
]


@pytest.fixture
def default_quick_add_response() -> dict[str, Any]:
return QUICK_ADD_RESPONSE_FULL
return DEFAULT_TASK_RESPONSE


@pytest.fixture
def default_quick_add_result() -> QuickAddResult:
return QuickAddResult.from_quick_add_response(QUICK_ADD_RESPONSE_FULL)
def default_quick_add_result() -> Task:
return Task.from_dict(DEFAULT_TASK_RESPONSE)


@pytest.fixture
Expand All @@ -184,13 +210,3 @@ def default_auth_response() -> dict[str, Any]:
@pytest.fixture
def default_auth_result() -> AuthResult:
return AuthResult.from_dict(DEFAULT_AUTH_RESPONSE)


@pytest.fixture
def default_completed_items_response() -> dict[str, Any]:
return DEFAULT_COMPLETED_ITEMS_RESPONSE


@pytest.fixture
def default_completed_items() -> CompletedItems:
return CompletedItems.from_dict(DEFAULT_COMPLETED_ITEMS_RESPONSE)
95 changes: 0 additions & 95 deletions tests/data/quick_add_responses.py

This file was deleted.

Loading