Refactor oneliner storage to use enum

The oneliner is now stored as `OnelinerState` enum which can represent
three states: `Ready`, `Empty` or `Error`. This allows the client to
differentiate between a case with no medical content and a case where
the oneliner generation failed.

The `OnelinerState` enum is serialized to JSON and stored in
`oneliner.json` file. The `OnelinerEntry` struct has been updated to
reflect this change.

The client-desktop application has been updated to handle the new
`OnelinerState` enum and display appropriate UI elements for each state.

The server-side code has also been updated to read and write the
`OnelinerState` enum, and to handle the new file format.
The `reset_case_artefacts` function in
`server/src/routes/case_actions.rs` has been updated to remove
`oneliner.txt` and create `oneliner.json` instead.

The tests have been updated to reflect these changes.
This commit is contained in:
2026-04-20 12:37:48 +02:00
parent 224ee60363
commit d65720c671
15 changed files with 440 additions and 125 deletions
+35
View File
@@ -1,6 +1,8 @@
use std::path::{Path, PathBuf};
use doctate_common::oneliners::{ONELINER_FILENAME, OnelinerState};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use uuid::Uuid;
/// Absolute on-disk location of a case directory.
@@ -46,3 +48,36 @@ pub async fn write_delete_marker(case_dir: &Path, marker: &DeleteMarker) -> std:
.map_err(|e| std::io::Error::other(format!("serialize delete marker: {e}")))?;
tokio::fs::write(case_dir.join(DELETE_MARKER), bytes).await
}
/// Read the persisted oneliner state for a case.
///
/// Returns `(Some(state), Some(mtime))` when `oneliner.json` exists and
/// parses. Missing file → `(None, None)`. A file that exists but fails
/// to parse is treated as missing and logged — the next transcription
/// job will overwrite it with a well-formed state.
pub async fn read_oneliner_state(
case_dir: &Path,
) -> (Option<OnelinerState>, Option<OffsetDateTime>) {
let path = case_dir.join(ONELINER_FILENAME);
let bytes = match tokio::fs::read(&path).await {
Ok(b) => b,
Err(_) => return (None, None),
};
let state: OnelinerState = match serde_json::from_slice(&bytes) {
Ok(s) => s,
Err(e) => {
tracing::warn!(
path = %path.display(),
error = %e,
"malformed oneliner.json; treating as missing"
);
return (None, None);
}
};
let mtime = tokio::fs::metadata(&path)
.await
.ok()
.and_then(|m| m.modified().ok())
.map(OffsetDateTime::from);
(Some(state), mtime)
}