mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
1070 lines
24 KiB
Markdown
1070 lines
24 KiB
Markdown
# TaskNotes HTTP API
|
|
|
|
The TaskNotes HTTP API allows external applications to interact with your TaskNotes data. This enables powerful integrations with browsers, automation tools, mobile apps, and custom scripts.
|
|
|
|
## Quick Start
|
|
|
|
1. **Enable API**: Go to TaskNotes Settings → HTTP API tab (desktop only)
|
|
2. **Configure**: Set port (default 8080) and optional auth token
|
|
3. **Restart**: Restart Obsidian to start the server
|
|
4. **Test**: `curl http://localhost:8080/api/health`
|
|
5. **Explore**: Visit `http://localhost:8080/api/docs/ui` for interactive documentation
|
|
|
|
## Interactive Documentation
|
|
|
|
TaskNotes provides comprehensive API documentation through Swagger UI:
|
|
|
|
- **OpenAPI Specification**: `GET /api/docs` - Machine-readable API spec in OpenAPI 3.0 format
|
|
- **Interactive Docs**: `GET /api/docs/ui` - Swagger UI for exploring and testing endpoints
|
|
|
|
The interactive documentation includes:
|
|
- Complete endpoint documentation with examples
|
|
- Request/response schemas
|
|
- Try-it-out functionality for testing endpoints
|
|
- Authentication setup for protected endpoints
|
|
|
|
## Authentication
|
|
|
|
### Optional Bearer Token
|
|
```bash
|
|
# Set token in settings, then use in requests:
|
|
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/tasks
|
|
```
|
|
|
|
### No Authentication
|
|
If no token is configured, all requests are allowed from localhost.
|
|
|
|
## Base URL
|
|
```
|
|
http://localhost:{PORT}/api
|
|
```
|
|
Default port is 8080 (configurable in settings).
|
|
|
|
## Response Format
|
|
|
|
All endpoints return JSON in this format:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { /* response data */ },
|
|
"message": "optional success message"
|
|
}
|
|
```
|
|
|
|
Error responses:
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Error description"
|
|
}
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
### Health Check
|
|
```
|
|
GET /api/health
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"status": "ok",
|
|
"timestamp": "2025-08-12T10:30:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Tasks
|
|
|
|
#### List Tasks
|
|
```
|
|
GET /api/tasks
|
|
```
|
|
|
|
**Query Parameters:**
|
|
|
|
- `status` - Filter by status (e.g., "open", "completed")
|
|
- `priority` - Filter by priority (e.g., "High", "Normal")
|
|
- `project` - Filter by project name (partial match)
|
|
- `tag` - Filter by tag (partial match)
|
|
- `overdue` - "true" for overdue tasks only
|
|
- `completed` - "true" or "false"
|
|
- `archived` - "true" or "false"
|
|
- `due_before` - ISO date (e.g., "2025-08-15")
|
|
- `due_after` - ISO date
|
|
- `sort` - Field to sort by (e.g., "due:asc", "priority:desc")
|
|
- `limit` - Max number of results
|
|
- `offset` - Skip this many results
|
|
|
|
**Examples:**
|
|
```bash
|
|
# All active tasks
|
|
curl "http://localhost:8080/api/tasks?completed=false&archived=false"
|
|
|
|
# High priority overdue tasks
|
|
curl "http://localhost:8080/api/tasks?priority=High&overdue=true"
|
|
|
|
# Tasks due this week, sorted by due date
|
|
curl "http://localhost:8080/api/tasks?due_before=2025-08-19&sort=due:asc"
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"tasks": [
|
|
{
|
|
"path": "TaskNotes/Tasks/sample-task.md",
|
|
"title": "Review quarterly budget",
|
|
"status": "open",
|
|
"priority": "High",
|
|
"due": "2025-08-15",
|
|
"scheduled": "2025-08-14",
|
|
"tags": ["work", "finance"],
|
|
"projects": ["[[Q3 Planning]]"],
|
|
"contexts": ["@office"],
|
|
"dateCreated": "2025-08-10T09:00:00.000Z",
|
|
"dateModified": "2025-08-10T09:00:00.000Z"
|
|
}
|
|
],
|
|
"total": 150,
|
|
"filtered": 1
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Create Task
|
|
|
|
```
|
|
POST /api/tasks
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "New task title",
|
|
"priority": "High",
|
|
"status": "open",
|
|
"due": "2025-08-15",
|
|
"scheduled": "2025-08-14",
|
|
"tags": ["email", "urgent"],
|
|
"projects": ["[[Work Project]]"],
|
|
"contexts": ["@computer"],
|
|
"details": "Additional task description",
|
|
"timeEstimate": 60
|
|
}
|
|
```
|
|
|
|
**Required Fields:**
|
|
|
|
- `title` - Task title (max 200 characters)
|
|
|
|
**Optional Fields:**
|
|
|
|
- `priority` - Task priority
|
|
- `status` - Task status
|
|
- `due` - Due date (ISO format)
|
|
- `scheduled` - Scheduled date (ISO format)
|
|
- `tags` - Array of tag strings
|
|
- `projects` - Array of project links
|
|
- `contexts` - Array of context strings
|
|
- `details` - Task description/details
|
|
- `timeEstimate` - Estimated time in minutes
|
|
|
|
#### Get Single Task
|
|
|
|
```
|
|
GET /api/tasks/{id}
|
|
```
|
|
|
|
Where `{id}` is the task file path (URL-encoded).
|
|
|
|
#### Update Task
|
|
|
|
```
|
|
PUT /api/tasks/{id}
|
|
```
|
|
|
|
**Request Body:** Same format as create task, with partial updates supported.
|
|
|
|
#### Delete Task
|
|
|
|
```
|
|
DELETE /api/tasks/{id}
|
|
```
|
|
|
|
### Time Tracking
|
|
|
|
Control and query time tracking data for tasks with comprehensive analytics and reporting capabilities.
|
|
|
|
#### Start Time Tracking
|
|
|
|
```
|
|
POST /api/tasks/{id}/time/start
|
|
```
|
|
|
|
Start time tracking for a specific task.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "path/to/task.md",
|
|
"title": "Work on API integration",
|
|
"status": "in-progress",
|
|
"timeEntries": [
|
|
{
|
|
"startTime": "2025-08-14T10:00:00.000Z",
|
|
"description": null
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Start Time Tracking with Description
|
|
|
|
```
|
|
POST /api/tasks/{id}/time/start-with-description
|
|
```
|
|
|
|
Start time tracking with an optional description of the work being done.
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"description": "Working on API endpoint implementation"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"task": {
|
|
"id": "path/to/task.md",
|
|
"title": "Work on API integration"
|
|
},
|
|
"message": "Time tracking started with description: Working on API endpoint implementation"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Stop Time Tracking
|
|
|
|
```
|
|
POST /api/tasks/{id}/time/stop
|
|
```
|
|
|
|
Stop the currently active time tracking session for a task.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "path/to/task.md",
|
|
"title": "Work on API integration",
|
|
"timeEntries": [
|
|
{
|
|
"startTime": "2025-08-14T10:00:00.000Z",
|
|
"endTime": "2025-08-14T11:30:00.000Z",
|
|
"duration": 90,
|
|
"description": "Working on API endpoint implementation"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Get Task Time Data
|
|
|
|
```
|
|
GET /api/tasks/{id}/time
|
|
```
|
|
|
|
Get comprehensive time tracking data for a specific task.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"task": {
|
|
"id": "path/to/task.md",
|
|
"title": "Work on API integration",
|
|
"status": "in-progress",
|
|
"priority": "high"
|
|
},
|
|
"summary": {
|
|
"totalMinutes": 180,
|
|
"totalHours": 3.0,
|
|
"totalSessions": 3,
|
|
"completedSessions": 2,
|
|
"activeSessions": 1,
|
|
"averageSessionMinutes": 60
|
|
},
|
|
"activeSession": {
|
|
"startTime": "2025-08-14T14:00:00.000Z",
|
|
"description": "Final testing phase",
|
|
"elapsedMinutes": 15
|
|
},
|
|
"timeEntries": [
|
|
{
|
|
"startTime": "2025-08-14T10:00:00.000Z",
|
|
"endTime": "2025-08-14T11:30:00.000Z",
|
|
"description": "Initial implementation",
|
|
"duration": 90,
|
|
"isActive": false
|
|
},
|
|
{
|
|
"startTime": "2025-08-14T13:00:00.000Z",
|
|
"endTime": "2025-08-14T13:45:00.000Z",
|
|
"description": "Code review and fixes",
|
|
"duration": 45,
|
|
"isActive": false
|
|
},
|
|
{
|
|
"startTime": "2025-08-14T14:00:00.000Z",
|
|
"endTime": null,
|
|
"description": "Final testing phase",
|
|
"duration": 15,
|
|
"isActive": true
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Get Active Time Sessions
|
|
|
|
```
|
|
GET /api/time/active
|
|
```
|
|
|
|
Get all currently active time tracking sessions across all tasks.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"activeSessions": [
|
|
{
|
|
"task": {
|
|
"id": "path/to/task1.md",
|
|
"title": "API Integration",
|
|
"status": "in-progress",
|
|
"priority": "high",
|
|
"tags": ["development", "api"],
|
|
"projects": ["[[Project Alpha]]"]
|
|
},
|
|
"session": {
|
|
"startTime": "2025-08-14T14:00:00.000Z",
|
|
"description": "Final testing phase",
|
|
"elapsedMinutes": 25
|
|
},
|
|
"elapsedMinutes": 25
|
|
},
|
|
{
|
|
"task": {
|
|
"id": "path/to/task2.md",
|
|
"title": "Documentation Update",
|
|
"status": "open",
|
|
"priority": "normal",
|
|
"tags": ["documentation"],
|
|
"projects": ["[[Project Beta]]"]
|
|
},
|
|
"session": {
|
|
"startTime": "2025-08-14T13:45:00.000Z",
|
|
"description": "Writing API examples",
|
|
"elapsedMinutes": 40
|
|
},
|
|
"elapsedMinutes": 40
|
|
}
|
|
],
|
|
"totalActiveSessions": 2,
|
|
"totalElapsedMinutes": 65
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Get Time Summary
|
|
|
|
```
|
|
GET /api/time/summary
|
|
```
|
|
|
|
Get time tracking statistics and summaries with flexible date filtering.
|
|
|
|
**Query Parameters:**
|
|
- `period` - Time period: `today`, `week`, `month`, `all` (default: `today`)
|
|
- `from` - Start date for custom period (ISO format: `2025-08-01`)
|
|
- `to` - End date for custom period (ISO format: `2025-08-15`)
|
|
|
|
**Examples:**
|
|
```bash
|
|
# Today's time summary
|
|
curl "http://localhost:8080/api/time/summary"
|
|
|
|
# This week's summary
|
|
curl "http://localhost:8080/api/time/summary?period=week"
|
|
|
|
# Custom date range
|
|
curl "http://localhost:8080/api/time/summary?from=2025-08-01&to=2025-08-15"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"period": "today",
|
|
"dateRange": {
|
|
"from": "2025-08-14T00:00:00.000Z",
|
|
"to": "2025-08-14T23:59:59.999Z"
|
|
},
|
|
"summary": {
|
|
"totalMinutes": 320,
|
|
"totalHours": 5.33,
|
|
"tasksWithTime": 8,
|
|
"activeTasks": 2,
|
|
"completedTasks": 3
|
|
},
|
|
"topTasks": [
|
|
{
|
|
"task": "projects/api-integration.md",
|
|
"title": "API Integration",
|
|
"minutes": 120
|
|
},
|
|
{
|
|
"task": "projects/documentation.md",
|
|
"title": "Documentation Update",
|
|
"minutes": 95
|
|
}
|
|
],
|
|
"topProjects": [
|
|
{
|
|
"project": "[[Project Alpha]]",
|
|
"minutes": 180
|
|
},
|
|
{
|
|
"project": "[[Project Beta]]",
|
|
"minutes": 140
|
|
}
|
|
],
|
|
"topTags": [
|
|
{
|
|
"tag": "development",
|
|
"minutes": 200
|
|
},
|
|
{
|
|
"tag": "documentation",
|
|
"minutes": 120
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Task Actions
|
|
|
|
#### Toggle Status
|
|
|
|
```
|
|
POST /api/tasks/{id}/toggle-status
|
|
```
|
|
|
|
Toggles between open/completed status.
|
|
|
|
#### Toggle Archive
|
|
|
|
```
|
|
POST /api/tasks/{id}/archive
|
|
```
|
|
|
|
Archives or unarchives the task.
|
|
|
|
#### Complete Recurring Instance
|
|
|
|
```
|
|
POST /api/tasks/{id}/complete-instance
|
|
```
|
|
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"date": "2025-08-12"
|
|
}
|
|
```
|
|
|
|
### Advanced Queries
|
|
|
|
#### Query Tasks
|
|
|
|
```
|
|
POST /api/tasks/query
|
|
```
|
|
|
|
|
|
**Request Body:** Advanced FilterQuery object (see TaskNotes FilterQuery documentation).
|
|
|
|
#### Get Filter Options
|
|
|
|
```
|
|
GET /api/filter-options
|
|
```
|
|
|
|
Returns available tags, projects, statuses, and priorities for building filter UIs.
|
|
|
|
### Statistics
|
|
|
|
#### Get Task Statistics
|
|
|
|
```
|
|
GET /api/stats
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"total": 245,
|
|
"completed": 189,
|
|
"active": 45,
|
|
"overdue": 8,
|
|
"archived": 11,
|
|
"withTimeTracking": 67
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Get Aggregated Time Estimates
|
|
|
|
```
|
|
GET /api/time-stats
|
|
```
|
|
|
|
Aggregates the `timeEstimate` for tasks within a given date range. The range can be a predefined period or a custom start/end date.
|
|
|
|
**Query Parameters:**
|
|
|
|
- `range` - A predefined range. Can be one of `daily`, `weekly`, `monthly`, `yearly`.
|
|
- `start` - A start date for a custom range, in `YYYY-MM-DD` format. Must be used with `end`.
|
|
- `end` - An end date for a custom range, in `YYYY-MM-DD` format. Must be used with `start`.
|
|
|
|
**Examples:**
|
|
```bash
|
|
# Get total estimated time for tasks this week
|
|
curl "http://localhost:8080/api/time-stats?range=weekly"
|
|
|
|
# Get total estimated time for a custom range
|
|
curl "http://localhost:8080/api/time-stats?start=2025-01-01&end=2025-01-31"
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"totalMinutes": 750
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pomodoro
|
|
|
|
Control pomodoro sessions programmatically through the API.
|
|
|
|
#### Start Pomodoro Session
|
|
|
|
```
|
|
POST /api/pomodoro/start
|
|
```
|
|
|
|
**Request Body (Optional):**
|
|
|
|
```json
|
|
{
|
|
"taskId": "path/to/task.md"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"session": {
|
|
"id": "pomo_123",
|
|
"type": "work",
|
|
"duration": 1500,
|
|
"startTime": "2025-08-13T10:00:00.000Z"
|
|
},
|
|
"task": {
|
|
"id": "path/to/task.md",
|
|
"title": "Work on API integration"
|
|
},
|
|
"message": "Pomodoro session started"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Stop Pomodoro Session
|
|
|
|
```
|
|
POST /api/pomodoro/stop
|
|
```
|
|
|
|
#### Pause Pomodoro Session
|
|
|
|
```
|
|
POST /api/pomodoro/pause
|
|
```
|
|
|
|
#### Resume Pomodoro Session
|
|
|
|
```
|
|
POST /api/pomodoro/resume
|
|
```
|
|
|
|
#### Get Pomodoro Status
|
|
|
|
```
|
|
GET /api/pomodoro/status
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"isRunning": true,
|
|
"timeRemaining": 900,
|
|
"currentSession": {
|
|
"id": "pomo_123",
|
|
"type": "work",
|
|
"duration": 1500,
|
|
"startTime": "2025-08-13T10:00:00.000Z"
|
|
},
|
|
"totalPomodoros": 42,
|
|
"currentStreak": 3,
|
|
"totalMinutesToday": 180
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Get Pomodoro Session History
|
|
|
|
```
|
|
GET /api/pomodoro/sessions
|
|
```
|
|
|
|
**Query Parameters:**
|
|
|
|
- `limit` - Maximum number of sessions to return
|
|
- `date` - Filter sessions by date (YYYY-MM-DD)
|
|
|
|
**Examples:**
|
|
|
|
```bash
|
|
# Get last 10 sessions
|
|
curl "http://localhost:8080/api/pomodoro/sessions?limit=10"
|
|
|
|
# Get sessions for specific date
|
|
curl "http://localhost:8080/api/pomodoro/sessions?date=2025-08-13"
|
|
```
|
|
|
|
#### Get Pomodoro Statistics
|
|
|
|
```
|
|
GET /api/pomodoro/stats
|
|
```
|
|
|
|
**Query Parameters:**
|
|
|
|
- `date` - Get stats for specific date (YYYY-MM-DD), defaults to today
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"totalSessions": 15,
|
|
"completedSessions": 12,
|
|
"interruptedSessions": 3,
|
|
"totalFocusTime": 300,
|
|
"workSessions": 10,
|
|
"breakSessions": 5,
|
|
"longestStreak": 8,
|
|
"averageSessionLength": 24.5
|
|
}
|
|
}
|
|
```
|
|
|
|
## Integration Examples
|
|
|
|
### Browser Bookmarklet
|
|
|
|
```javascript
|
|
javascript:(function(){
|
|
const title = document.title;
|
|
const url = window.location.href;
|
|
|
|
fetch('http://localhost:8080/api/tasks', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
title: `Review: ${title}`,
|
|
tags: ['web'],
|
|
details: `Source: ${url}`
|
|
})
|
|
}).then(r => r.json()).then(d => {
|
|
alert(d.success ? 'Task created!' : 'Error: ' + d.error);
|
|
});
|
|
})();
|
|
```
|
|
|
|
### Python Script
|
|
|
|
```python
|
|
import requests
|
|
|
|
def create_task(title, **kwargs):
|
|
response = requests.post('http://localhost:8080/api/tasks',
|
|
json={'title': title, **kwargs})
|
|
return response.json()
|
|
|
|
# Create task from command line
|
|
task = create_task("Call dentist", priority="High", due="2025-08-15")
|
|
print(f"Created task: {task['data']['title']}")
|
|
```
|
|
|
|
### Automation (Zapier/IFTTT)
|
|
|
|
```bash
|
|
# Webhook URL for automation services
|
|
curl -X POST http://localhost:8080/api/tasks \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"title":"{{trigger.subject}}", "tags":["email"], "details":"{{trigger.body}}"}'
|
|
```
|
|
|
|
### Pomodoro Timer Integration
|
|
|
|
```javascript
|
|
// Simple Pomodoro timer controller
|
|
class PomodoroController {
|
|
constructor(apiUrl = 'http://localhost:8080') {
|
|
this.apiUrl = apiUrl;
|
|
}
|
|
|
|
async startSession(taskId = null) {
|
|
const body = taskId ? { taskId } : {};
|
|
const response = await fetch(`${this.apiUrl}/api/pomodoro/start`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body)
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
async getStatus() {
|
|
const response = await fetch(`${this.apiUrl}/api/pomodoro/status`);
|
|
return response.json();
|
|
}
|
|
|
|
async pause() {
|
|
const response = await fetch(`${this.apiUrl}/api/pomodoro/pause`, {
|
|
method: 'POST'
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
async resume() {
|
|
const response = await fetch(`${this.apiUrl}/api/pomodoro/resume`, {
|
|
method: 'POST'
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
async stop() {
|
|
const response = await fetch(`${this.apiUrl}/api/pomodoro/stop`, {
|
|
method: 'POST'
|
|
});
|
|
return response.json();
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
const pomodoro = new PomodoroController();
|
|
|
|
// Start a session for a specific task
|
|
await pomodoro.startSession('Projects/MyProject.md');
|
|
|
|
// Check current status
|
|
const status = await pomodoro.getStatus();
|
|
console.log(`Time remaining: ${Math.floor(status.data.timeRemaining / 60)} minutes`);
|
|
```
|
|
|
|
### Time Tracking Integration
|
|
|
|
```javascript
|
|
// Comprehensive time tracking controller
|
|
class TimeTracker {
|
|
constructor(apiUrl = 'http://localhost:8080') {
|
|
this.apiUrl = apiUrl;
|
|
}
|
|
|
|
// Start time tracking with description
|
|
async startTracking(taskId, description = null) {
|
|
const endpoint = description
|
|
? `/api/tasks/${encodeURIComponent(taskId)}/time/start-with-description`
|
|
: `/api/tasks/${encodeURIComponent(taskId)}/time/start`;
|
|
|
|
const body = description ? { description } : {};
|
|
|
|
const response = await fetch(`${this.apiUrl}${endpoint}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body)
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
// Stop time tracking
|
|
async stopTracking(taskId) {
|
|
const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time/stop`, {
|
|
method: 'POST'
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
// Get active sessions
|
|
async getActiveSessions() {
|
|
const response = await fetch(`${this.apiUrl}/api/time/active`);
|
|
return response.json();
|
|
}
|
|
|
|
// Get task time data
|
|
async getTaskTimeData(taskId) {
|
|
const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time`);
|
|
return response.json();
|
|
}
|
|
|
|
// Get time summary
|
|
async getTimeSummary(period = 'today', fromDate = null, toDate = null) {
|
|
let url = `${this.apiUrl}/api/time/summary?period=${period}`;
|
|
if (fromDate) url += `&from=${fromDate}`;
|
|
if (toDate) url += `&to=${toDate}`;
|
|
|
|
const response = await fetch(url);
|
|
return response.json();
|
|
}
|
|
|
|
// Get daily dashboard data
|
|
async getDashboard() {
|
|
const [activeSessions, todaySummary, weekSummary] = await Promise.all([
|
|
this.getActiveSessions(),
|
|
this.getTimeSummary('today'),
|
|
this.getTimeSummary('week')
|
|
]);
|
|
|
|
return {
|
|
active: activeSessions.data,
|
|
today: todaySummary.data,
|
|
week: weekSummary.data
|
|
};
|
|
}
|
|
|
|
// Toggle time tracking for a task
|
|
async toggleTracking(taskId, description = null) {
|
|
const activeSessions = await this.getActiveSessions();
|
|
const isCurrentlyTracking = activeSessions.data.activeSessions.some(
|
|
session => session.task.id === taskId
|
|
);
|
|
|
|
if (isCurrentlyTracking) {
|
|
return await this.stopTracking(taskId);
|
|
} else {
|
|
return await this.startTracking(taskId, description);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Usage examples
|
|
const tracker = new TimeTracker();
|
|
|
|
// Start tracking with description
|
|
await tracker.startTracking('projects/api-work.md', 'Implementing time tracking endpoints');
|
|
|
|
// Get active sessions
|
|
const active = await tracker.getActiveSessions();
|
|
console.log(`Currently tracking ${active.data.totalActiveSessions} tasks`);
|
|
|
|
// Get today's summary
|
|
const today = await tracker.getTimeSummary('today');
|
|
console.log(`Today: ${today.data.summary.totalHours} hours across ${today.data.summary.tasksWithTime} tasks`);
|
|
|
|
// Get weekly breakdown
|
|
const week = await tracker.getTimeSummary('week');
|
|
console.log('Top projects this week:');
|
|
week.data.topProjects.forEach(project => {
|
|
console.log(`- ${project.project}: ${Math.round(project.minutes / 60 * 100) / 100} hours`);
|
|
});
|
|
|
|
// Toggle tracking (start if stopped, stop if running)
|
|
await tracker.toggleTracking('projects/documentation.md', 'Writing API examples');
|
|
|
|
// Get comprehensive dashboard
|
|
const dashboard = await tracker.getDashboard();
|
|
console.log('Time Tracking Dashboard:', {
|
|
activeNow: dashboard.active.totalActiveSessions,
|
|
todayHours: dashboard.today.summary.totalHours,
|
|
weekHours: dashboard.week.summary.totalHours,
|
|
topTaskToday: dashboard.today.topTasks[0]?.title || 'None'
|
|
});
|
|
```
|
|
|
|
### Time Analytics Dashboard
|
|
|
|
```python
|
|
import requests
|
|
from datetime import datetime, timedelta
|
|
import json
|
|
|
|
class TimeAnalytics:
|
|
def __init__(self, api_url='http://localhost:8080'):
|
|
self.api_url = api_url
|
|
|
|
def get_time_summary(self, period='today', from_date=None, to_date=None):
|
|
params = {'period': period}
|
|
if from_date:
|
|
params['from'] = from_date
|
|
if to_date:
|
|
params['to'] = to_date
|
|
|
|
response = requests.get(f'{self.api_url}/api/time/summary', params=params)
|
|
return response.json()
|
|
|
|
def generate_weekly_report(self):
|
|
"""Generate a comprehensive weekly time tracking report"""
|
|
week_data = self.get_time_summary('week')
|
|
|
|
if not week_data['success']:
|
|
return None
|
|
|
|
data = week_data['data']
|
|
summary = data['summary']
|
|
|
|
report = {
|
|
'period': f"{data['dateRange']['from'][:10]} to {data['dateRange']['to'][:10]}",
|
|
'total_hours': summary['totalHours'],
|
|
'avg_hours_per_day': round(summary['totalHours'] / 7, 2),
|
|
'tasks_worked_on': summary['tasksWithTime'],
|
|
'productivity_score': min(100, round((summary['totalHours'] / 40) * 100, 1)),
|
|
'top_focus_areas': {
|
|
'projects': data['topProjects'][:3],
|
|
'tags': data['topTags'][:3],
|
|
'tasks': data['topTasks'][:5]
|
|
}
|
|
}
|
|
|
|
return report
|
|
|
|
def get_project_breakdown(self, days=30):
|
|
"""Get time breakdown by project for the last N days"""
|
|
end_date = datetime.now().isoformat()[:10]
|
|
start_date = (datetime.now() - timedelta(days=days)).isoformat()[:10]
|
|
|
|
data = self.get_time_summary('custom', start_date, end_date)
|
|
|
|
if data['success']:
|
|
return {
|
|
'period_days': days,
|
|
'total_hours': data['data']['summary']['totalHours'],
|
|
'projects': data['data']['topProjects']
|
|
}
|
|
return None
|
|
|
|
# Usage
|
|
analytics = TimeAnalytics()
|
|
|
|
# Weekly report
|
|
report = analytics.generate_weekly_report()
|
|
print(f"Weekly Report ({report['period']}):")
|
|
print(f"- Total: {report['total_hours']} hours")
|
|
print(f"- Daily average: {report['avg_hours_per_day']} hours")
|
|
print(f"- Productivity score: {report['productivity_score']}%")
|
|
print(f"- Top project: {report['top_focus_areas']['projects'][0]['project']}")
|
|
|
|
# Project breakdown
|
|
projects = analytics.get_project_breakdown(30)
|
|
print(f"\nLast 30 days project breakdown:")
|
|
for project in projects['projects']:
|
|
percentage = round((project['minutes'] / (projects['total_hours'] * 60)) * 100, 1)
|
|
print(f"- {project['project']}: {round(project['minutes']/60, 1)}h ({percentage}%)")
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Common Errors
|
|
|
|
- `400 Bad Request` - Invalid request data
|
|
- `401 Unauthorized` - Invalid or missing auth token
|
|
- `404 Not Found` - Task not found
|
|
- `500 Internal Server Error` - Server error
|
|
|
|
### Rate Limiting
|
|
|
|
No rate limiting currently implemented. Use responsibly.
|
|
|
|
### CORS
|
|
|
|
CORS is enabled for all origins (`*`). API is intended for localhost use only.
|
|
|
|
## Security Notes
|
|
|
|
- **Localhost Only**: API server only accepts connections from localhost
|
|
- **Desktop Only**: API is not available on mobile platforms
|
|
- **Optional Auth**: Bearer token authentication is optional but recommended
|
|
- **No HTTPS**: Traffic is unencrypted (localhost only)
|
|
|
|
## Troubleshooting
|
|
|
|
### API Not Starting
|
|
|
|
1. Check that API is enabled in settings
|
|
2. Ensure port is not in use by another application
|
|
3. Try different port (1024-65535)
|
|
4. Check Obsidian console for errors
|
|
|
|
### Connection Refused
|
|
|
|
1. Verify API is enabled and Obsidian is running
|
|
2. Check correct port number
|
|
3. Ensure using `http://` not `https://`
|
|
4. Try `127.0.0.1` instead of `localhost`
|
|
|
|
### Authentication Errors
|
|
|
|
1. Verify token matches exactly (case-sensitive)
|
|
2. Include `Bearer ` prefix in Authorization header
|
|
3. Check for trailing spaces in token
|
|
|
|
|