feat: implement tm list remote (#1185)
This commit is contained in:
170
packages/tm-core/src/mappers/TaskMapper.ts
Normal file
170
packages/tm-core/src/mappers/TaskMapper.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { Task, Subtask } from '../types/index.js';
|
||||
import { Database, Tables } from '../types/database.types.js';
|
||||
|
||||
type TaskRow = Tables<'tasks'>;
|
||||
type DependencyRow = Tables<'task_dependencies'>;
|
||||
|
||||
export class TaskMapper {
|
||||
/**
|
||||
* Maps database tasks to internal Task format
|
||||
*/
|
||||
static mapDatabaseTasksToTasks(
|
||||
dbTasks: TaskRow[],
|
||||
dbDependencies: DependencyRow[]
|
||||
): Task[] {
|
||||
if (!dbTasks || dbTasks.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Group dependencies by task_id
|
||||
const dependenciesByTaskId = this.groupDependenciesByTaskId(dbDependencies);
|
||||
|
||||
// Separate parent tasks and subtasks
|
||||
const parentTasks = dbTasks.filter((t) => !t.parent_task_id);
|
||||
const subtasksByParentId = this.groupSubtasksByParentId(dbTasks);
|
||||
|
||||
// Map parent tasks with their subtasks
|
||||
return parentTasks.map((taskRow) =>
|
||||
this.mapDatabaseTaskToTask(
|
||||
taskRow,
|
||||
subtasksByParentId.get(taskRow.id) || [],
|
||||
dependenciesByTaskId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a single database task to internal Task format
|
||||
*/
|
||||
static mapDatabaseTaskToTask(
|
||||
dbTask: TaskRow,
|
||||
dbSubtasks: TaskRow[],
|
||||
dependenciesByTaskId: Map<string, string[]>
|
||||
): Task {
|
||||
// Map subtasks
|
||||
const subtasks: Subtask[] = dbSubtasks.map((subtask, index) => ({
|
||||
id: index + 1, // Use numeric ID for subtasks
|
||||
parentId: dbTask.id,
|
||||
title: subtask.title,
|
||||
description: subtask.description || '',
|
||||
status: this.mapStatus(subtask.status),
|
||||
priority: this.mapPriority(subtask.priority),
|
||||
dependencies: dependenciesByTaskId.get(subtask.id) || [],
|
||||
details: (subtask.metadata as any)?.details || '',
|
||||
testStrategy: (subtask.metadata as any)?.testStrategy || '',
|
||||
createdAt: subtask.created_at,
|
||||
updatedAt: subtask.updated_at,
|
||||
assignee: subtask.assignee_id || undefined,
|
||||
complexity: subtask.complexity
|
||||
? this.mapComplexityToInternal(subtask.complexity)
|
||||
: undefined
|
||||
}));
|
||||
|
||||
return {
|
||||
id: dbTask.display_id || dbTask.id, // Use display_id if available
|
||||
title: dbTask.title,
|
||||
description: dbTask.description || '',
|
||||
status: this.mapStatus(dbTask.status),
|
||||
priority: this.mapPriority(dbTask.priority),
|
||||
dependencies: dependenciesByTaskId.get(dbTask.id) || [],
|
||||
details: (dbTask.metadata as any)?.details || '',
|
||||
testStrategy: (dbTask.metadata as any)?.testStrategy || '',
|
||||
subtasks,
|
||||
createdAt: dbTask.created_at,
|
||||
updatedAt: dbTask.updated_at,
|
||||
assignee: dbTask.assignee_id || undefined,
|
||||
complexity: dbTask.complexity
|
||||
? this.mapComplexityToInternal(dbTask.complexity)
|
||||
: undefined,
|
||||
effort: dbTask.estimated_hours || undefined,
|
||||
actualEffort: dbTask.actual_hours || undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups dependencies by task ID
|
||||
*/
|
||||
private static groupDependenciesByTaskId(
|
||||
dependencies: DependencyRow[]
|
||||
): Map<string, string[]> {
|
||||
const dependenciesByTaskId = new Map<string, string[]>();
|
||||
|
||||
if (dependencies) {
|
||||
for (const dep of dependencies) {
|
||||
const deps = dependenciesByTaskId.get(dep.task_id) || [];
|
||||
deps.push(dep.depends_on_task_id);
|
||||
dependenciesByTaskId.set(dep.task_id, deps);
|
||||
}
|
||||
}
|
||||
|
||||
return dependenciesByTaskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups subtasks by their parent ID
|
||||
*/
|
||||
private static groupSubtasksByParentId(
|
||||
tasks: TaskRow[]
|
||||
): Map<string, TaskRow[]> {
|
||||
const subtasksByParentId = new Map<string, TaskRow[]>();
|
||||
|
||||
for (const task of tasks) {
|
||||
if (task.parent_task_id) {
|
||||
const subtasks = subtasksByParentId.get(task.parent_task_id) || [];
|
||||
subtasks.push(task);
|
||||
subtasksByParentId.set(task.parent_task_id, subtasks);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort subtasks by subtask_position for each parent
|
||||
for (const subtasks of subtasksByParentId.values()) {
|
||||
subtasks.sort((a, b) => a.subtask_position - b.subtask_position);
|
||||
}
|
||||
|
||||
return subtasksByParentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps database status to internal status
|
||||
*/
|
||||
private static mapStatus(
|
||||
status: Database['public']['Enums']['task_status']
|
||||
): Task['status'] {
|
||||
switch (status) {
|
||||
case 'todo':
|
||||
return 'pending';
|
||||
case 'in_progress':
|
||||
return 'in-progress';
|
||||
case 'done':
|
||||
return 'done';
|
||||
default:
|
||||
return 'pending';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps database priority to internal priority
|
||||
*/
|
||||
private static mapPriority(
|
||||
priority: Database['public']['Enums']['task_priority']
|
||||
): Task['priority'] {
|
||||
switch (priority) {
|
||||
case 'urgent':
|
||||
return 'critical';
|
||||
default:
|
||||
return priority as Task['priority'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps numeric complexity to descriptive complexity
|
||||
*/
|
||||
private static mapComplexityToInternal(
|
||||
complexity: number
|
||||
): Task['complexity'] {
|
||||
if (complexity <= 2) return 'simple';
|
||||
if (complexity <= 5) return 'moderate';
|
||||
if (complexity <= 8) return 'complex';
|
||||
return 'very-complex';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user