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

Group 10: Modified date recognization #128931

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions homeassistant/components/google_tasks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@
from homeassistant.helpers import config_entry_oauth2_flow

from . import api
from .api import AsyncConfigEntryAuth
from .const import DOMAIN
from .coordinator import TaskUpdateCoordinator
from .todo import GoogleTaskTodoListEntity

_all_ = [AsyncConfigEntryAuth, DOMAIN, TaskUpdateCoordinator, GoogleTaskTodoListEntity]

PLATFORMS: list[Platform] = [Platform.TODO]
DOMAIN = "google_tasks"


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
Expand Down
15 changes: 14 additions & 1 deletion homeassistant/components/google_tasks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,16 @@ async def patch(
task: dict[str, Any],
) -> None:
"""Update a task resource."""
if not task:
_LOGGER.debug(
"No fields to update for task %s in list %s", task_id, task_list_id
)
return

service = await self._get_service()
_LOGGER.debug(
"Patching task %s in list %s with data: %s", task_id, task_list_id, task
)
cmd: HttpRequest = service.tasks().patch(
tasklist=task_list_id,
task=task_id,
Expand Down Expand Up @@ -152,8 +161,12 @@ async def _execute(self, request: HttpRequest | BatchHttpRequest) -> Any:
try:
result = await self._hass.async_add_executor_job(request.execute)
except HttpError as err:
error_content = (
err.content.decode("utf-8") if err.content else "No error content"
)
_LOGGER.error("Google Tasks API error: %s, Content: %s", err, error_content)
raise GoogleTasksApiError(
f"Google Tasks API responded with error ({err.status_code})"
f"Google Tasks API responded with error ({err.status_code}):{error_content}"
) from err
if result:
_raise_if_error(result)
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/google_tasks/manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"domain": "google_tasks",
"name": "Google Tasks",
"codeowners": ["@allenporter"],
"config_flow": true,
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/google_tasks",
"dependencies": [],
"codeowners": [],
"requirements": ["dateparser==1.1.1"],
"iot_class": "cloud_polling",
"requirements": ["google-api-python-client==2.71.0"]
"version": "1.0.0"
}
45 changes: 33 additions & 12 deletions homeassistant/components/google_tasks/todo.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
"""Google Tasks todo platform."""

from __future__ import annotations

from datetime import date, datetime, timedelta
import re
from typing import Any, cast

from homeassistant.components.todo import (
Expand All @@ -29,29 +26,53 @@
}
TODO_STATUS_MAP_INV = {v: k for k, v in TODO_STATUS_MAP.items()}

DATE_PATTERN = r"\d{4}/\d{2}/\d{2}" # Define a regex pattern for YYYY/MM/DD format


# Helper function to ensure proper date format (ISO 8601 with UTC time)
def _format_due_date(due: date) -> str:
"""Format due date for Google Tasks API."""
return due.isoformat() + "T00:00:00.000Z" # Format the date as all-day in UTC


def _convert_todo_item(item: TodoItem) -> dict[str, str | None]:
"""Convert TodoItem dataclass items to dictionary of attributes the tasks API."""
"""Convert TodoItem dataclass items to dictionary of attributes for the tasks API."""
result: dict[str, str | None] = {}
result["title"] = item.summary
if item.status is not None:
result["status"] = TODO_STATUS_MAP_INV[item.status]
else:
result["status"] = TodoItemStatus.NEEDS_ACTION

if (due := item.due) is not None:
# due API field is a timestamp string, but with only date resolution
result["due"] = dt_util.start_of_local_day(due).isoformat()
# Ensure 'due' is treated as a date-only field (strip time) before sending to the API
result["due"] = _format_due_date(due) # Format date for Google Tasks API
else:
result["due"] = None
result["notes"] = item.description

result["notes"] = (
item.description if item.description else None
) # Avoid sending empty strings
return result


def _convert_api_item(item: dict[str, str]) -> TodoItem:
"""Convert tasks API items into a TodoItem."""
due: date | None = None
if (due_str := item.get("due")) is not None:
# Parse the date as a local date without time components
due = datetime.fromisoformat(due_str).date()
else:
# Check for custom date format (YYYY/MM/DD) in title or description
title = item.get("title", "")
notes = item.get("notes", "")

# Search for date pattern in title or description
match = re.search(DATE_PATTERN, title) or re.search(DATE_PATTERN, notes)
if match:
# Parse date in YYYY/MM/DD format
due = datetime.strptime(match.group(), "%Y/%m/%d").date()

return TodoItem(
summary=item["title"],
uid=item["id"],
Expand Down Expand Up @@ -106,8 +127,8 @@ def __init__(
config_entry_id: str,
task_list_id: str,
) -> None:
"""Initialize LocalTodoListEntity."""
super().__init__(coordinator)
"""Initialize GoogleTaskTodoListEntity."""
super().__init__(coordinator) # Corrected: only pass the coordinator
self._attr_name = name.capitalize()
self._attr_unique_id = f"{config_entry_id}-{task_list_id}"
self._task_list_id = task_list_id
Expand Down Expand Up @@ -153,9 +174,9 @@ async def async_move_todo_item(
def _order_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Order the task items response.

All tasks have an order amongst their sibblings based on position.
All tasks have an order amongst their siblings based on position.

Home Assistant To-do items do not support the Google Task parent/sibbling
Home Assistant To-do items do not support the Google Task parent/sibling
relationships and the desired behavior is for them to be filtered.
"""
parents = [task for task in tasks if task.get("parent") is None]
Expand Down
4 changes: 3 additions & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,9 @@ datadog==0.15.0
# homeassistant.components.metoffice
datapoint==0.9.9

# homeassistant.components.google_tasks
dateparser==1.1.1

# homeassistant.components.bluetooth
dbus-fast==2.24.0

Expand Down Expand Up @@ -988,7 +991,6 @@ goalzero==0.2.2
goodwe==0.3.6

# homeassistant.components.google_mail
# homeassistant.components.google_tasks
google-api-python-client==2.71.0

# homeassistant.components.google_pubsub
Expand Down
4 changes: 3 additions & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,9 @@ datadog==0.15.0
# homeassistant.components.metoffice
datapoint==0.9.9

# homeassistant.components.google_tasks
dateparser==1.1.1

# homeassistant.components.bluetooth
dbus-fast==2.24.0

Expand Down Expand Up @@ -838,7 +841,6 @@ goalzero==0.2.2
goodwe==0.3.6

# homeassistant.components.google_mail
# homeassistant.components.google_tasks
google-api-python-client==2.71.0

# homeassistant.components.google_pubsub
Expand Down
Loading