feat: upload audio, get a private Swedish transcript, discuss it (#33, ADR-033) #39
@@ -52,12 +52,20 @@ func (s *Store) CompleteUpload(ctx context.Context, userID, id string, t domain.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("store: complete upload: marshal segments: %w", err)
|
return fmt.Errorf("store: complete upload: marshal segments: %w", err)
|
||||||
}
|
}
|
||||||
|
// A nil Reconciliation must be SQL NULL, not the JSON text "null": the page decides
|
||||||
|
// whether to claim a second opinion by whether this column is NULL.
|
||||||
|
var rec []byte
|
||||||
|
if t.Reconciliation != nil {
|
||||||
|
if rec, err = json.Marshal(t.Reconciliation); err != nil {
|
||||||
|
return fmt.Errorf("store: complete upload: marshal reconciliation: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return s.updateUpload(ctx, userID, id, "complete upload",
|
return s.updateUpload(ctx, userID, id, "complete upload",
|
||||||
`UPDATE uploaded_transcripts
|
`UPDATE uploaded_transcripts
|
||||||
SET status = 'done', content = $3, segments = $4, endpoint = $5,
|
SET status = 'done', content = $3, segments = $4, endpoint = $5,
|
||||||
audio_seconds = $6, error_kind = NULL, finished_at = NOW()
|
audio_seconds = $6, reconciliation = $7, error_kind = NULL, finished_at = NOW()
|
||||||
WHERE id = $1 AND user_id = $2`,
|
WHERE id = $1 AND user_id = $2`,
|
||||||
t.Text, segs, t.Endpoint, t.AudioSeconds)
|
t.Text, segs, t.Endpoint, t.AudioSeconds, rec)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailUpload moves an upload to failed with a typed reason.
|
// FailUpload moves an upload to failed with a typed reason.
|
||||||
@@ -157,19 +165,19 @@ func (s *Store) FailInterruptedUploads(ctx context.Context) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uploadSelect = `SELECT id, user_id, title, status, content, segments, endpoint,
|
const uploadSelect = `SELECT id, user_id, title, status, content, segments, endpoint,
|
||||||
audio_seconds, error_kind, had_video, created_at, finished_at FROM uploaded_transcripts`
|
audio_seconds, error_kind, had_video, reconciliation, created_at, finished_at FROM uploaded_transcripts`
|
||||||
|
|
||||||
// scanUpload scans one uploadSelect row. withText controls whether the transcript
|
// scanUpload scans one uploadSelect row. withText controls whether the transcript
|
||||||
// body and segments are kept (GetUpload) or dropped (ListUploads).
|
// body and segments are kept (GetUpload) or dropped (ListUploads).
|
||||||
func scanUpload(row pgx.Row, u *domain.UploadedTranscript, withText bool) error {
|
func scanUpload(row pgx.Row, u *domain.UploadedTranscript, withText bool) error {
|
||||||
var (
|
var (
|
||||||
status, errKind, endpoint, content *string
|
status, errKind, endpoint, content *string
|
||||||
segments []byte
|
segments, reconciliation []byte
|
||||||
seconds *float64
|
seconds *float64
|
||||||
finished *time.Time
|
finished *time.Time
|
||||||
)
|
)
|
||||||
if err := row.Scan(&u.ID, &u.UserID, &u.Title, &status, &content, &segments, &endpoint,
|
if err := row.Scan(&u.ID, &u.UserID, &u.Title, &status, &content, &segments, &endpoint,
|
||||||
&seconds, &errKind, &u.HadVideo, &u.CreatedAt, &finished); err != nil {
|
&seconds, &errKind, &u.HadVideo, &reconciliation, &u.CreatedAt, &finished); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Status = domain.UploadStatus(deref(status))
|
u.Status = domain.UploadStatus(deref(status))
|
||||||
@@ -184,6 +192,13 @@ func scanUpload(row pgx.Row, u *domain.UploadedTranscript, withText bool) error
|
|||||||
if !withText {
|
if !withText {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(reconciliation) > 0 {
|
||||||
|
var r domain.Reconciliation
|
||||||
|
if err := json.Unmarshal(reconciliation, &r); err != nil {
|
||||||
|
return fmt.Errorf("unmarshal reconciliation: %w", err)
|
||||||
|
}
|
||||||
|
u.Reconciliation = &r
|
||||||
|
}
|
||||||
u.Content = deref(content)
|
u.Content = deref(content)
|
||||||
if len(segments) > 0 {
|
if len(segments) > 0 {
|
||||||
if err := json.Unmarshal(segments, &u.Segments); err != nil {
|
if err := json.Unmarshal(segments, &u.Segments); err != nil {
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ type UploadedTranscript struct {
|
|||||||
// was used and the video was discarded; the page says so. It is set at upload
|
// was used and the video was discarded; the page says so. It is set at upload
|
||||||
// and never cleared (ADR-033).
|
// and never cleared (ADR-033).
|
||||||
HadVideo bool
|
HadVideo bool
|
||||||
|
// Reconciliation is how a merged transcript came about, or nil for a single
|
||||||
|
// engine (ADR-034). Set by Get, not by List.
|
||||||
|
Reconciliation *Reconciliation
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
FinishedAt time.Time // zero until the job reaches done or failed
|
FinishedAt time.Time // zero until the job reaches done or failed
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user