# @things-factory/labeling

[![Version](https://img.shields.io/npm/v/@things-factory/labeling.svg)](https://www.npmjs.com/package/@things-factory/labeling)
[![License](https://img.shields.io/npm/l/@things-factory/labeling.svg)](https://github.com/hatiolab/things-factory/blob/master/LICENSE)

Labeling workflow orchestration module for Things-Factory platform.

## Overview

This module provides comprehensive workflow orchestration capabilities for data labeling processes, including both **backend services** (GraphQL API, database persistence) and **frontend UI components** (workflow builder, list view).

## Features

### Backend (Server) ✅

- **Workflow Management**: Create, execute, pause, resume, and delete labeling workflows
- **TypeORM Entities**: Persistent storage with PostgreSQL/SQLite support
- **GraphQL API**: Full-featured GraphQL resolvers with type safety
- **Multi-tenancy**: Domain-based data isolation
- **Execution Tracking**: Monitor workflow and step-level execution status
- **Error Handling**: Configurable retry logic and continue-on-error behavior
- **Step Types**:
  - `import_data` - Import data from datasets or external sources
  - `generate_predictions` - Generate AI predictions for pre-labeling
  - `wait_for_annotations` - Wait for human annotations with completion tracking
  - `sync_annotations` - Sync labels back to dataset
  - `validate_quality` - Quality validation checks
  - `export_results` - Export annotations to external systems
  - `notification` - Send alerts and notifications

### Frontend (Client) ✅

- **Workflow List Page**: View and manage all workflows with filtering
- **Workflow Builder**: Visual workflow creation and editing interface
- **Step Editor**: Configure individual workflow steps with JSON config
- **Status Indicators**: Color-coded workflow status (Active, Draft, Paused, Failed)
- **CRUD Operations**: Complete workflow lifecycle management
- **Responsive Design**: Material Design 3 themed UI components

## Terminology

This module uses **"workflow"** terminology to distinguish from the existing `scenario` entity in Things-Factory:

- `LabelingWorkflow` - A workflow definition
- `WorkflowExecution` - A workflow execution instance
- `WorkflowStep` - Individual step in a workflow
- `WorkflowStatus` - Workflow state (Draft, Active, Paused, Completed, Failed)

## Installation

```bash
npm install @things-factory/labeling
```

## Dependencies

### Server
- `@things-factory/integration-label-studio` - Label Studio API integration
- `@things-factory/dataset` - Dataset management
- `@things-factory/ai-inference` - AI model inference
- `@things-factory/auth-base` - Authentication
- `@things-factory/shell` - Core framework
- `typeorm` - Database ORM

### Client
- `@operato/shell` - Shell components and navigation
- `@operato/graphql` - GraphQL client
- `lit` - Web components framework
- `graphql-tag` - GraphQL query parsing
- `i18next` - Internationalization

## Usage

### Backend

```typescript
import { LabelingWorkflowService } from '@things-factory/labeling'

// Create workflow service
const service = new LabelingWorkflowService()

// Create workflow
const workflow = await service.createLabelingWorkflow({
  name: 'Image Classification Workflow',
  description: 'Automated image classification with AI',
  projectId: 123,
  triggerType: 'manual',
  steps: [
    {
      name: 'Import Images',
      type: 'import_data',
      config: JSON.stringify({
        sourceType: 'dataset',
        dataSetId: 'my-dataset',
        limit: 1000
      }),
      continueOnError: false
    },
    {
      name: 'Generate Predictions',
      type: 'generate_predictions',
      config: JSON.stringify({
        modelId: 'resnet50-v2',
        confidenceThreshold: 0.7
      }),
      continueOnError: true
    }
  ],
  autoStart: false
}, context)

// Execute workflow
const execution = await service.executeLabelingWorkflow({
  workflowId: workflow.id
}, context)

// Monitor execution
const status = await service.workflowExecution(execution.executionId, context)
```

### Frontend

```typescript
// Import UI components
import '@things-factory/labeling/dist-client/pages/workflow-list'
import '@things-factory/labeling/dist-client/pages/workflow-builder'

// Use in HTML
<workflow-list-page></workflow-list-page>
<workflow-builder-page></workflow-builder-page>

// Edit existing workflow
<workflow-builder-page workflowId="workflow-uuid"></workflow-builder-page>
```

### Menu Integration

```typescript
import { getMenuTemplate } from '@things-factory/labeling'

// Get menu items (owner = true for admin features)
const menuItems = getMenuTemplate(true)

// Integrate into your app's menu
export function getAppMenu(owner: boolean) {
  return [
    ...yourMenuItems,
    ...getMenuTemplate(owner)
  ]
}
```

### Route Configuration

```typescript
import route from '@things-factory/labeling/dist-client/route'

// Available routes:
// - /workflows          → Workflow list page
// - /workflow-builder   → Create new workflow
// - /workflow-builder/{id} → Edit existing workflow

// In your app's route.ts:
export default function appRoute(page: string) {
  // Try labeling routes first
  const labelingRoute = route(page)
  if (labelingRoute) return labelingRoute

  // Your other routes
  switch (page) {
    // ...
  }
}
```

## GraphQL API

### Mutations

```graphql
# Create workflow
mutation CreateWorkflow($input: CreateWorkflowRequest!) {
  createLabelingWorkflow(input: $input) {
    id
    name
    status
    steps {
      name
      type
      order
    }
  }
}

# Update workflow
mutation UpdateWorkflow($workflowId: String!, $input: UpdateWorkflowRequest!) {
  updateLabelingWorkflow(workflowId: $workflowId, input: $input) {
    id
    name
  }
}

# Execute workflow
mutation ExecuteWorkflow($input: ExecuteWorkflowRequest!) {
  executeLabelingWorkflow(input: $input) {
    executionId
    status
    summary
  }
}

# Pause workflow
mutation PauseWorkflow($workflowId: String!) {
  pauseLabelingWorkflow(workflowId: $workflowId) {
    id
    status
  }
}

# Resume workflow
mutation ResumeWorkflow($workflowId: String!) {
  resumeLabelingWorkflow(workflowId: $workflowId) {
    id
    status
  }
}

# Delete workflow
mutation DeleteWorkflow($workflowId: String!) {
  deleteLabelingWorkflow(workflowId: $workflowId)
}
```

### Queries

```graphql
# List workflows
query GetWorkflows($projectId: Int) {
  labelingWorkflows(projectId: $projectId) {
    items {
      id
      name
      description
      status
      projectId
      triggerType
      steps {
        name
        type
        config
      }
      createdAt
      lastExecutedAt
    }
    total
  }
}

# Get workflow details
query GetWorkflow($workflowId: String!) {
  labelingWorkflow(workflowId: $workflowId) {
    id
    name
    description
    projectId
    steps {
      name
      type
      order
      config
      continueOnError
    }
    status
  }
}

# Get execution status
query GetExecution($executionId: String!) {
  workflowExecution(executionId: $executionId) {
    id
    workflowName
    status
    steps {
      stepName
      stepType
      status
      order
      startedAt
      completedAt
      durationMs
      output
      error
    }
    startedAt
    completedAt
    totalDurationMs
    summary
  }
}
```

## Database Schema

### Tables

**labeling_workflows**
- `id` (UUID) - Primary key
- `domain_id` - Multi-tenancy
- `creator_id` - User who created workflow
- `name`, `description` - Workflow metadata
- `project_id` - Label Studio project ID
- `trigger_type` - Manual/Scheduled/Event
- `trigger_config` - Trigger configuration (JSON)
- `steps` (JSONB) - Workflow step definitions
- `status` - Draft/Active/Paused/Completed/Failed
- `last_executed_at`, `next_execution_at` - Scheduling
- `created_at`, `updated_at`, `deleted_at` - Timestamps

**workflow_executions**
- `id` (UUID) - Execution instance ID
- `workflow_id` - Reference to workflow
- `status` - running/completed/failed
- `started_at`, `completed_at`, `total_duration_ms` - Timing
- `summary`, `error` - Results

**workflow_execution_steps**
- `id` (UUID) - Step instance ID
- `execution_id` - Reference to execution
- `step_name`, `step_type` - Step info
- `order` - Execution sequence
- `status` - pending/running/completed/failed/skipped
- `started_at`, `completed_at`, `duration_ms` - Timing
- `output`, `error` - Results

### Indexes

- `ix_labeling_workflow_0` - (domain, projectId, status)
- `ix_labeling_workflow_1` - (domain, name)
- `ix_workflow_execution_0` - (domain, workflow, status)
- `ix_workflow_execution_1` - (domain, startedAt)
- `ix_workflow_execution_step_0` - (domain, execution, status)
- `ix_workflow_execution_step_1` - (domain, execution, order)

## Step Configuration Examples

### Import Data Step
```json
{
  "sourceType": "dataset",
  "dataSetId": "dataset-uuid",
  "imageField": "image",
  "limit": 1000
}
```

### Generate Predictions Step
```json
{
  "modelId": "resnet50-v2",
  "confidenceThreshold": 0.7,
  "forceRegenerate": false
}
```

### Wait for Annotations Step
```json
{
  "minCompletionRate": 0.9,
  "autoProceed": false
}
```

### Sync Annotations Step
```json
{
  "completedOnly": true,
  "targetDataSet": "dataset-uuid"
}
```

### Validate Quality Step
```json
{
  "minQualityScore": 0.8,
  "validationRules": ["consistency", "completeness"]
}
```

## Architecture

```
@things-factory/labeling
├── server/                    # Backend
│   ├── entities/             # TypeORM entities
│   │   ├── labeling-workflow.ts
│   │   ├── workflow-execution.ts
│   │   └── workflow-execution-step.ts
│   ├── service/              # Business logic
│   │   └── labeling-workflow-service.ts
│   ├── types/                # GraphQL types
│   │   └── workflow-types.ts
│   └── index.ts              # Exports
│
└── client/                    # Frontend
    ├── pages/                # Page components
    │   ├── workflow-list.ts
    │   └── workflow-builder.ts
    ├── route.ts              # Route configuration
    ├── menu.ts               # Menu configuration
    └── index.ts              # Exports
```

## Integration Examples

### With operato-dataset

```typescript
// Extend with dataset-specific functionality
import { LabelingWorkflowService } from '@things-factory/labeling'

@Resolver()
export class DatasetLabelingWorkflow {
  private workflowService = new LabelingWorkflowService()

  @Mutation()
  async createDatasetLabelingWorkflow(
    @Arg('dataSetId') dataSetId: string,
    @Arg('projectId', type => Int) projectId: number
  ) {
    return await this.workflowService.createLabelingWorkflow({
      name: `Auto-label ${dataSetId}`,
      projectId,
      steps: [
        // Dataset-specific preset steps
      ]
    }, context)
  }
}
```

### Custom UI Wrapper

```typescript
// Create custom workflow builder with presets
@customElement('custom-workflow-builder')
export class CustomWorkflowBuilder extends LitElement {
  render() {
    return html`
      <workflow-builder-page
        .defaultConfig=${{
          triggerType: 'event',
          steps: [/* preset steps */]
        }}
      ></workflow-builder-page>
    `
  }
}
```

## Documentation

- **[UI Documentation](./UI_DOCUMENTATION.md)** - Complete UI component reference
- **[Entity Implementation](./ENTITY_IMPLEMENTATION.md)** - Database schema details
- **[Integration Guide](../operato-dataset/LABELING_INTEGRATION.md)** - Integration examples

## Development

```bash
# Install dependencies
npm install

# Build both server and client
npm run build

# Build server only
npm run build:server

# Build client only
npm run build:client

# Clean build artifacts
npm run clean
```

## Best Practices

1. **Validate JSON configurations** before saving workflows
2. **Use continue-on-error judiciously** for non-critical steps
3. **Monitor execution status** for long-running workflows
4. **Set appropriate limits** in import_data steps
5. **Use descriptive step names** for clarity

## Troubleshooting

**Issue**: Workflow not saving
- **Solution**: Check JSON configuration syntax, ensure required fields are filled

**Issue**: Execution stuck
- **Solution**: Check workflow_execution_steps table for failed steps, review error messages

**Issue**: UI components not loading
- **Solution**: Verify route configuration, check browser console for import errors

## Future Enhancements

- [x] Database persistence with TypeORM
- [x] Visual workflow builder UI
- [x] GraphQL API
- [ ] Schedule-based triggers (cron support)
- [ ] Event-based triggers (webhook integration)
- [ ] Drag & drop workflow builder
- [ ] Real-time execution monitoring
- [ ] Workflow templates library
- [ ] Conditional branching
- [ ] Parallel step execution
- [ ] Workflow versioning

## License

MIT

## Contributing

Contributions are welcome! Please follow the Things-Factory contribution guidelines.

## Support

For issues and questions, please open an issue on GitHub or contact the Things-Factory team.

## Changelog

### 9.1.0 (2024-10-14)

**Added**:
- ✅ Complete backend with TypeORM entities
- ✅ GraphQL API for workflow management
- ✅ Workflow list page UI component
- ✅ Workflow builder page UI component
- ✅ Step editor with JSON configuration
- ✅ Route and menu integration
- ✅ Multi-tenancy support
- ✅ PostgreSQL and SQLite compatibility
- ✅ Comprehensive documentation

**Changed**:
- Renamed from "scenario" to "workflow" terminology
- Separated from integration-label-studio package

**Fixed**:
- Parameter order issues in GraphQL resolvers
- Type conflicts between entities and GraphQL types
