fix: settle silent-only cases to Empty oneliner state
Introduce `TranscriptState { Pending, Silent, Content }` in
doctate-common as the canonical three-way state of a recording's
`.transcript.txt` sidecar. Previously each call-site projected the
raw `Option<String>` / `.exists()` onto its own 2-state view and the
projections disagreed: the UI treated a 0-byte silent transcript like
`Content` while the oneliner worker treated it like `Pending`,
leaving silent-only cases stuck on "generiere Titel …" forever with
no persisted oneliner.json.
`update_oneliner` now settles a silent-only case to
`OnelinerState::Empty` when no `Content` transcript exists and no
recording is still `Pending`, so the UI resolves to "unbenannt" and
recovery treats it as terminal.
Single reader: `paths::read_transcript_state`. All call-sites
(scan_recordings, compute_oneliner_display, has_pending_recordings,
all_transcripts_joined, read_recordings, enqueue_pending_for_user,
scan_m4as, cases_needing_oneliner_in, case_recordings.html) go
through the same typed abstraction and must handle `Silent` via
exhaustive match.
Regression tests:
- silent_case_empty_test: heal path settles silent-only case to
Empty without calling Ollama
- case_page_silent_only_shows_empty_not_generating: UI renders
"unbenannt", not "generiere Titel …"
This commit is contained in:
@@ -5,6 +5,7 @@ use std::sync::atomic::Ordering;
|
||||
use askama::Template;
|
||||
use axum::extract::{Query, State};
|
||||
use axum::response::Html;
|
||||
use doctate_common::TranscriptState;
|
||||
use doctate_common::oneliners::OnelinerState;
|
||||
use serde::Deserialize;
|
||||
use tracing::{info, warn};
|
||||
@@ -258,7 +259,7 @@ async fn compute_flags(
|
||||
|
||||
let non_failed: Vec<&RecordingView> = recordings.iter().filter(|r| !r.failed).collect();
|
||||
let all_transcribed =
|
||||
!non_failed.is_empty() && non_failed.iter().all(|r| r.transcript.is_some());
|
||||
!non_failed.is_empty() && non_failed.iter().all(|r| r.transcript.has_file());
|
||||
|
||||
let analyzable = !analyzing && all_transcribed;
|
||||
|
||||
@@ -486,7 +487,10 @@ pub async fn handle_case_page(
|
||||
let flags = compute_flags(&case_dir, &recordings, config.llm_configured(), a_busy).await;
|
||||
|
||||
let recordings_count = recordings.len();
|
||||
let transcribed_count = recordings.iter().filter(|r| r.transcript.is_some()).count();
|
||||
let transcribed_count = recordings
|
||||
.iter()
|
||||
.filter(|r| r.transcript.has_file())
|
||||
.count();
|
||||
let case_id_short = case_id_str.chars().take(8).collect();
|
||||
let is_admin = user.is_admin();
|
||||
|
||||
@@ -732,7 +736,7 @@ async fn compute_case_view(
|
||||
&& recordings
|
||||
.iter()
|
||||
.filter(|r| !r.failed)
|
||||
.all(|r| r.transcript.is_some());
|
||||
.all(|r| r.transcript.has_file());
|
||||
|
||||
let oneliner = compute_oneliner_display(case_path, &recordings).await;
|
||||
|
||||
@@ -809,7 +813,7 @@ async fn compute_oneliner_display(
|
||||
&& recordings
|
||||
.iter()
|
||||
.filter(|r| !r.failed)
|
||||
.all(|r| r.transcript.is_some());
|
||||
.all(|r| r.transcript.has_file());
|
||||
let (state, _) = paths::read_oneliner_state(case_dir).await;
|
||||
|
||||
if non_failed_count == 0 {
|
||||
|
||||
Reference in New Issue
Block a user