Skip to content

Commit 96f94d0

Browse files
authored
Feat: Prune logs to current selection. (#234)
* feat: Prune logs to current timeslider * fix: preserve trips for API errors. Non-Trips as grey polylines * feat: More trip colors
1 parent cabdc38 commit 96f94d0

File tree

5 files changed

+121
-13
lines changed

5 files changed

+121
-13
lines changed

src/App.js

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ import LogTable from "./LogTable";
88
import ToggleBar from "./ToggleBar";
99
import TripLogs from "./TripLogs";
1010
import CloudLogging from "./CloudLogging";
11-
import { uploadFile, getUploadedData, deleteUploadedData, uploadCloudLogs, saveDatasetAsJson } from "./localStorage";
11+
import {
12+
uploadFile,
13+
getUploadedData,
14+
deleteUploadedData,
15+
uploadCloudLogs,
16+
saveDatasetAsJson,
17+
saveToIndexedDB,
18+
} from "./localStorage";
1219
import _ from "lodash";
1320
import { getQueryStringValue, setQueryStringValue } from "./queryString";
1421
import "./global.css";
@@ -519,18 +526,80 @@ class App extends React.Component {
519526

520527
const handleSaveClick = async (e) => {
521528
e.stopPropagation();
522-
log(`Save initiated for dataset ${index}`);
529+
log(`Export initiated for dataset ${index}`);
523530

524531
// Close menu
525532
this.setState({ activeMenuIndex: null });
526533

527534
try {
528535
await saveDatasetAsJson(index);
529-
toast.success(`Dataset ${index + 1} saved successfully`);
530-
log(`Dataset ${index} saved successfully`);
536+
toast.success(`Dataset ${index + 1} exported successfully`);
537+
log(`Dataset ${index} exported successfully`);
531538
} catch (error) {
532-
console.error("Error saving dataset:", error);
533-
toast.error(`Error saving dataset: ${error.message}`);
539+
console.error("Error exporting dataset:", error);
540+
toast.error(`Error exporting dataset: ${error.message}`);
541+
}
542+
};
543+
544+
const handlePruneClick = async (e) => {
545+
e.stopPropagation();
546+
log(`Prune initiated for dataset ${index}`);
547+
548+
// Close menu
549+
this.setState({ activeMenuIndex: null });
550+
551+
try {
552+
const { minTime, maxTime } = this.state.timeRange;
553+
const data = await getUploadedData(index);
554+
555+
if (!data || !data.rawLogs || !Array.isArray(data.rawLogs)) {
556+
throw new Error("No valid data to prune");
557+
}
558+
559+
// Calculate how many logs will be removed
560+
const originalLength = data.rawLogs.length;
561+
const minDate = new Date(minTime);
562+
const maxDate = new Date(maxTime);
563+
const remainingLogs = data.rawLogs.filter((log) => {
564+
const logTime = new Date(log.timestamp || log.insertTime);
565+
return logTime >= minDate && logTime <= maxDate;
566+
});
567+
568+
const removeCount = originalLength - remainingLogs.length;
569+
570+
if (
571+
!confirm(
572+
`This will remove ${removeCount} logs outside the selected time range.\nAre you sure you want to continue?`
573+
)
574+
) {
575+
log("Prune operation cancelled by user");
576+
return;
577+
}
578+
579+
log(`Pruning dataset ${index}: removing ${removeCount} logs outside time range`);
580+
581+
data.rawLogs = remainingLogs;
582+
583+
// Save the pruned dataset back to storage
584+
await saveToIndexedDB(data, index);
585+
586+
// Update the current dataset if this is the active one
587+
if (this.state.activeDatasetIndex === index) {
588+
const tripLogs = new TripLogs(data.rawLogs, data.solutionType);
589+
this.props.logData.tripLogs = tripLogs;
590+
this.props.logData.solutionType = data.solutionType;
591+
592+
// Force update of components
593+
this.forceUpdate();
594+
595+
// Select first row after pruning
596+
this.selectFirstRow();
597+
}
598+
599+
toast.success(`Dataset pruned: removed ${removeCount} logs outside the selected time range.`);
600+
} catch (error) {
601+
console.error("Error pruning dataset:", error);
602+
toast.error(`Error pruning dataset: ${error.message}`);
534603
}
535604
};
536605

@@ -598,13 +667,16 @@ class App extends React.Component {
598667
>
599668
{isUploaded ? `Dataset ${index + 1}` : `Select Dataset ${index + 1}`}
600669

601-
{isUploaded && (
670+
{isUploaded && isActive && (
602671
<span className="dataset-button-actions" onClick={toggleMenu}>
603672
604673
{isMenuOpen && (
605674
<div className="dataset-button-menu">
606-
<div className="dataset-button-menu-item save" onClick={handleSaveClick}>
607-
Save
675+
<div className="dataset-button-menu-item export" onClick={handleSaveClick}>
676+
Export
677+
</div>
678+
<div className="dataset-button-menu-item prune" onClick={handlePruneClick}>
679+
Prune
608680
</div>
609681
<div className="dataset-button-menu-item delete" onClick={handleDeleteClick}>
610682
Delete

src/Trip.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,17 @@ class Trip {
101101
* Colors were chosen for visibility
102102
*/
103103
export function getColor(tripIdx) {
104-
const colors = ["#2d7dd2", "#97cc04", "#eeb902", "#f45d01", "#474647", "00aa00"];
104+
const colors = [
105+
"#007bff", // Blue
106+
"#97cc04", // Lime Green
107+
"#d63384", // Magenta
108+
"#198754", // Green
109+
"#f45d01", // Orange
110+
"#6A0DAD", // Purple
111+
"#fdc500", // Gold
112+
"#0dcaf0", // Cyan
113+
"#8B4513", // Brown
114+
];
105115
return colors[tripIdx % colors.length];
106116
}
107117

src/TripLogs.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ function processRawLogs(rawLogs, solutionType) {
134134
heading: 0,
135135
routeSegment: null,
136136
routeSegmentTraffic: null,
137+
currentTrips: [],
137138
};
138139

139140
for (let idx = 0; idx < sortedLogs.length; idx++) {
@@ -151,6 +152,7 @@ function processRawLogs(rawLogs, solutionType) {
151152
newLog.request = apiCall.request;
152153
newLog.response = apiCall.response;
153154
newLog.error = apiCall.error;
155+
const hasApiError = !!newLog.error || (origLog.jsonpayload && origLog.jsonpayload.errorresponse);
154156

155157
adjustFieldFormats(solutionType, newLog);
156158

@@ -171,6 +173,17 @@ function processRawLogs(rawLogs, solutionType) {
171173
newLog.lastlocation.heading = lastKnownState.heading;
172174
}
173175

176+
// Keep the same current trips if we had an API error
177+
if (hasApiError && lastKnownState.currentTrips.length > 0) {
178+
log(`Preserving current trips due to API error for log at ${newLog.timestamp}`);
179+
if (!newLog.response) {
180+
newLog.response = {};
181+
}
182+
newLog.response.currenttrips = [...lastKnownState.currentTrips];
183+
} else if (_.get(newLog, "response.currenttrips")) {
184+
lastKnownState.currentTrips = [...newLog.response.currenttrips];
185+
}
186+
174187
// If Navigation SDK is NO_GUIDANCE, reset the lastKnownState planned route and traffic
175188
if (typeof newLog.navStatus === "string" && newLog.navStatus.endsWith("NO_GUIDANCE")) {
176189
lastKnownState.routeSegment = null;

src/TripObjects.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ export class TripObjects {
114114

115115
addTripVisuals(trip, minDate, maxDate) {
116116
const tripId = trip.tripName;
117+
const isNonTripSegment = tripId.startsWith("non-trip-segment-");
118+
117119
log(`Processing trip visuals for ${tripId}`, {
120+
isNonTripSegment,
121+
coordsCount: trip.pathCoords.length,
122+
firstUpdate: trip.firstUpdate,
123+
lastUpdate: trip.lastUpdate,
118124
pickupPoint: trip.getPickupPoint(),
119125
actualPickupPoint: trip.getActualPickupPoint(),
120126
dropoffPoint: trip.getDropoffPoint(),
@@ -126,11 +132,13 @@ export class TripObjects {
126132
// Add path polyline
127133
const tripCoords = trip.getPathCoords(minDate, maxDate);
128134
if (tripCoords.length > 0) {
135+
const strokeColor = isNonTripSegment ? "#474647" : getColor(trip.tripIdx);
136+
129137
const path = new google.maps.Polyline({
130138
path: tripCoords,
131139
geodesic: true,
132-
strokeColor: getColor(trip.tripIdx),
133-
strokeOpacity: 0.5,
140+
strokeColor: strokeColor,
141+
strokeOpacity: 0.3,
134142
strokeWeight: 6,
135143
clickable: false,
136144
map: this.map,
@@ -148,6 +156,11 @@ export class TripObjects {
148156
this.paths.set(tripId, path);
149157
}
150158

159+
// Skip creating markers for non-trip segments
160+
if (isNonTripSegment) {
161+
return;
162+
}
163+
151164
const markers = [];
152165

153166
// Get points

src/localStorage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export function ensureCorrectFormat(data) {
285285
};
286286
}
287287

288-
async function saveToIndexedDB(data, index) {
288+
export async function saveToIndexedDB(data, index) {
289289
const db = await openDB();
290290
const transaction = db.transaction(STORE_NAME, "readwrite");
291291
const store = transaction.objectStore(STORE_NAME);

0 commit comments

Comments
 (0)