Metadata-Version: 2.4
Name: taskwatch
Version: 0.2.5
Summary: A lightweight local task scheduler with CLI, web UI, and email reporting
Author-email: Chandler <275737875@qq.com>
License-Expression: MIT
Keywords: scheduler,task,cron,automation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: typer>=0.9.0
Requires-Dist: rich>=13.0.0
Requires-Dist: fastapi>=0.100.0
Requires-Dist: uvicorn>=0.23.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: apscheduler>=3.10.0
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
Requires-Dist: tomli-w>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"

# TaskWatch

> 轻量级本地任务调度器：一条命令 + 一个调度表达式，即可定时执行任务、记录日志、统计结果，并通过邮件发送告警与日报/周报。

TaskWatch 面向个人与小团队的本地调度场景，集 **CLI 管理**、**Web 管理界面**、**邮件报告** 于一体，零外部服务依赖（无需 Redis / 数据库服务），安装即用。

## 核心特性

- **定时调度** — 支持 cron 表达式、`30m`/`2h`/`1d` 间隔表达式、`YYYY-MM-DD HH:MM:SS` 一次性任务，自动识别
- **一次性任务** — 指定具体日期时间执行一次，执行后自动禁用，过期任务启动时自动跳过
- **任务星标** — 标记重要任务，一键筛选只看星标，随时添加或取消
- **悬浮抽屉** — 鼠标悬浮任务名即可右侧滑出详情面板，无需跳转即可运行/编辑/启用/星标/删除
- **手动触发** — CLI 或 Web UI 一键立即执行任意任务
- **重试机制** — 失败后自动重试，可配置重试次数与间隔
- **超时控制** — 超时自动终止任务进程（含进程树）
- **实时日志** — stdout/stderr 实时落盘，CLI 可 follow 跟踪，Web UI 运行中自动刷新
- **Web 管理界面** — 仪表盘、任务管理、运行日志、报告中心、系统设置
- **邮件通知** — 失败即时告警（带冷却机制）、日报/周报自动发送、多 SMTP 账号轮转
- **零配置存储** — SQLite 本地持久化（WAL 模式）+ TOML 配置文件

## 目录

- [快速启动](#快速启动)
- [详细教程](#详细教程)
- [配置说明](#配置说明)
- [项目架构](#项目架构)
- [开发指南](#开发指南)
- [License](#license)

---

## 快速启动

### 环境要求

- Python >= 3.9（支持 3.9 ~ 3.12）
- pip

### 安装

```bash
# 进入项目根目录（pyproject.toml 所在目录）
cd taskwatch

# 安装
pip install .

# 或开发模式安装（代码修改即时生效）
pip install -e .
```

安装后会注册两个等价的命令入口：`tw` 和 `taskwatch`。

### 5 分钟跑起来

```bash
# 1. 初始化：在当前目录生成 config.toml、taskwatch.db、logs/
tw init

# 2. 添加一个任务：每天早上 9 点执行
tw task add --name "hello" --command "echo Hello TaskWatch" --schedule "0 9 * * *"

# 3. 添加一个一次性任务：2026 年 12 月 31 日 23:59 执行一次
tw task add --name "year_end" --command "python cleanup.py" --schedule "2026-12-31 23:59:00"

# 4. 立即手动执行一次，验证是否工作
tw task run 1

# 5. 启动 Web 管理界面（内嵌调度器，任务会按计划自动运行）
tw web
```

浏览器访问 **http://127.0.0.1:8899**，即可在仪表盘查看统计、在「运行日志」页面查看刚才的输出。

> 如果只需要纯命令行调度：`tw run`（前台运行，Ctrl+C 停止）。
>
> ⚠️ `tw web` 已内嵌调度器，请勿与 `tw run` 同时运行，否则任务会被重复调度。

---

## 详细教程

### CLI 常用命令

#### `tw init` — 初始化

在当前目录创建数据目录、SQLite 数据库和默认配置文件，并可交互式配置 SMTP。

```bash
tw init
```

#### `tw task` — 任务管理

| 命令 | 说明 | 示例 |
|------|------|------|
| `tw task add` | 添加任务 | `tw task add -n "backup" -c "python backup.py" -s "0 2 * * *"` |
| `tw task list` | 列出任务 | `tw task list --status enabled --search etl` |
| `tw task show <ID>` | 任务详情与运行统计 | `tw task show 1` |
| `tw task edit <ID>` | 编辑任务 | `tw task edit 1 --schedule "30 8 * * *"` |
| `tw task rm <ID>` | 删除任务（`-f` 跳过确认） | `tw task rm 1 -f` |
| `tw task enable <ID>` | 启用任务 | `tw task enable 1` |
| `tw task disable <ID>` | 禁用任务 | `tw task disable 1` |
| `tw task run <ID>` | 手动触发一次运行 | `tw task run 1` |

`task add` / `task edit` 支持 `--schedule-type` 显式指定调度类型（`cron`/`interval`/`once`），未指定时自动识别。

#### `tw run` — 启动调度器

```bash
tw run            # 前台运行，Ctrl+C 停止
tw run --daemon   # 后台守护进程（仅 Unix 系统）
```

#### `tw log` — 查看日志

```bash
tw log 1                  # 查看任务 1 的最近运行记录 + 最新日志
tw log 1 --run-id 42      # 查看指定运行记录的日志
tw log 1 --tail 50        # 显示最近 50 行
tw log 1 --follow         # 实时跟踪日志（类似 tail -f）
```

#### `tw report` — 生成报告

```bash
tw report daily                       # 生成今日日报
tw report daily 2026-07-22 --send     # 生成指定日期日报并邮件发送
tw report weekly                      # 生成本周周报
tw report weekly 2026-07-14 2026-07-20 --send
```

#### `tw config` — 配置管理

```bash
tw config --list                    # 查看全部配置
tw config smtp.host                 # 查看单项
tw config smtp.host smtp.qq.com     # 修改单项
```

#### `tw mail` — 邮件测试

```bash
tw mail test    # 发送测试邮件，验证 SMTP 配置
```

#### `tw web` — Web 管理界面

```bash
tw web                              # 默认 127.0.0.1:8899，自动打开浏览器
tw web --host 0.0.0.0 --port 9000   # 自定义监听地址
tw web --no-browser                 # 不自动打开浏览器
```

### Web UI 功能说明

| 页面 | 路径 | 功能 |
|------|------|------|
| 仪表盘 | `/` | 任务总数、运行统计、成功率、近 7 天趋势图、最近运行记录 |
| 任务管理 | `/tasks` | 任务列表、搜索过滤、**星标标记与筛选**、**悬浮抽屉快速查看**、新建/编辑/删除、启用/禁用、手动运行 |
| 任务详情 | `/tasks/{id}` | 任务配置、运行统计（总次数/成功率/平均耗时）、最近运行记录 |
| 运行日志 | `/logs` | 多条件过滤（任务/状态/触发方式/时间范围/关键词）、分页、日志详情弹窗（运行中每 3 秒自动刷新）、终止运行中任务 |
| 报告中心 | `/reports` | 日报/周报历史、手动生成、查看详情、重新发送邮件 |
| 系统设置 | `/settings` | SMTP 配置、报告时间、告警配置、发送测试邮件、清理日志 |

**任务管理页面交互：**

- **星标**：每行任务可点击 ★/☆ 切换星标，顶部"仅看星标"开关可一键筛选星标任务
- **悬浮抽屉**：鼠标悬浮任务名 400ms 后，右侧滑出详情面板，展示任务基础信息与统计，可直接运行/编辑/启用禁用/星标/删除，Esc 或鼠标移出关闭

### 任务配置项说明

| 配置项 | CLI 参数 | 说明 |
|--------|----------|------|
| `name` | `--name` / `-n` | 任务名称（必填） |
| `command` | `--command` / `-c` | 执行命令，通过 shell 执行（必填） |
| `schedule` | `--schedule` / `-s` | 调度表达式，见下方说明（必填） |
| `schedule_type` | `--schedule-type` | 调度类型：`cron`/`interval`/`once`，不传则自动识别 |
| `working_dir` | `--working-dir` / `-w` | 工作目录，默认为当前目录 |
| `env` | `--env` | 环境变量，格式 `KEY1=val1,KEY2=val2` |
| `timeout` | `--timeout` / `-t` | 超时秒数，默认 36000 |
| `retry` | `--retry` / `-r` | 失败最大重试次数，默认 0（不重试） |
| `retry_interval` | `--retry-interval` | 重试间隔秒数，默认跟随 `retry.default_retry_interval` |
| `interpreter` | `--interpreter` | Python 解释器路径（写入 `TASKWATCH_INTERPRETER` 环境变量） |
| `tags` | `--tags` | 标签，逗号分隔，用于搜索分组 |
| `notify` | `--notify` | 通知策略：`on_failure`（默认）/ `always` / `never` |

**调度表达式（自动识别类型）：**

| 类型 | 格式 | 示例 | 说明 |
|------|------|------|------|
| cron | 5 位标准表达式：`分 时 日 月 周` | `0 9 * * *` | 每天 9:00 |
| cron | 6 位带秒：`秒 分 时 日 月 周` | `30 0 9 * * *` | 每天 9:00:30 |
| interval | 数字 + 单位（m/h/d） | `30m`、`2h`、`1d` | 每 30 分钟 / 2 小时 / 1 天 |
| **once** | `YYYY-MM-DD HH:MM:SS` | `2026-12-31 23:59:00` | 指定时刻执行一次，完成后自动禁用 |

**一次性任务（once）行为：**
- 在指定时刻触发执行一次，执行完成后自动禁用（`enabled=0`），不再重复
- 调度器重启后，未执行的 once 任务仍能按时触发
- 启动时若指定时间已过，自动标记为禁用并跳过（不补跑，避免惊群）
- 可随时编辑目标时间后重新启用

---

## 配置说明

### 配置文件位置与格式

配置文件为工作目录下的 **`config.toml`**（TOML 格式），首次运行 `tw init` 时自动生成。可通过 `tw config` 命令或 Web UI「系统设置」页面修改，也可以直接编辑文件（修改后重启服务生效）。

### 完整配置示例（带注释）

以下为 `tw init` 生成的默认配置及各项说明：

```toml
[core]
database_path = "./taskwatch.db"     # SQLite 数据库文件路径
log_dir = "./logs"                   # 任务日志目录
pid_file = "./taskwatch.pid"         # 调度器 PID 文件路径
force_utf8_codepage = true           # Windows 下自动为命令添加 chcp 65001 前缀，
                                     # 解决 CMD 中文/emoji 乱码；不需要可设为 false

[smtp]
enabled = false                      # 是否启用邮件通知
host = "smtp.gmail.com"              # SMTP 服务器
port = 587                           # SMTP 端口
use_tls = true                       # 是否使用 TLS
username = ""                        # SMTP 用户名
password = ""                        # SMTP 密码（推荐用环境变量 TASKWATCH_SMTP_PASSWORD 代替）
from_addr = ""                       # 发件人地址
to_addrs = []                        # 收件人列表

[retry]
default_retry = 0                    # 任务未指定时的默认重试次数（0 表示不重试）
default_retry_interval = 60          # 默认重试间隔（秒）

[timeout]
default_timeout = 36000              # 任务未指定时的默认超时（秒），即 10 小时

[logging]
max_log_files = 100                  # 日志文件保留数量上限
max_log_age_days = 30                # 日志保留天数

[web]
host = "127.0.0.1"                   # Web 监听地址
port = 8899                          # Web 监听端口
auto_open_browser = true             # 启动 tw web 时自动打开浏览器

[report]
daily_enabled = true                 # 是否启用日报
daily_time = "10:00"                 # 日报发送时间
weekly_enabled = true                # 是否启用周报
weekly_day = "monday"                # 周报发送星期
weekly_time = "11:00"                # 周报发送时间

[alert]
enabled = true                       # 是否启用任务失败告警
cooldown_minutes = 10                # 同一任务的告警冷却时间（分钟），防止邮件轰炸
```

> 多 SMTP 账号轮转：可在 `[smtp]` 下配置 `accounts` 账号列表，发信时自动轮转，规避单账号频率限制。

---

## 项目架构

### 目录结构

```
taskwatch/
├── pyproject.toml                  # 项目元数据、依赖、命令入口（tw / taskwatch）
├── docs/                           # 设计文档（spec / design / add_spec）
└── taskwatch/                      # 主包
    ├── cli/                        # 命令行层（Typer + Rich）
    │   ├── main.py                 # 主应用：init / config / mail / web
    │   ├── task.py                 # 任务管理：add/list/show/edit/rm/enable/disable/run
    │   ├── run.py                  # 启动调度器：tw run [--daemon]
    │   ├── log.py                  # 日志查看：tw log [--tail/--follow/--run-id]
    │   └── report.py               # 报告生成：daily / weekly
    ├── core/                       # 核心业务层（不依赖任何 UI 框架）
    │   ├── models.py               # SQLite 数据层（tasks / runs / reports，Schema 版本化迁移）
    │   ├── scheduler.py            # APScheduler 调度引擎（cron/interval/once 触发、孤儿运行清理）
    │   ├── executor.py             # 子进程执行器（超时/重试、实时日志捕获、once 执行后自动禁用）
    │   ├── notifier.py             # SMTP 通知（失败告警、日报/周报发送、多账号轮转、冷却）
    │   └── reporter.py             # 日报/周报 HTML 生成引擎
    ├── utils/                      # 工具层
    │   ├── config.py               # TOML 配置管理（全局单例，默认值 + 用户覆盖深合并）
    │   ├── logger.py               # 日志工具
    │   └── process.py              # 进程工具（按 PID 终止进程、存活检测）
    └── web/                        # Web 展示层（FastAPI）
        ├── app.py                  # 应用工厂 create_app()，启动时内嵌调度器
        ├── api/                    # REST API（/api/stats、tasks、runs、reports、settings）
        ├── routes/                 # 页面路由（Jinja2 服务端渲染）
        ├── static/css/             # 自定义样式
        └── templates/              # HTML 模板（Tailwind CSS + HTMX + Chart.js，CDN 引入）
```

### 核心数据流

```
tw run / tw web（内嵌调度器）
      │  cron / interval / once 定时触发，或 CLI / Web 手动触发
      ▼
TaskExecutor 子进程执行（shell 执行命令）
      │  实时捕获 stdout/stderr，逐行智能解码（UTF-8 优先，系统编码回退）
      │  once 任务执行完成后自动禁用
      ▼
日志文件 logs/<task_id>/*.log  +  SQLite runs 表（状态/退出码/输出摘要）
      │
      ├──▶ Web UI：/logs 页面通过 /api/runs/{id}/stream 实时查看
      ├──▶ CLI：tw log / tw task show
      └──▶ 邮件：失败告警（Notifier）+ 日报/周报（Reporter）
```

### 技术栈

| 技术 | 用途 |
|------|------|
| Typer + Rich | CLI 框架与终端美化输出（表格、颜色） |
| FastAPI + Uvicorn | Web 框架与 ASGI 服务器 |
| Jinja2 | HTML 模板引擎（服务端渲染） |
| Tailwind CSS / HTMX / Chart.js | 前端样式、交互增强、趋势图表（全部 CDN 引入，零构建） |
| APScheduler | 调度引擎（BackgroundScheduler，cron/interval/date 触发器） |
| SQLite（WAL 模式） | 数据持久化，零配置、支持并发读写 |
| tomllib / tomli-w | TOML 配置读写（Python < 3.11 使用 tomli） |
| smtplib | 邮件发送（标准库） |

### 数据库 Schema 版本演进

| 版本 | 变更 |
|------|------|
| v1-v2 | tasks / runs / reports 三表建立 |
| v3 | runs 表增加 pid 列 |
| v4 | tasks.schedule_type 放宽 CHECK 约束，增加 `'once'` 一次性任务类型 |
| v5 | tasks 表增加 starred 列，支持任务星标功能 |

迁移在启动时自动执行，事务化保证安全，向后兼容。

---

## 开发指南

### 本地开发环境

```bash
# 1. 克隆项目并进入目录
cd taskwatch

# 2. 以开发模式安装（含 pytest 等开发依赖）
pip install -e ".[dev]"

# 3. 验证安装
tw --help
```

开发时数据文件（`config.toml`、`taskwatch.db`、`logs/`）生成在**当前工作目录**，建议在项目外单独建一个工作目录运行 `tw init`，避免污染源码目录。

### 运行测试

项目使用 pytest 作为测试框架（已包含在 `dev` 可选依赖中）：

```bash
pytest
```

> 当前仓库未内置 `tests/` 目录；新增测试文件后 pytest 会自动收集执行。
> 修改代码后可先用 `python -m py_compile <文件>` 做语法快速检查。

---

## 免责声明

- 本项目为**本地轻量工具**，设计用于个人或小团队的本地任务调度场景，**不适用于生产环境高可用、高并发场景**。
- 任务执行依赖本地系统环境（Shell、Python 解释器等），项目**不对用户命令的执行结果负责**。
- 邮件发送功能依赖用户自行配置的 SMTP 服务，项目**不保证邮件一定能成功送达**。
- 数据存储为本地 SQLite 文件，**用户需自行做好数据备份**，项目不对数据丢失承担责任。
- 本项目按 **"原样"（AS IS）** 提供，不附带任何明示或暗示的保证。

---

## License

[MIT](https://opensource.org/licenses/MIT)
