Refactor user config validation and add transcript display

Introduces a dedicated function `validate_and_index_users` to
encapsulate the logic for validating user configurations, checking for
duplicate slugs and API keys. This function returns a `Result` to handle
errors gracefully, and its usage in `load_users` is updated to panic
with a more informative error message.

Additionally, this commit modifies the web routing to fetch and display
audio transcripts. When scanning recordings, it now attempts to read a
corresponding `.transcript.txt` file. If found, the transcript content
is included in the `RecordingView` and rendered in the `cases.html`
template. If the transcript file is not found, a "Transcription pending"
message is displayed.

The `transcribe` function in `whisper.rs` is updated to explicitly set
the language to German (`language=de`), which can improve transcription
accuracy by skipping language detection. The `encode=false` query
parameter has been removed, as the service is designed to handle encoded
audio.

Finally, tests have been added for the new `validate_and_index_users`
function to ensure duplicate slugs and API keys are correctly rejected.
Integration tests in `web_test.rs` have been updated to verify the
rendering of transcripts and the pending status.
This commit is contained in:
2026-04-13 17:08:21 +02:00
parent 13375b8297
commit 1e3cc9574c
6 changed files with 93 additions and 13 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ async fn whisper_client_posts_multipart_and_returns_text() {
Mock::given(method("POST"))
.and(path("/asr"))
.and(query_param("output", "txt"))
.and(query_param("encode", "false"))
.and(query_param("language", "de"))
.respond_with(ResponseTemplate::new(200).set_body_string(expected))
.expect(1)
.mount(&server)
+9
View File
@@ -93,6 +93,11 @@ async fn web_index_shows_cases() {
b"fake audio 2",
)
.unwrap();
std::fs::write(
done_case.join("2026-04-12T09-00-00Z.transcript.txt"),
"Herzkatheter ohne Befund.",
)
.unwrap();
let app = doctate_server::create_router(config);
let response = app
@@ -113,6 +118,10 @@ async fn web_index_shows_cases() {
assert!(body.contains("dr_test"));
assert!(body.contains("<audio"));
assert!(body.contains("2026-04-13T10-30-00Z.m4a"));
// case1 has no transcript → pending placeholder
assert!(body.contains("Transcription pending"));
// case2 has a transcript → rendered
assert!(body.contains("Herzkatheter ohne Befund."));
let _ = std::fs::remove_dir_all(&data_path);
}