summaryrefslogtreecommitdiff
path: root/makima/src/db/models.rs
blob: 45b0e5317ec2180d528f2bf68c4370777f814f28 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! Database models for the files table.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use utoipa::ToSchema;
use uuid::Uuid;

/// TranscriptEntry stored in JSONB - matches frontend TranscriptEntry
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptEntry {
    pub id: String,
    pub speaker: String,
    pub start: f32,
    pub end: f32,
    pub text: String,
    pub is_final: bool,
}

/// File record from the database.
#[derive(Debug, Clone, FromRow, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct File {
    pub id: Uuid,
    pub owner_id: Uuid,
    pub name: String,
    pub description: Option<String>,
    #[sqlx(json)]
    pub transcript: Vec<TranscriptEntry>,
    pub location: Option<String>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

/// Request payload for creating a new file.
#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct CreateFileRequest {
    /// Name of the file (auto-generated if not provided)
    pub name: Option<String>,
    /// Optional description
    pub description: Option<String>,
    /// Transcript entries
    pub transcript: Vec<TranscriptEntry>,
    /// Storage location (e.g., s3://bucket/path) - not used yet
    pub location: Option<String>,
}

/// Request payload for updating an existing file.
#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct UpdateFileRequest {
    /// New name (optional)
    pub name: Option<String>,
    /// New description (optional)
    pub description: Option<String>,
    /// New transcript (optional)
    pub transcript: Option<Vec<TranscriptEntry>>,
}

/// Response for file list endpoint.
#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct FileListResponse {
    pub files: Vec<FileSummary>,
    pub total: i64,
}

/// Summary of a file for list views (excludes full transcript).
#[derive(Debug, Clone, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct FileSummary {
    pub id: Uuid,
    pub name: String,
    pub description: Option<String>,
    pub transcript_count: usize,
    /// Duration derived from last transcript end time
    pub duration: Option<f32>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

impl From<File> for FileSummary {
    fn from(file: File) -> Self {
        let duration = file
            .transcript
            .iter()
            .map(|t| t.end)
            .fold(0.0_f32, f32::max);
        Self {
            id: file.id,
            name: file.name,
            description: file.description,
            transcript_count: file.transcript.len(),
            duration: if duration > 0.0 { Some(duration) } else { None },
            created_at: file.created_at,
            updated_at: file.updated_at,
        }
    }
}