Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get completed items #90

Merged
merged 1 commit into from
Aug 15, 2023
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 tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
DEFAULT_COLLABORATORS_RESPONSE,
DEFAULT_COMMENT_RESPONSE,
DEFAULT_COMMENTS_RESPONSE,
DEFAULT_COMPLETED_ITEMS_RESPONSE,
DEFAULT_LABEL_RESPONSE,
DEFAULT_LABELS_RESPONSE,
DEFAULT_PROJECT_RESPONSE,
Expand All @@ -25,6 +26,7 @@
AuthResult,
Collaborator,
Comment,
CompletedItems,
Label,
Project,
QuickAddResult,
Expand Down Expand Up @@ -177,3 +179,13 @@ 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)
30 changes: 30 additions & 0 deletions tests/data/test_defaults.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations

from typing import Any

REST_API_BASE_URL = "https://api.todoist.com/rest/v2"
Expand Down Expand Up @@ -148,3 +149,32 @@
"access_token": "1234",
"state": "somestate",
}

DEFAULT_ITEM_RESPONSE = {
"id": "2995104339",
"user_id": "2671355",
"project_id": "2203306141",
"content": "Buy Milk",
"description": "",
"priority": 1,
"due": DEFAULT_DUE_RESPONSE,
"child_order": 1,
"day_order": -1,
"collapsed": False,
"labels": ["Food", "Shopping"],
"added_by_uid": "2671355",
"assigned_by_uid": "2671355",
"checked": False,
"is_deleted": False,
"added_at": "2014-09-26T08:25:05.000000Z",
}

DEFAULT_ITEM_COMPLETED_INFO_RESPONSE = {"item_id": "2995104339", "completed_items": 12}

DEFAULT_COMPLETED_ITEMS_RESPONSE = {
"items": [DEFAULT_ITEM_RESPONSE],
"completed_info": [DEFAULT_ITEM_COMPLETED_INFO_RESPONSE],
"total": 22,
"next_cursor": "k85gVI5ZAs8AAAABFoOzAQ",
"has_more": True,
}
64 changes: 64 additions & 0 deletions tests/test_api_items.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from typing import Any
from urllib.parse import parse_qs, urlparse

import pytest
import responses

from tests.data.test_defaults import SYNC_API_BASE_URL
from tests.utils.test_utils import assert_auth_header
from todoist_api_python.api import TodoistAPI
from todoist_api_python.api_async import TodoistAPIAsync
from todoist_api_python.endpoints import COMPLETED_ITEMS_ENDPOINT
from todoist_api_python.models import CompletedItems


@pytest.mark.asyncio
async def test_get_completed_items(
todoist_api: TodoistAPI,
todoist_api_async: TodoistAPIAsync,
requests_mock: responses.RequestsMock,
default_completed_items_response: dict[str, Any],
default_completed_items: CompletedItems,
) -> None:
project_id = "1234"
section_id = "5678"
item_id = "90ab"
last_seen_id = "cdef"
limit = 30
cursor = "ghij"

def assert_query(url):
queries = parse_qs(urlparse(url).query)
assert queries.get("project_id") == [project_id]
assert queries.get("section_id") == [section_id]
assert queries.get("item_id") == [item_id]
assert queries.get("last_seen_id") == [last_seen_id]
assert queries.get("limit") == [str(limit)]
assert queries.get("cursor") == [cursor]

expected_endpoint = f"{SYNC_API_BASE_URL}/{COMPLETED_ITEMS_ENDPOINT}"

requests_mock.add(
responses.GET,
expected_endpoint,
json=default_completed_items_response,
status=200,
)

completed_items = todoist_api.get_completed_items(
project_id, section_id, item_id, last_seen_id, limit, cursor
)

assert len(requests_mock.calls) == 1
assert_auth_header(requests_mock.calls[0].request)
assert_query(requests_mock.calls[0].request.url)
assert completed_items == default_completed_items

completed_items = await todoist_api_async.get_completed_items(
project_id, section_id, item_id, last_seen_id, limit, cursor
)

assert len(requests_mock.calls) == 2
assert_auth_header(requests_mock.calls[1].request)
assert_query(requests_mock.calls[1].request.url)
assert completed_items == default_completed_items
90 changes: 90 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
DEFAULT_ATTACHMENT_RESPONSE,
DEFAULT_COLLABORATOR_RESPONSE,
DEFAULT_COMMENT_RESPONSE,
DEFAULT_COMPLETED_ITEMS_RESPONSE,
DEFAULT_DUE_RESPONSE,
DEFAULT_ITEM_COMPLETED_INFO_RESPONSE,
DEFAULT_ITEM_RESPONSE,
DEFAULT_LABEL_RESPONSE,
DEFAULT_PROJECT_RESPONSE,
DEFAULT_SECTION_RESPONSE,
Expand All @@ -17,7 +20,10 @@
AuthResult,
Collaborator,
Comment,
CompletedItems,
Due,
Item,
ItemCompletedInfo,
Label,
Project,
QuickAddResult,
Expand Down Expand Up @@ -280,3 +286,87 @@ def test_auth_result_from_dict():

assert auth_result.access_token == token
assert auth_result.state == state


def test_item_from_dict():
sample_data = dict(DEFAULT_ITEM_RESPONSE)
sample_data.update(unexpected_data)

item = Item.from_dict(sample_data)

assert item.id == "2995104339"
assert item.user_id == "2671355"
assert item.project_id == "2203306141"
assert item.content == "Buy Milk"
assert item.description == ""
assert item.priority == 1
assert item.due.date == DEFAULT_DUE_RESPONSE["date"]
assert item.due.is_recurring == DEFAULT_DUE_RESPONSE["is_recurring"]
assert item.due.string == DEFAULT_DUE_RESPONSE["string"]
assert item.due.datetime == DEFAULT_DUE_RESPONSE["datetime"]
assert item.due.timezone == DEFAULT_DUE_RESPONSE["timezone"]
assert item.parent_id is None
assert item.child_order == 1
assert item.section_id is None
assert item.day_order == -1
assert item.collapsed is False
assert item.labels == ["Food", "Shopping"]
assert item.added_by_uid == "2671355"
assert item.assigned_by_uid == "2671355"
assert item.responsible_uid is None
assert item.checked is False
assert item.is_deleted is False
assert item.sync_id is None
assert item.added_at == "2014-09-26T08:25:05.000000Z"


def test_item_completed_info_from_dict():
sample_data = dict(DEFAULT_ITEM_COMPLETED_INFO_RESPONSE)
sample_data.update(unexpected_data)

info = ItemCompletedInfo.from_dict(sample_data)

assert info.item_id == "2995104339"
assert info.completed_items == 12


def test_completed_items_from_dict():
sample_data = dict(DEFAULT_COMPLETED_ITEMS_RESPONSE)
sample_data.update(unexpected_data)

completed_items = CompletedItems.from_dict(sample_data)

assert completed_items.total == 22
assert completed_items.next_cursor == "k85gVI5ZAs8AAAABFoOzAQ"
assert completed_items.has_more is True
assert len(completed_items.items) == 1
assert completed_items.items[0].id == "2995104339"
assert completed_items.items[0].user_id == "2671355"
assert completed_items.items[0].project_id == "2203306141"
assert completed_items.items[0].content == "Buy Milk"
assert completed_items.items[0].description == ""
assert completed_items.items[0].priority == 1
assert completed_items.items[0].due.date == DEFAULT_DUE_RESPONSE["date"]
assert (
completed_items.items[0].due.is_recurring
== DEFAULT_DUE_RESPONSE["is_recurring"]
)
assert completed_items.items[0].due.string == DEFAULT_DUE_RESPONSE["string"]
assert completed_items.items[0].due.datetime == DEFAULT_DUE_RESPONSE["datetime"]
assert completed_items.items[0].due.timezone == DEFAULT_DUE_RESPONSE["timezone"]
assert completed_items.items[0].parent_id is None
assert completed_items.items[0].child_order == 1
assert completed_items.items[0].section_id is None
assert completed_items.items[0].day_order == -1
assert completed_items.items[0].collapsed is False
assert completed_items.items[0].labels == ["Food", "Shopping"]
assert completed_items.items[0].added_by_uid == "2671355"
assert completed_items.items[0].assigned_by_uid == "2671355"
assert completed_items.items[0].responsible_uid is None
assert completed_items.items[0].checked is False
assert completed_items.items[0].is_deleted is False
assert completed_items.items[0].sync_id is None
assert completed_items.items[0].added_at == "2014-09-26T08:25:05.000000Z"
assert len(completed_items.completed_info) == 1
assert completed_items.completed_info[0].item_id == "2995104339"
assert completed_items.completed_info[0].completed_items == 12
27 changes: 27 additions & 0 deletions todoist_api_python/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from todoist_api_python.endpoints import (
COLLABORATORS_ENDPOINT,
COMMENTS_ENDPOINT,
COMPLETED_ITEMS_ENDPOINT,
LABELS_ENDPOINT,
PROJECTS_ENDPOINT,
QUICK_ADD_ENDPOINT,
Expand All @@ -22,6 +23,7 @@
from todoist_api_python.models import (
Collaborator,
Comment,
CompletedItems,
Label,
Project,
QuickAddResult,
Expand Down Expand Up @@ -207,3 +209,28 @@ def remove_shared_label(self, name: str) -> bool:
endpoint = get_rest_url(SHARED_LABELS_REMOVE_ENDPOINT)
data = {"name": name}
return post(self._session, endpoint, self._token, data=data)

def get_completed_items(
self,
project_id: str | None = None,
section_id: str | None = None,
item_id: str | None = None,
last_seen_id: str | None = None,
limit: int | None = None,
cursor: str | None = None,
) -> CompletedItems:
endpoint = get_sync_url(COMPLETED_ITEMS_ENDPOINT)
completed_items = get(
self._session,
endpoint,
self._token,
{
"project_id": project_id,
"section_id": section_id,
"item_id": item_id,
"last_seen_id": last_seen_id,
"limit": limit,
"cursor": cursor,
},
)
return CompletedItems.from_dict(completed_items)
16 changes: 16 additions & 0 deletions todoist_api_python/api_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from todoist_api_python.models import (
Collaborator,
Comment,
CompletedItems,
Label,
Project,
QuickAddResult,
Expand Down Expand Up @@ -120,3 +121,18 @@ async def rename_shared_label(self, name: str, new_name: str) -> bool:

async def remove_shared_label(self, name: str) -> bool:
return await run_async(lambda: self._api.remove_shared_label(name))

async def get_completed_items(
self,
project_id: str | None = None,
section_id: str | None = None,
item_id: str | None = None,
last_seen_id: str | None = None,
limit: int | None = None,
cursor: str | None = None,
) -> CompletedItems:
return await run_async(
lambda: self._api.get_completed_items(
project_id, section_id, item_id, last_seen_id, limit, cursor
)
)
2 changes: 2 additions & 0 deletions todoist_api_python/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
TOKEN_ENDPOINT = "oauth/access_token"
REVOKE_TOKEN_ENDPOINT = "access_tokens/revoke"

COMPLETED_ITEMS_ENDPOINT = "archive/items"


def get_rest_url(relative_path: str) -> str:
return urljoin(REST_API, relative_path)
Expand Down
Loading