feat: add database schema, migrations, and API endpoints for span annotations
- Add span_label_types and span_annotations tables to schema - Seed default span label types (bull_flag, bear_flag, etc.) - Implement CRUD API endpoints for span label types - Implement CRUD API endpoints for span annotations - Add time swap validation in POST endpoint (start_time <= end_time)
This commit is contained in:
parent
8a7eb1fb08
commit
dadf515406
11 changed files with 1131 additions and 0 deletions
|
|
@ -38,3 +38,28 @@ export const annotations = sqliteTable('annotations', {
|
|||
color: text('color').default('#3b82f6'), // hex color code
|
||||
created_at: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const spanLabelTypes = sqliteTable('span_label_types', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull().unique(), // internal name (e.g., 'bull_flag')
|
||||
display_name: text('display_name').notNull(), // UI label (e.g., 'Bull Flag')
|
||||
color: text('color').notNull(), // hex color for rectangle fill
|
||||
hotkey: text('hotkey'), // keyboard shortcut (e.g., '1')
|
||||
is_active: integer('is_active').notNull().default(1), // 1 = active, 0 = inactive
|
||||
sort_order: integer('sort_order').notNull().default(0), // display order
|
||||
created_at: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const spanAnnotations = sqliteTable('span_annotations', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
chart_id: integer('chart_id').notNull().references(() => charts.id),
|
||||
start_time: integer('start_time').notNull(), // Unix timestamp of first candle
|
||||
end_time: integer('end_time').notNull(), // Unix timestamp of last candle
|
||||
label: text('label').notNull(), // pattern name referencing span_label_types.name
|
||||
confidence: integer('confidence'), // 1-5 scale, nullable
|
||||
outcome: text('outcome'), // 'win'|'loss'|'breakeven'|null
|
||||
notes: text('notes'), // free-text, nullable
|
||||
sub_spans: text('sub_spans'), // JSON array of sub-span objects, nullable
|
||||
color: text('color').notNull().default('#2196F3'), // hex color
|
||||
created_at: integer('created_at').notNull(),
|
||||
});
|
||||
|
|
|
|||
82
src/lib/db/seed-span-label-types.ts
Normal file
82
src/lib/db/seed-span-label-types.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { db } from './index';
|
||||
import { spanLabelTypes } from './schema';
|
||||
|
||||
export async function seedSpanLabelTypes() {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
const defaultTypes = [
|
||||
{
|
||||
name: 'bull_flag',
|
||||
display_name: 'Bull Flag',
|
||||
color: '#4CAF50',
|
||||
hotkey: '1',
|
||||
is_active: 1,
|
||||
sort_order: 1,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'bear_flag',
|
||||
display_name: 'Bear Flag',
|
||||
color: '#F44336',
|
||||
hotkey: '2',
|
||||
is_active: 1,
|
||||
sort_order: 2,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'head_and_shoulders',
|
||||
display_name: 'Head and Shoulders',
|
||||
color: '#9C27B0',
|
||||
hotkey: '3',
|
||||
is_active: 1,
|
||||
sort_order: 3,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'double_bottom',
|
||||
display_name: 'Double Bottom',
|
||||
color: '#2196F3',
|
||||
hotkey: '4',
|
||||
is_active: 1,
|
||||
sort_order: 4,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'wedge_up',
|
||||
display_name: 'Wedge Up',
|
||||
color: '#FF9800',
|
||||
hotkey: '5',
|
||||
is_active: 1,
|
||||
sort_order: 5,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'wedge_down',
|
||||
display_name: 'Wedge Down',
|
||||
color: '#FF5722',
|
||||
hotkey: '6',
|
||||
is_active: 1,
|
||||
sort_order: 6,
|
||||
created_at: now,
|
||||
},
|
||||
{
|
||||
name: 'custom',
|
||||
display_name: 'Custom',
|
||||
color: '#607D8B',
|
||||
hotkey: '7',
|
||||
is_active: 1,
|
||||
sort_order: 7,
|
||||
created_at: now,
|
||||
},
|
||||
];
|
||||
|
||||
// Check if types already exist
|
||||
const existing = await db.select().from(spanLabelTypes);
|
||||
|
||||
if (existing.length === 0) {
|
||||
await db.insert(spanLabelTypes).values(defaultTypes);
|
||||
console.log('Seeded default span label types');
|
||||
} else {
|
||||
console.log('Span label types already exist, skipping seed');
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue