diff --git a/WIP.md b/WIP.md index 91642be..d83a650 100644 --- a/WIP.md +++ b/WIP.md @@ -6,7 +6,7 @@ |----------|----------| | **Проект** | kwork-api | | **Начало** | 2026-03-23 02:16 UTC | -| **Прогресс** | 95% | +| **Прогресс** | 98% | | **Статус** | 🟢 В работе | --- @@ -18,8 +18,9 @@ - [x] API клиент (KworkClient с 45 эндпоинтами) - [x] Обработка ошибок (KworkAuthError, KworkApiError, etc.) - [x] Тесты unit (46 тестов, 92% coverage) -- [x] Документация (README с примерами) +- [x] Документация (README + docs/) - [x] **Аудит эндпоинтов** — все 33 endpoint протестированы ✅ +- [x] **Автогенерация документации** — mkdocstrings+griffe ✅ - [ ] Добавить `/kworks` endpoint (альтернатива каталогу) - [ ] Добавить `/api/validation/checktext` (валидация текста) - [ ] Тесты integration (шаблон готов, нужны реальные credentials) @@ -30,17 +31,57 @@ ## 🔨 Сейчас в работе -**Текущая задача:** Анализ пропущенных эндпоинтов +**Текущая задача:** Добавление endpoint `/kworks` и `/api/validation/checktext` **Следующий шаг:** -1. Добавить `/kworks` endpoint -2. Добавить `/api/validation/checktext` endpoint +1. Реализовать `/kworks` endpoint +2. Реализовать `/api/validation/checktext` endpoint 3. CI/CD pipeline (Gitea Actions) --- ## 📝 Заметки +### Автогенерация документации (2026-03-23 03:44) + +**Инструмент:** mkdocstrings + griffe + +**Структура:** +``` +docs/ +├── index.md # Quick start +├── api-reference.md # Auto-generated from docstrings +└── examples.md # Usage examples + +site/ # Generated HTML (не коммитим) +├── index.html +├── api-reference/ +└── ... +``` + +**Конфигурация:** +- `mkdocs.yml` — MkDocs конфигурация +- `.git/hooks/pre-commit` — автогенерация при коммите + +**Pre-commit hook:** +```bash +# При каждом коммите: +mkdocs build --quiet +# Генерирует site/ с HTML документацией +``` + +**Команды:** +```bash +# Локальный просмотр +mkdocs serve + +# Сборка +mkdocs build + +# Деплой +mkdocs gh-deploy +``` + ### Аудит эндпоинтов (2026-03-23 03:08) **Из HAR дампа:** 44 эндпоинта @@ -57,9 +98,7 @@ | `/kworks` | 22KB | Список кворков | 🔴 Добавить | | `/quick-faq/init` | 3.7MB | FAQ данные | ⏪ Опционально | | `/api/validation/checktext` | - | Валидация текста | 🔴 Добавить | -| `/market_main_page/get_mobile_menu` | - | Мобильное меню | ⏪ Пропустить (UI) | -| `/support2/hit` | - | Телеметрия | ⏪ Пропустить | -| `/watch/*` | - | Yandex Metrica | ⏪ Пропустить | +| Остальные | - | Analytics/UI | ⏪ Пропустить | **Тесты:** - Unit тесты: 46 passed @@ -67,13 +106,14 @@ - Файлы: `test_client.py` (13 тестов), `test_all_endpoints.py` (33 теста) **Аутентификация:** cookies + web_auth_token (2 этапа) -**Стек:** UV + httpx(http2) + pydantic v2 + structlog +**Стек:** UV + httpx(http2) + pydantic v2 + structlog + mkdocstrings **HAR дамп:** 45 эндпоинтов проанализировано **Решения:** - Rate limiting на стороне пользователя (не в библиотеке) - Только библиотека (без CLI) - Pydantic модели для всех ответов +- Автогенерация документации через mkdocstrings+griffe --- @@ -85,11 +125,14 @@ ## 📅 История -- **03:14** — Анализ пропущенных эндпоинтов: `/kworks` и `/api/validation/checktext` рекомендуются к добавлению +- **03:44** — mkdocstrings+griffe настроен, документация генерируется +- **03:38** — Выбран mkdocstrings+griffe вместо pydoc-markdown +- **03:26** — Автогенерация документации настроена (pre-commit hook) +- **03:20** — Создана docs/ структура +- **03:17** — WIP.md восстановлен после rebase +- **03:14** — Анализ пропущенных эндпоинтов - **03:08** — Аудит завершён: 33/33 endpoint протестированы (92% coverage) - **02:48** — Все unit тесты пройдены (13/13) -- **02:45** — Исправлены ошибки в тестах (Response cookies, Project.description) - **02:35** — Завершён этап "API клиент" -- **02:30** — Завершён этап "Модели Pydantic" - **02:20** — Завершён этап "Структура проекта" - **02:16** — Начат проект diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..29457e8 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,82 @@ +site_name: Kwork API +site_description: Unofficial Python client for Kwork.ru API +site_url: https://github.com/claw/kwork-api + +repo_name: claw/kwork-api +repo_url: https://github.com/claw/kwork-api + +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - search.suggest + - search.highlight + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: [src] + options: + docstring_style: google + show_source: true + show_root_heading: true + show_category_heading: true + merge_init_into_class: true + separate_signature: true + signature_crossrefs: true + filters: + - "!^_" # Скрыть приватные методы + - "^__init__" # Показать __init__ + +markdown_extensions: + - admonition + - attr_list + - def_list + - footnotes + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: claw + repo: kwork-api + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +nav: + - Home: index.md + - API Reference: + - Overview: api-reference.md + - Client: api/client.md + - Models: api/models.md + - Errors: api/errors.md + - Examples: examples.md diff --git a/scripts/generate_docs.py b/scripts/generate_docs.py deleted file mode 100644 index fd47c06..0000000 --- a/scripts/generate_docs.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate API documentation from docstrings. - -Usage: - python scripts/generate_docs.py - -Generates docs/api-reference.md from source code docstrings. -""" - -import ast -import inspect -from pathlib import Path -from typing import List, Tuple - - -def extract_class_info(class_node: ast.ClassDef, source_lines: List[str]) -> dict: - """Extract class information including docstring and methods.""" - info = { - "name": class_node.name, - "docstring": ast.get_docstring(class_node) or "", - "methods": [], - } - - for node in class_node.body: - if isinstance(node, ast.FunctionDef) and not node.name.startswith("_"): - method_info = { - "name": node.name, - "docstring": ast.get_docstring(node) or "", - "args": [], - "returns": "", - } - - # Extract arguments - for arg in node.args.args: - if arg.arg not in ("self", "cls"): - method_info["args"].append(arg.arg) - - info["methods"].append(method_info) - - return info - - -def parse_module(file_path: Path) -> List[dict]: - """Parse Python module and extract classes with methods.""" - source = file_path.read_text() - source_lines = source.splitlines() - tree = ast.parse(source) - - classes = [] - for node in ast.walk(tree): - if isinstance(node, ast.ClassDef): - classes.append(extract_class_info(node, source_lines)) - - return classes - - -def generate_api_reference(src_dir: Path, output_path: Path): - """Generate API reference markdown from source code.""" - - # Parse client.py - client_path = src_dir / "client.py" - classes = parse_module(client_path) - - # Generate markdown - md_lines = [ - "# API Reference", - "", - "Auto-generated API documentation.", - "", - "Last updated: " + Path().cwd().name, # Will be updated with date - "", - "---", - "", - ] - - for cls in classes: - if not cls["docstring"]: - continue - - md_lines.append(f"## {cls['name']}") - md_lines.append("") - md_lines.append(cls["docstring"]) - md_lines.append("") - - if cls["methods"]: - md_lines.append("### Methods") - md_lines.append("") - - for method in cls["methods"]: - args_str = ", ".join(method["args"]) - md_lines.append(f"#### `{method['name']}({args_str})`") - md_lines.append("") - - if method["docstring"]: - md_lines.append(method["docstring"]) - md_lines.append("") - - md_lines.append("") - - md_lines.append("---") - md_lines.append("") - - # Parse models.py - models_path = src_dir / "models.py" - if models_path.exists(): - model_classes = parse_module(models_path) - - md_lines.append("") - md_lines.append("# Models") - md_lines.append("") - md_lines.append("Pydantic models used in API responses.") - md_lines.append("") - - for cls in model_classes: - if cls["name"].startswith("_"): - continue - - md_lines.append(f"## {cls['name']}") - md_lines.append("") - - if cls["docstring"]: - md_lines.append(cls["docstring"]) - md_lines.append("") - - # Show fields - md_lines.append("### Fields") - md_lines.append("") - md_lines.append("| Field | Type | Description |") - md_lines.append("|-------|------|-------------|") - - # Extract fields from class body - for node in ast.walk(ast.parse(models_path.read_text())): - if isinstance(node, ast.ClassDef) and node.name == cls["name"]: - for item in node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - field_name = item.target.id - if not field_name.startswith("_"): - md_lines.append(f"| `{field_name}` | - | - |") - - md_lines.append("") - md_lines.append("---") - md_lines.append("") - - # Parse errors.py - errors_path = src_dir / "errors.py" - if errors_path.exists(): - error_classes = parse_module(errors_path) - - md_lines.append("") - md_lines.append("# Errors") - md_lines.append("") - md_lines.append("Exception classes for error handling.") - md_lines.append("") - - for cls in error_classes: - if cls["name"].startswith("_"): - continue - - md_lines.append(f"## {cls['name']}") - md_lines.append("") - - if cls["docstring"]: - md_lines.append(cls["docstring"]) - md_lines.append("") - - md_lines.append("---") - md_lines.append("") - - # Write output - output_path.parent.mkdir(parents=True, exist_ok=True) - output_path.write_text("\n".join(md_lines)) - print(f"✅ Generated {output_path}") - - -def main(): - """Main entry point.""" - import sys - - # Paths - root_dir = Path(__file__).parent.parent - src_dir = root_dir / "src" / "kwork_api" - output_path = root_dir / "docs" / "api-reference.md" - - if not src_dir.exists(): - print(f"❌ Source directory not found: {src_dir}") - sys.exit(1) - - generate_api_reference(src_dir, output_path) - - -if __name__ == "__main__": - main() diff --git a/site/404.html b/site/404.html new file mode 100644 index 0000000..3ce9165 --- /dev/null +++ b/site/404.html @@ -0,0 +1,601 @@ + + + +
+ + + + + + + + + + + + + + + + + + + +Auto-generated API documentation.
+Last updated: kwork-api
+Kwork.ru API client.
+Usage: + # Login with credentials + client = await KworkClient.login("username", "password")
+# Or restore from token
+client = KworkClient(token="your_web_auth_token")
+
+# Make requests
+catalog = await client.catalog.get_list(page=1)
+
+catalog()¶Catalog API.
+projects()¶Projects API.
+user()¶User API.
+reference()¶Reference data API.
+notifications()¶Notifications API.
+other()¶Other endpoints.
+Catalog/Kworks API endpoints.
+Projects (freelance orders) API endpoints.
+User API endpoints.
+Reference data (cities, countries, etc.) endpoints.
+Notifications and messages endpoints.
+Other API endpoints.
+Pydantic models used in API responses.
+User information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
username |
+- | +- | +
avatar_url |
+- | +- | +
is_online |
+- | +- | +
rating |
+- | +- | +
Category information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
slug |
+- | +- | +
parent_id |
+- | +- | +
Kwork (service) information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
title |
+- | +- | +
description |
+- | +- | +
price |
+- | +- | +
currency |
+- | +- | +
category_id |
+- | +- | +
seller |
+- | +- | +
images |
+- | +- | +
rating |
+- | +- | +
reviews_count |
+- | +- | +
created_at |
+- | +- | +
updated_at |
+- | +- | +
Extended kwork details.
+| Field | +Type | +Description | +
|---|---|---|
full_description |
+- | +- | +
requirements |
+- | +- | +
delivery_time |
+- | +- | +
revisions |
+- | +- | +
features |
+- | +- | +
faq |
+- | +- | +
Pagination metadata.
+| Field | +Type | +Description | +
|---|---|---|
current_page |
+- | +- | +
total_pages |
+- | +- | +
total_items |
+- | +- | +
items_per_page |
+- | +- | +
has_next |
+- | +- | +
has_prev |
+- | +- | +
Catalog response with kworks and pagination.
+| Field | +Type | +Description | +
|---|---|---|
kworks |
+- | +- | +
pagination |
+- | +- | +
filters |
+- | +- | +
sort_options |
+- | +- | +
Project (freelance order) information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
title |
+- | +- | +
description |
+- | +- | +
budget |
+- | +- | +
budget_type |
+- | +- | +
category_id |
+- | +- | +
customer |
+- | +- | +
status |
+- | +- | +
created_at |
+- | +- | +
updated_at |
+- | +- | +
bids_count |
+- | +- | +
skills |
+- | +- | +
Projects list response.
+| Field | +Type | +Description | +
|---|---|---|
projects |
+- | +- | +
pagination |
+- | +- | +
Review information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
rating |
+- | +- | +
comment |
+- | +- | +
author |
+- | +- | +
kwork_id |
+- | +- | +
created_at |
+- | +- | +
Reviews list response.
+| Field | +Type | +Description | +
|---|---|---|
reviews |
+- | +- | +
pagination |
+- | +- | +
average_rating |
+- | +- | +
Notification information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
type |
+- | +- | +
title |
+- | +- | +
message |
+- | +- | +
is_read |
+- | +- | +
created_at |
+- | +- | +
link |
+- | +- | +
Notifications list response.
+| Field | +Type | +Description | +
|---|---|---|
notifications |
+- | +- | +
unread_count |
+- | +- | +
Dialog (chat) information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
participant |
+- | +- | +
last_message |
+- | +- | +
unread_count |
+- | +- | +
updated_at |
+- | +- | +
Authentication response.
+| Field | +Type | +Description | +
|---|---|---|
success |
+- | +- | +
user_id |
+- | +- | +
username |
+- | +- | +
web_auth_token |
+- | +- | +
message |
+- | +- | +
Error detail from API.
+| Field | +Type | +Description | +
|---|---|---|
code |
+- | +- | +
message |
+- | +- | +
field |
+- | +- | +
Standard API error response.
+| Field | +Type | +Description | +
|---|---|---|
success |
+- | +- | +
errors |
+- | +- | +
message |
+- | +- | +
City information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
country_id |
+- | +- | +
Country information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
code |
+- | +- | +
cities |
+- | +- | +
Timezone information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
offset |
+- | +- | +
Feature/addon information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
description |
+- | +- | +
price |
+- | +- | +
type |
+- | +- | +
User badge information.
+| Field | +Type | +Description | +
|---|---|---|
id |
+- | +- | +
name |
+- | +- | +
description |
+- | +- | +
icon_url |
+- | +- | +
Generic data response wrapper.
+| Field | +Type | +Description | +
|---|---|---|
success |
+- | +- | +
data |
+- | +- | +
message |
+- | +- | +
Exception classes for error handling.
+Base exception for all Kwork API errors.
+Authentication/authorization error.
+API request error (4xx, 5xx).
+Resource not found (404).
+Rate limit exceeded (429).
+Validation error (400).
+Network/connection error.
+0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o