refactor(sessions): show job name in automation groups and preserve action labels

This commit is contained in:
M57 committed 2026-09-05 23:02:46 +03:00
1 parent acc9c7ce9a
commit 1135f05011
4 files changed
+42 -11

No files matched your search

@@ -6,6 +6,7 @@ internal data class AutomationSessionGroup(
val key: String, val key: String,
val jobId: String?, val jobId: String?,
val sessions: List<SessionInfo>, val sessions: List<SessionInfo>,
val title: String? = null,
) )
// cron.scheduler mints durable run IDs as cron_{job_id}_{YYYYMMDD_HHMMSS}. // cron.scheduler mints durable run IDs as cron_{job_id}_{YYYYMMDD_HHMMSS}.
@@ -20,9 +21,18 @@ internal fun automationGroups(sessions: List<SessionInfo>): List<AutomationSessi
if (session.source == "cron") CRON_RUN_ID.matchEntire(session.id)?.groupValues?.get(1) else null if (session.source == "cron") CRON_RUN_ID.matchEntire(session.id)?.groupValues?.get(1) else null
jobId?.let { "job:$it" } ?: "session:${session.id}" jobId?.let { "job:$it" } ?: "session:${session.id}"
}.map { (key, runs) -> }.map { (key, runs) ->
val jobId = key.takeIf { it.startsWith("job:") }?.removePrefix("job:")
val title =
runs
.mapNotNull { it.title?.takeIf(String::isNotBlank) }
.firstOrNull()
?.substringBeforeLast(" · ")
?.trim()
?.takeIf(String::isNotBlank)
AutomationSessionGroup( AutomationSessionGroup(
key = key, key = key,
jobId = key.takeIf { it.startsWith("job:") }?.removePrefix("job:"), jobId = jobId,
title = title,
sessions = runs, sessions = runs,
) )
} }
@@ -617,7 +617,7 @@ fun SessionsScreen(
modifier = Modifier.size(18.dp), modifier = Modifier.size(18.dp),
) )
Spacer(modifier = Modifier.width(spacing.xs)) Spacer(modifier = Modifier.width(spacing.xs))
Text(stringResource(R.string.sessions_empty_cleanup_all_history)) Text(stringResource(R.string.sessions_empty_cleanup_desc))
} }
} }
} }
@@ -823,7 +823,7 @@ fun SessionsScreen(
modifier = Modifier.size(20.dp), modifier = Modifier.size(20.dp),
) )
Text( Text(
text = stringResource(R.string.sessions_action_prune_all_history), text = stringResource(R.string.sessions_action_prune),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = statusColors.warning, color = statusColors.warning,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
@@ -946,12 +946,13 @@ fun SessionsScreen(
Column { Column {
Text( Text(
text = text =
group.jobId?.let { group.title
stringResource( ?: group.jobId?.let {
R.string.sessions_section_automation_job, stringResource(
it, R.string.sessions_section_automation_job,
) it,
} ?: stringResource( )
} ?: stringResource(
R.string.sessions_section_automation_unknown, R.string.sessions_section_automation_unknown,
), ),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
-2
View File
@@ -590,13 +590,11 @@
<string name="sessions_section_automation_unknown">Automation run</string> <string name="sessions_section_automation_unknown">Automation run</string>
<string name="sessions_section_automation_runs">%1$d loaded run(s)</string> <string name="sessions_section_automation_runs">%1$d loaded run(s)</string>
<string name="sessions_action_prune">Prune</string> <string name="sessions_action_prune">Prune</string>
<string name="sessions_action_prune_all_history">Prune all history</string>
<string name="sessions_prune_title">Prune Old Sessions</string> <string name="sessions_prune_title">Prune Old Sessions</string>
<string name="sessions_prune_desc">Delete sessions older than the specified number of days.</string> <string name="sessions_prune_desc">Delete sessions older than the specified number of days.</string>
<string name="sessions_prune_days_label">Days threshold</string> <string name="sessions_prune_days_label">Days threshold</string>
<string name="sessions_prune_confirm">Prune</string> <string name="sessions_prune_confirm">Prune</string>
<string name="sessions_empty_cleanup_desc">Empty</string> <string name="sessions_empty_cleanup_desc">Empty</string>
<string name="sessions_empty_cleanup_all_history">Empty in all history</string>
<string name="sessions_empty_cleanup_title">Delete empty sessions?</string> <string name="sessions_empty_cleanup_title">Delete empty sessions?</string>
<string name="sessions_empty_cleanup_message">Delete %1$d empty session(s). Active and archived sessions are kept.</string> <string name="sessions_empty_cleanup_message">Delete %1$d empty session(s). Active and archived sessions are kept.</string>
<string name="sessions_empty_cleanup_confirm">Delete</string> <string name="sessions_empty_cleanup_confirm">Delete</string>
@@ -53,4 +53,26 @@ class HistorySessionGroupsTest {
val groups = automationGroups(listOf(SessionInfo("cron_job_20260905_100000", source = "cli"))) val groups = automationGroups(listOf(SessionInfo("cron_job_20260905_100000", source = "cli")))
assertNull(groups.single().jobId) assertNull(groups.single().jobId)
} }
@Test
fun derivesJobTitleFromLatestSessionWhileGroupingByJobId() {
val groups =
automationGroups(
listOf(
SessionInfo(
"cron_nightly_20260905_100000",
title = "Nightly Backup · Sep 05 10:00",
source = "cron",
),
SessionInfo(
"cron_nightly_20260905_090000",
title = "Nightly Backup · Sep 05 09:00",
source = "cron",
),
),
)
assertEquals(1, groups.size)
assertEquals("nightly", groups.single().jobId)
assertEquals("Nightly Backup", groups.single().title)
}
} }