dida365


Namedida365 JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummaryUnofficial Python client for Dida365/TickTick API - Community-maintained package for task automation
upload_time2025-01-18 13:31:15
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords automation dida365 task-management ticktick unofficial-api
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Dida365/TickTick API Client

An unofficial Python client library for the Dida365/TickTick API, supporting both the Chinese (Dida365) and international (TickTick) versions of the service. Built with modern async Python and robust error handling.

This is a package created to facilitate task management automation. It is not affiliated with or endorsed by Dida365 or TickTick.

## API Documentation References

- Dida365 API: https://developer.dida365.com/api#/openapi
- TickTick API: https://developer.ticktick.com/docs#/openapi

## OAuth2 Setup

1. Get your OAuth2 credentials:
   - For TickTick: Visit https://developer.ticktick.com/manage
   - For Dida365: Visit https://developer.dida365.com/manage
   - Click "New App" to create a new application
   - After creation, you'll receive your Client ID and Client Secret

2. Configure OAuth2 redirect URL:
   - In your Manage App page, click "Edit" of your newly created app
   - Add the redirect URL: `http://localhost:8080/callback` at "OAuth redirect URL"
   - Save the changes
   - Note: If you want to use a different redirect URL, make sure to update it in both:
     - The app settings on TickTick/Dida365 developer portal
     - Your .env file (see below)

3. Configure your credentials:
   ```bash
   # .env file
   DIDA365_CLIENT_ID=your_client_id        # From step 1
   DIDA365_CLIENT_SECRET=your_client_secret # From step 1
   DIDA365_REDIRECT_URI=http://localhost:8080/callback  # From step 2
   DIDA365_SERVICE_TYPE=ticktick  # or dida365
   ```

## Features

- ✨ Full async support using `httpx`
- 🔒 OAuth2 authentication with automatic token management
- 📝 Type-safe with Pydantic v2 models
- 🌐 Configurable endpoints (Dida365/TickTick)
- 🛡️ Comprehensive error handling
- ⚡ Automatic retry mechanism
- 🔄 Environment file integration
- 📊 State management for tasks and projects

## Installation

```bash
pip install dida365
```

## Quick Start

```python
import asyncio
from datetime import datetime, timezone
from dida365 import Dida365Client, ServiceType, TaskCreate, ProjectCreate, TaskPriority

async def main():
    # Initialize client (credentials can also be loaded from .env file)
    client = Dida365Client(
        client_id="your_client_id",  # Optional if in .env
        client_secret="your_client_secret",  # Optional if in .env
        service_type=ServiceType.TICKTICK,  # or DIDA365
        redirect_uri="http://localhost:8080/callback",  # Optional
        save_to_env=True  # Automatically save credentials and tokens to .env
    )

    # First-time authentication:
    if not client.auth.token:
        # This will start a local server at the redirect_uri
        # and open your browser for authorization
        await client.authenticate()
        # Token will be automatically saved to .env if save_to_env=True

    # Create a project
    project = await client.create_project(
        ProjectCreate(
            name="My Project",
            color="#FF0000"
        )
    )

    # Create a task
    task = await client.create_task(
        TaskCreate(
            project_id=project.id,
            title="My new task",
            content="Task description",
            priority=TaskPriority.HIGH,
            start_date=datetime.now(timezone.utc),
            is_all_day=False,
            time_zone="UTC"
        )
    )
    
    print(f"Created task: {task.title}")

if __name__ == "__main__":
    asyncio.run(main())
```

## CRUD Operations

### Projects

```python
from dida365 import ProjectCreate, ProjectUpdate, ViewMode, ProjectKind, Dida365Client

async def manage_projects(client: Dida365Client):
    # Create a project
    project = await client.create_project(
        ProjectCreate(
            name="My Project",
            color="#FF0000",  # Optional: hex color code
            view_mode=ViewMode.KANBAN,  # Optional: LIST, KANBAN, TIMELINE
            kind=ProjectKind.TASK  # Optional: TASK, NOTE
        )
    )

    # Get project we just created
    project = await client.get_project(project_id=project.id)

    # Get project with all tasks and columns
    project_data = await client.get_project_with_data(project_id=project.id)
    print(f"Project {project_data.project.name} has {len(project_data.tasks)} tasks")
    for column in project_data.columns:  # Only present in KANBAN view
        print(f"Column: {column.name}")

    # Update project
    updated_project = await client.update_project(
        ProjectUpdate(
            id=project.id,
            name="Updated Project Name",
            color="#00FF00",
            view_mode=ViewMode.LIST
        )
    )

    # Delete project
    await client.delete_project(project_id=project.id)

    # List all projects
    projects = await client.get_projects()
    for project in projects:
        print(f"Project: {project.name} ({project.id})")
```

### Tasks

```python
from datetime import datetime, timezone
from dida365 import TaskCreate, TaskUpdate, TaskPriority, Dida365Client, Project

async def manage_tasks(client: Dida365Client, project: Project):
    # Create a task
    task = await client.create_task(
        TaskCreate(
            project_id=project.id,  # Required: tasks must belong to a project
            title="Complete documentation",
            content="Add CRUD examples",
            priority=TaskPriority.HIGH,  # Enum: NONE, LOW, MEDIUM, HIGH
            start_date=datetime.now(timezone.utc),
            due_date=datetime.now(timezone.utc),
            is_all_day=True,
            time_zone="UTC"
        )
    )

    # Read a task
    task = await client.get_task(project_id=project.id, task_id=task.id)

    # Update a task
    updated_task = await client.update_task(
        TaskUpdate(
            id=task.id,
            project_id=task.project_id,  # Both id and project_id are required
            title="Updated title",
            content="Added more details",
            priority=TaskPriority.MEDIUM
        )
    )

    # Complete a task
    await client.complete_task(project_id=task.project_id, task_id=task.id)
    
    # Delete a task
    await client.delete_task(project_id=task.project_id, task_id=task.id)
```

### State Management

The client maintains an internal state of tasks and projects:

```python
# Access cached state
tasks = client.state["tasks"]
projects = client.state["projects"]
tags = client.state["tags"]

# State is automatically updated when you:
# 1. Create new items
# 2. Update existing items
# 3. Delete items
```

## Configuration

The client can be configured through:
1. Environment variables
2. Constructor parameters
3. pyproject.toml settings

### Environment Variables

Create a `.env` file:
```bash
# Required credentials
DIDA365_CLIENT_ID=your_client_id
DIDA365_CLIENT_SECRET=your_client_secret

# Optional configurations
DIDA365_SERVICE_TYPE=ticktick     # or dida365 (default)
DIDA365_ACCESS_TOKEN=your_token   # Will be saved automatically after auth
DIDA365_BASE_URL=custom_url      # Optional: custom API endpoint
DIDA365_LOG_LEVEL=INFO          # Optional: DEBUG, INFO, WARNING, ERROR
```

You can also use a custom `.env` file location:
```python
from dotenv import load_dotenv

load_dotenv("/path/to/your/.env")
client = Dida365Client()  # Will load from the specified .env file
```

### Request Timeouts

Configure request timeouts in `pyproject.toml`:
```toml
[tool.dida365.request_timeout]
connect = 10.0  # Connection timeout
read = 30.0     # Read timeout
write = 30.0    # Write timeout
pool = 5.0      # Pool timeout
```

### Logging Configuration

```toml
[tool.dida365]
log_level = "INFO"
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
log_date_format = "%Y-%m-%d %H:%M:%S"
log_file = ""  # Set to a path to enable file logging
debug = false
```

## Error Handling

The library provides detailed error handling:

```python
from dida365.exceptions import (
    ApiError,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError
)

try:
    task = await client.get_task("project_id", "task_id")
except NotFoundError:
    print("Task not found")
except AuthenticationError:
    print("Authentication failed - token may have expired")
except RateLimitError as e:
    print(f"Rate limit exceeded. Retry after {e.retry_after} seconds")
except ValidationError as e:
    print(f"Invalid data: {e}")
except ApiError as e:
    print(f"API error: {e.status_code} - {e.message}")
```

## Author

Carter Yifeng Cheng ([@cyfine](https://github.com/cyfine))

## License

This project is licensed under the MIT License - see the LICENSE file for details. 
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "dida365",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "automation, dida365, task-management, ticktick, unofficial-api",
    "author": null,
    "author_email": "Carter Yifeng Cheng <cyfine@github.com>",
    "download_url": "https://files.pythonhosted.org/packages/d6/34/5b3e5861009a58a8950da14f999c21ff9d5b785cc525113ce947a100c089/dida365-0.1.2.tar.gz",
    "platform": null,
    "description": "# Dida365/TickTick API Client\n\nAn unofficial Python client library for the Dida365/TickTick API, supporting both the Chinese (Dida365) and international (TickTick) versions of the service. Built with modern async Python and robust error handling.\n\nThis is a package created to facilitate task management automation. It is not affiliated with or endorsed by Dida365 or TickTick.\n\n## API Documentation References\n\n- Dida365 API: https://developer.dida365.com/api#/openapi\n- TickTick API: https://developer.ticktick.com/docs#/openapi\n\n## OAuth2 Setup\n\n1. Get your OAuth2 credentials:\n   - For TickTick: Visit https://developer.ticktick.com/manage\n   - For Dida365: Visit https://developer.dida365.com/manage\n   - Click \"New App\" to create a new application\n   - After creation, you'll receive your Client ID and Client Secret\n\n2. Configure OAuth2 redirect URL:\n   - In your Manage App page, click \"Edit\" of your newly created app\n   - Add the redirect URL: `http://localhost:8080/callback` at \"OAuth redirect URL\"\n   - Save the changes\n   - Note: If you want to use a different redirect URL, make sure to update it in both:\n     - The app settings on TickTick/Dida365 developer portal\n     - Your .env file (see below)\n\n3. Configure your credentials:\n   ```bash\n   # .env file\n   DIDA365_CLIENT_ID=your_client_id        # From step 1\n   DIDA365_CLIENT_SECRET=your_client_secret # From step 1\n   DIDA365_REDIRECT_URI=http://localhost:8080/callback  # From step 2\n   DIDA365_SERVICE_TYPE=ticktick  # or dida365\n   ```\n\n## Features\n\n- \u2728 Full async support using `httpx`\n- \ud83d\udd12 OAuth2 authentication with automatic token management\n- \ud83d\udcdd Type-safe with Pydantic v2 models\n- \ud83c\udf10 Configurable endpoints (Dida365/TickTick)\n- \ud83d\udee1\ufe0f Comprehensive error handling\n- \u26a1 Automatic retry mechanism\n- \ud83d\udd04 Environment file integration\n- \ud83d\udcca State management for tasks and projects\n\n## Installation\n\n```bash\npip install dida365\n```\n\n## Quick Start\n\n```python\nimport asyncio\nfrom datetime import datetime, timezone\nfrom dida365 import Dida365Client, ServiceType, TaskCreate, ProjectCreate, TaskPriority\n\nasync def main():\n    # Initialize client (credentials can also be loaded from .env file)\n    client = Dida365Client(\n        client_id=\"your_client_id\",  # Optional if in .env\n        client_secret=\"your_client_secret\",  # Optional if in .env\n        service_type=ServiceType.TICKTICK,  # or DIDA365\n        redirect_uri=\"http://localhost:8080/callback\",  # Optional\n        save_to_env=True  # Automatically save credentials and tokens to .env\n    )\n\n    # First-time authentication:\n    if not client.auth.token:\n        # This will start a local server at the redirect_uri\n        # and open your browser for authorization\n        await client.authenticate()\n        # Token will be automatically saved to .env if save_to_env=True\n\n    # Create a project\n    project = await client.create_project(\n        ProjectCreate(\n            name=\"My Project\",\n            color=\"#FF0000\"\n        )\n    )\n\n    # Create a task\n    task = await client.create_task(\n        TaskCreate(\n            project_id=project.id,\n            title=\"My new task\",\n            content=\"Task description\",\n            priority=TaskPriority.HIGH,\n            start_date=datetime.now(timezone.utc),\n            is_all_day=False,\n            time_zone=\"UTC\"\n        )\n    )\n    \n    print(f\"Created task: {task.title}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## CRUD Operations\n\n### Projects\n\n```python\nfrom dida365 import ProjectCreate, ProjectUpdate, ViewMode, ProjectKind, Dida365Client\n\nasync def manage_projects(client: Dida365Client):\n    # Create a project\n    project = await client.create_project(\n        ProjectCreate(\n            name=\"My Project\",\n            color=\"#FF0000\",  # Optional: hex color code\n            view_mode=ViewMode.KANBAN,  # Optional: LIST, KANBAN, TIMELINE\n            kind=ProjectKind.TASK  # Optional: TASK, NOTE\n        )\n    )\n\n    # Get project we just created\n    project = await client.get_project(project_id=project.id)\n\n    # Get project with all tasks and columns\n    project_data = await client.get_project_with_data(project_id=project.id)\n    print(f\"Project {project_data.project.name} has {len(project_data.tasks)} tasks\")\n    for column in project_data.columns:  # Only present in KANBAN view\n        print(f\"Column: {column.name}\")\n\n    # Update project\n    updated_project = await client.update_project(\n        ProjectUpdate(\n            id=project.id,\n            name=\"Updated Project Name\",\n            color=\"#00FF00\",\n            view_mode=ViewMode.LIST\n        )\n    )\n\n    # Delete project\n    await client.delete_project(project_id=project.id)\n\n    # List all projects\n    projects = await client.get_projects()\n    for project in projects:\n        print(f\"Project: {project.name} ({project.id})\")\n```\n\n### Tasks\n\n```python\nfrom datetime import datetime, timezone\nfrom dida365 import TaskCreate, TaskUpdate, TaskPriority, Dida365Client, Project\n\nasync def manage_tasks(client: Dida365Client, project: Project):\n    # Create a task\n    task = await client.create_task(\n        TaskCreate(\n            project_id=project.id,  # Required: tasks must belong to a project\n            title=\"Complete documentation\",\n            content=\"Add CRUD examples\",\n            priority=TaskPriority.HIGH,  # Enum: NONE, LOW, MEDIUM, HIGH\n            start_date=datetime.now(timezone.utc),\n            due_date=datetime.now(timezone.utc),\n            is_all_day=True,\n            time_zone=\"UTC\"\n        )\n    )\n\n    # Read a task\n    task = await client.get_task(project_id=project.id, task_id=task.id)\n\n    # Update a task\n    updated_task = await client.update_task(\n        TaskUpdate(\n            id=task.id,\n            project_id=task.project_id,  # Both id and project_id are required\n            title=\"Updated title\",\n            content=\"Added more details\",\n            priority=TaskPriority.MEDIUM\n        )\n    )\n\n    # Complete a task\n    await client.complete_task(project_id=task.project_id, task_id=task.id)\n    \n    # Delete a task\n    await client.delete_task(project_id=task.project_id, task_id=task.id)\n```\n\n### State Management\n\nThe client maintains an internal state of tasks and projects:\n\n```python\n# Access cached state\ntasks = client.state[\"tasks\"]\nprojects = client.state[\"projects\"]\ntags = client.state[\"tags\"]\n\n# State is automatically updated when you:\n# 1. Create new items\n# 2. Update existing items\n# 3. Delete items\n```\n\n## Configuration\n\nThe client can be configured through:\n1. Environment variables\n2. Constructor parameters\n3. pyproject.toml settings\n\n### Environment Variables\n\nCreate a `.env` file:\n```bash\n# Required credentials\nDIDA365_CLIENT_ID=your_client_id\nDIDA365_CLIENT_SECRET=your_client_secret\n\n# Optional configurations\nDIDA365_SERVICE_TYPE=ticktick     # or dida365 (default)\nDIDA365_ACCESS_TOKEN=your_token   # Will be saved automatically after auth\nDIDA365_BASE_URL=custom_url      # Optional: custom API endpoint\nDIDA365_LOG_LEVEL=INFO          # Optional: DEBUG, INFO, WARNING, ERROR\n```\n\nYou can also use a custom `.env` file location:\n```python\nfrom dotenv import load_dotenv\n\nload_dotenv(\"/path/to/your/.env\")\nclient = Dida365Client()  # Will load from the specified .env file\n```\n\n### Request Timeouts\n\nConfigure request timeouts in `pyproject.toml`:\n```toml\n[tool.dida365.request_timeout]\nconnect = 10.0  # Connection timeout\nread = 30.0     # Read timeout\nwrite = 30.0    # Write timeout\npool = 5.0      # Pool timeout\n```\n\n### Logging Configuration\n\n```toml\n[tool.dida365]\nlog_level = \"INFO\"\nlog_format = \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\nlog_date_format = \"%Y-%m-%d %H:%M:%S\"\nlog_file = \"\"  # Set to a path to enable file logging\ndebug = false\n```\n\n## Error Handling\n\nThe library provides detailed error handling:\n\n```python\nfrom dida365.exceptions import (\n    ApiError,\n    AuthenticationError,\n    NotFoundError,\n    RateLimitError,\n    ValidationError\n)\n\ntry:\n    task = await client.get_task(\"project_id\", \"task_id\")\nexcept NotFoundError:\n    print(\"Task not found\")\nexcept AuthenticationError:\n    print(\"Authentication failed - token may have expired\")\nexcept RateLimitError as e:\n    print(f\"Rate limit exceeded. Retry after {e.retry_after} seconds\")\nexcept ValidationError as e:\n    print(f\"Invalid data: {e}\")\nexcept ApiError as e:\n    print(f\"API error: {e.status_code} - {e.message}\")\n```\n\n## Author\n\nCarter Yifeng Cheng ([@cyfine](https://github.com/cyfine))\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details. ",
    "bugtrack_url": null,
    "license": null,
    "summary": "Unofficial Python client for Dida365/TickTick API - Community-maintained package for task automation",
    "version": "0.1.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/Cyfine/TickTick-Dida365-API-Client/issues",
        "Changelog": "https://github.com/Cyfine/TickTick-Dida365-API-Client/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/Cyfine/TickTick-Dida365-API-Client#readme",
        "Homepage": "https://github.com/Cyfine/TickTick-Dida365-API-Client"
    },
    "split_keywords": [
        "automation",
        " dida365",
        " task-management",
        " ticktick",
        " unofficial-api"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4acd3b7513a4dd7d97ddb4c040c49c979f87e09971173f3948919cb8963bebdb",
                "md5": "7696b4f78717b35449e129a8eba0d90e",
                "sha256": "078338a79c4c212081adc2dbd1c1028523d7501faf1d1130ffa5c1a91d3fe3e0"
            },
            "downloads": -1,
            "filename": "dida365-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7696b4f78717b35449e129a8eba0d90e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 20866,
            "upload_time": "2025-01-18T13:31:11",
            "upload_time_iso_8601": "2025-01-18T13:31:11.968035Z",
            "url": "https://files.pythonhosted.org/packages/4a/cd/3b7513a4dd7d97ddb4c040c49c979f87e09971173f3948919cb8963bebdb/dida365-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d6345b3e5861009a58a8950da14f999c21ff9d5b785cc525113ce947a100c089",
                "md5": "4023420e2d618dafc414fdc87e9c456c",
                "sha256": "d804a500d62d2d8328ff84f073dd3c442ce39b6d82d2bc96d7cc8a26517b40f8"
            },
            "downloads": -1,
            "filename": "dida365-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "4023420e2d618dafc414fdc87e9c456c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 18218,
            "upload_time": "2025-01-18T13:31:15",
            "upload_time_iso_8601": "2025-01-18T13:31:15.203452Z",
            "url": "https://files.pythonhosted.org/packages/d6/34/5b3e5861009a58a8950da14f999c21ff9d5b785cc525113ce947a100c089/dida365-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-01-18 13:31:15",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Cyfine",
    "github_project": "TickTick-Dida365-API-Client",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "dida365"
}
        
Elapsed time: 0.48862s