diff --git a/frontend/js/db.js b/frontend/js/db.js index 6dbed57..364f7a0 100644 --- a/frontend/js/db.js +++ b/frontend/js/db.js @@ -1001,6 +1001,65 @@ async function splitGpsSession(sessionId, part2Ids, part1EndTime, part2StartTime return String(newId); } +// Undo a merge: split a merged session back into its original sessions by orig_session_id +// (merge preserved it on every point). The current session keeps its own original group; each +// other orig_session_id gets its session row recreated (metadata copied). Points are retagged +// (orig_session_id cleared, seq cleared → timestamp order). Returns the list of resulting +// session id_strs (incl. the current one), or null if this session isn't a merge. The caller +// recomputes stats + times for each (computeStats + finishGpsSession). +async function unmergeGpsSession(sessionId) { + const s = await getGpsSession(sessionId); + if (!s) return null; + const rows = await select( + "SELECT DISTINCT CAST(orig_session_id AS TEXT) AS o FROM gps_points WHERE session_id = CAST(?1 AS INTEGER) AND orig_session_id IS NOT NULL", + [s.id_str] + ); + const origs = rows.map(r => r.o).filter(Boolean); + if (origs.length < 2) return null; // not a merge — nothing to split + const resultIds = []; + for (const oid of origs) { + if (oid !== s.id_str) { + await execute( + `INSERT INTO gps_sessions (id, date, start_time, end_time, activity_type, source, bike_id) + VALUES (CAST(?1 AS INTEGER), ?2, ?3, ?4, ?5, ?6, ?7)`, + [oid, s.date, s.start_time, s.end_time, s.activity_type, s.source ?? "recorded", s.bike_id ?? null] + ); + await execute( + "UPDATE gps_points SET session_id = CAST(?1 AS INTEGER), orig_session_id = NULL, seq = NULL WHERE session_id = CAST(?2 AS INTEGER) AND orig_session_id = CAST(?1 AS TEXT)", + [oid, s.id_str] + ); + } else { + await execute( + "UPDATE gps_points SET orig_session_id = NULL, seq = NULL WHERE session_id = CAST(?1 AS INTEGER) AND orig_session_id = CAST(?1 AS TEXT)", + [s.id_str] + ); + } + resultIds.push(oid); + } + // Fix each piece's date/times from its own points (merge had stretched them to the whole span). + for (const id of resultIds) { + const ts = await select("SELECT MIN(ts) AS a, MAX(ts) AS b FROM gps_points WHERE session_id = CAST(?1 AS INTEGER)", [id]); + const a = ts[0] && ts[0].a; + if (a) { + const hm = (t) => new Date(t).toTimeString().slice(0, 8); + await execute("UPDATE gps_sessions SET date = ?1, start_time = ?2, end_time = ?3 WHERE id = CAST(?4 AS INTEGER)", + [new Date(a).toISOString().slice(0, 10), hm(a), hm(ts[0].b), id]); + } + } + return resultIds; +} + +// How many distinct original sessions are glued into this one (>1 ⇒ it's a merge that can be split). +async function countMergedOrigins(sessionId) { + const s = await getGpsSession(sessionId); + if (!s) return 0; + const r = await select( + "SELECT COUNT(DISTINCT orig_session_id) AS c FROM gps_points WHERE session_id = CAST(?1 AS INTEGER) AND orig_session_id IS NOT NULL", + [s.id_str] + ); + return (r[0] && r[0].c) || 0; +} + async function importGpxSession(session, points) { await execute( `INSERT INTO gps_sessions (date, start_time, end_time, activity_type, @@ -1389,6 +1448,8 @@ window.DB.getGpsSession = getGpsSession; window.DB.getGpsPoints = getGpsPoints; window.DB.deleteGpsSession = deleteGpsSession; window.DB.mergeGpsSessions = mergeGpsSessions; +window.DB.unmergeGpsSession = unmergeGpsSession; +window.DB.countMergedOrigins = countMergedOrigins; window.DB.splitGpsSession = splitGpsSession; window.DB.importGpxSession = importGpxSession; window.DB.saveGapFill = saveGapFill;