From f3a9e2114919a4d35db284f792899bf6d8e9ecfc Mon Sep 17 00:00:00 2001 From: bmbianca Date: Mon, 1 Jun 2026 19:28:42 +0300 Subject: [PATCH 1/3] maxed the confidence score yay --- .../com/p2ps/service/DataDecayService.java | 12 +++---- .../p2ps/service/LocationProcessorWorker.java | 33 +++++++++++-------- src/main/resources/application.properties | 6 ++-- .../p2ps/service/DataDecayServiceTest.java | 4 +-- .../service/LocationProcessorWorkerTest.java | 6 ++-- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/p2ps/service/DataDecayService.java b/src/main/java/com/p2ps/service/DataDecayService.java index e4228de2..ac9a4843 100644 --- a/src/main/java/com/p2ps/service/DataDecayService.java +++ b/src/main/java/com/p2ps/service/DataDecayService.java @@ -17,14 +17,14 @@ public class DataDecayService { @Value("${data-decay.enabled:true}") private boolean dataDecayEnabled = true; - @Value("${data-decay.penalty:0.02}") - private double penalty = 0.02d; + @Value("${data-decay.penalty:0.1}") + private double penalty = 0.1d; - @Value("${data-decay.cutoff-days:14}") - private long cutoffDays = 14L; + @Value("${data-decay.cutoff-days:3}") + private long cutoffDays = 3L; - @Value("${data-decay.min-confidence-floor:0.15}") - private double minConfidenceFloor = 0.15d; + @Value("${data-decay.min-confidence-floor:0.05}") + private double minConfidenceFloor = 0.05d; public DataDecayService(StoreInventoryMapRepository repository) { this.repository = repository; diff --git a/src/main/java/com/p2ps/service/LocationProcessorWorker.java b/src/main/java/com/p2ps/service/LocationProcessorWorker.java index b99a784a..80cab099 100644 --- a/src/main/java/com/p2ps/service/LocationProcessorWorker.java +++ b/src/main/java/com/p2ps/service/LocationProcessorWorker.java @@ -22,8 +22,8 @@ public class LocationProcessorWorker { private static final Logger logger = LoggerFactory.getLogger(LocationProcessorWorker.class); - private static final double LOW_CONFIDENCE_THRESHOLD = 0.4d; - private static final int MIN_PING_COUNT_FOR_CONFIDENCE = 5; + private static final double LOW_CONFIDENCE_THRESHOLD = 0.1d; + private static final int MIN_PING_COUNT_FOR_CONFIDENCE = 1; private static final AtomicLong RAPID_RECALCULATION_FAILURES = new AtomicLong(); private final JdbcTemplate jdbcTemplate; @@ -170,13 +170,15 @@ public void processAndCalculateCenters() { String sql = """ INSERT INTO store_inventory_map (map_id, store_id, item_id, estimated_loc_point, confidence_score, ping_count, last_updated) WITH FilteredPings AS ( - SELECT item_id, store_id, location_point, accuracy_m + SELECT item_id, store_id, location_point, accuracy_m, marked_at FROM raw_user_pings - WHERE loc_provider IN ('WIFI_RTT', 'GPS') AND accuracy_m < 30.0 + WHERE loc_provider IN ('WIFI_RTT', 'GPS') + AND accuracy_m < 30.0 + AND marked_at > NOW() - INTERVAL '3 days' ), ClusteredData AS ( SELECT - item_id, store_id, location_point, accuracy_m, + item_id, store_id, location_point, accuracy_m, marked_at, ST_ClusterDBSCAN(ST_Transform(location_point, 3857), eps := 30.0, minpoints := 1) OVER (PARTITION BY store_id, item_id) AS cluster_id FROM FilteredPings @@ -187,8 +189,9 @@ ClusterStats AS ( item_id, cluster_id, ST_GeometricMedian(ST_Collect(location_point)) AS estimated_loc_point, - LEAST(1.0, (COUNT(item_id) / 50.0) * (1.0 / GREATEST(1.0, AVG(accuracy_m)))) AS confidence_score, - COUNT(item_id) AS ping_count + 1.0 AS confidence_score, + COUNT(item_id) AS ping_count, + MAX(marked_at) AS last_seen FROM ClusteredData WHERE cluster_id IS NOT NULL GROUP BY store_id, item_id, cluster_id @@ -200,14 +203,14 @@ SELECT DISTINCT ON (store_id, item_id) estimated_loc_point, confidence_score, ping_count, - NOW() + last_seen FROM ClusterStats ORDER BY store_id, item_id, ping_count DESC ON CONFLICT (store_id, item_id) DO UPDATE SET estimated_loc_point = EXCLUDED.estimated_loc_point, confidence_score = EXCLUDED.confidence_score, ping_count = EXCLUDED.ping_count, - last_updated = NOW() + last_updated = EXCLUDED.last_updated """; int insertedRows = jdbcTemplate.update(sql); @@ -239,14 +242,15 @@ public CompletableFuture recalculateSingleItem(UUID storeId, UUID itemId) try { String sql = """ WITH ItemPings AS ( - SELECT location_point, accuracy_m + SELECT location_point, accuracy_m, marked_at FROM raw_user_pings WHERE store_id = ? AND item_id = ? AND loc_provider IN ('WIFI_RTT', 'GPS') AND accuracy_m < 30.0 + AND marked_at > NOW() - INTERVAL '3 days' ), Clustered AS ( - SELECT location_point, accuracy_m, + SELECT location_point, accuracy_m, marked_at, ST_ClusterDBSCAN(ST_Transform(location_point, 3857), eps := 30.0, minpoints := 1) OVER () AS cluster_id FROM ItemPings @@ -255,8 +259,9 @@ ClusterStats AS ( SELECT cluster_id, ST_GeometricMedian(ST_Collect(location_point)) AS estimated_loc_point, - LEAST(1.0, (COUNT(*) / 50.0) * (1.0 / GREATEST(1.0, AVG(accuracy_m)))) AS confidence_score, - COUNT(*) AS ping_count + 1.0 AS confidence_score, + COUNT(*) AS ping_count, + MAX(marked_at) AS last_seen FROM Clustered WHERE cluster_id IS NOT NULL GROUP BY cluster_id @@ -267,7 +272,7 @@ ClusterStats AS ( SET estimated_loc_point = COALESCE(stats.estimated_loc_point, inventory.estimated_loc_point), confidence_score = COALESCE(stats.confidence_score, inventory.confidence_score), ping_count = COALESCE(stats.ping_count, inventory.ping_count), - last_updated = NOW() + last_updated = COALESCE(stats.last_seen, inventory.last_updated) FROM ClusterStats stats WHERE inventory.store_id = ? AND inventory.item_id = ? """; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bbd389e5..2bebaf5c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -27,9 +27,9 @@ routing.recalculation.guard.max-size=${ROUTING_RECALCULATION_GUARD_MAX_SIZE:1000 data-decay.enabled=${DATA_DECAY_ENABLED:true} data-decay.cron=${DATA_DECAY_CRON:0 0 3 * * ?} data-decay.zone=${DATA_DECAY_ZONE:UTC} -data-decay.penalty=${DATA_DECAY_PENALTY:0.02} -data-decay.cutoff-days=${DATA_DECAY_CUTOFF_DAYS:14} -data-decay.min-confidence-floor=${DATA_DECAY_MIN_CONFIDENCE_FLOOR:0.15} +data-decay.penalty=${DATA_DECAY_PENALTY:0.1} +data-decay.cutoff-days=${DATA_DECAY_CUTOFF_DAYS:3} +data-decay.min-confidence-floor=${DATA_DECAY_MIN_CONFIDENCE_FLOOR:0.05} spring.data.redis.host=localhost spring.data.redis.port=6379 diff --git a/src/test/java/com/p2ps/service/DataDecayServiceTest.java b/src/test/java/com/p2ps/service/DataDecayServiceTest.java index cab990e6..562736dd 100644 --- a/src/test/java/com/p2ps/service/DataDecayServiceTest.java +++ b/src/test/java/com/p2ps/service/DataDecayServiceTest.java @@ -28,8 +28,8 @@ class DataDecayServiceTest { @Test void shouldExecuteDataDecayAndCallRepositoryWithCorrectPenalty() { - Double expectedPenalty = 0.02; - Double expectedMinConfidenceFloor = 0.15; + Double expectedPenalty = 0.1; + Double expectedMinConfidenceFloor = 0.05; when(repository.applyDecayToOldRecords(eq(expectedPenalty), any(LocalDateTime.class), eq(expectedMinConfidenceFloor))) .thenReturn(5); diff --git a/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java b/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java index a773e431..16d1bce3 100644 --- a/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java +++ b/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java @@ -102,9 +102,9 @@ void processAndCalculateCenters_ThrowsExceptionOnError() throws Exception { @Test @DisplayName("Trebuie să marcheze corect produsele cu confidence scăzut") void isLowConfidence_ShouldReflectThresholds() { - assertTrue(worker.isLowConfidence(0.39d, 10)); - assertTrue(worker.isLowConfidence(0.8d, 4)); - assertFalse(worker.isLowConfidence(0.8d, 6)); + assertTrue(worker.isLowConfidence(0.05d, 10)); // 0.05 < 0.1 + assertTrue(worker.isLowConfidence(0.8d, 0)); // 0 < 1 + assertFalse(worker.isLowConfidence(0.15d, 2)); // 0.15 > 0.1, 2 > 1 } @Test From a99a277cdf56bbbca6343a87bbd8f66b998813fd Mon Sep 17 00:00:00 2001 From: bmbianca Date: Mon, 1 Jun 2026 20:35:49 +0300 Subject: [PATCH 2/3] removed "tu" point from the map --- src/main/java/com/p2ps/service/RoutingAsyncService.java | 8 +++++--- src/main/java/com/p2ps/service/RoutingService.java | 8 ++++---- .../java/com/p2ps/controller/RoutingControllerTest.java | 9 +++------ src/test/java/com/p2ps/service/RoutingServiceTest.java | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/p2ps/service/RoutingAsyncService.java b/src/main/java/com/p2ps/service/RoutingAsyncService.java index 67874dd8..2cc0bc8d 100644 --- a/src/main/java/com/p2ps/service/RoutingAsyncService.java +++ b/src/main/java/com/p2ps/service/RoutingAsyncService.java @@ -63,10 +63,12 @@ public void completeRouteAsync(String routeId, try { List optimized = optimizer.threeOptImprove(fullNnRoute); - // BE 3.2 Add audio instructions + // BE 3.2 Add audio instructions (must do while user_loc is present to get first segment) RoutingService routingService = applicationContext.getBean(RoutingService.class); routingService.addAudioInstructions(optimized); + double fullDistance = optimizer.routeDistance(optimized); + // Updated instantiation to use constructor and setters RoutingResponse fullResponse = new RoutingResponse(); fullResponse.setStatus("success"); @@ -75,8 +77,8 @@ public void completeRouteAsync(String routeId, fullResponse.setWarnings(warnings); fullResponse.setPartial(false); - fullResponse.setTotalDistanceMeters(optimizer.routeDistance(optimized)); - fullResponse.setTotalStops(optimized.size() - 1); + fullResponse.setTotalDistanceMeters(fullDistance); + fullResponse.setTotalStops(optimized.size()); fullResponse.setEstimatedTimeSeconds((int) (fullResponse.getTotalDistanceMeters() / 1.4)); String json = objectMapper.writeValueAsString(fullResponse); diff --git a/src/main/java/com/p2ps/service/RoutingService.java b/src/main/java/com/p2ps/service/RoutingService.java index a041e38f..0c3ee988 100644 --- a/src/main/java/com/p2ps/service/RoutingService.java +++ b/src/main/java/com/p2ps/service/RoutingService.java @@ -77,7 +77,7 @@ public RoutingResponse calculateOptimalRoute(RoutingRequest request) { return errorResponse; } - RoutePoint userPoint = new RoutePoint("user_loc", "Tu", request.getUserLat(), request.getUserLng()); + RoutePoint userPoint = new RoutePoint("user_loc", "", request.getUserLat(), request.getUserLng(), "USER"); // Full NN route (fast — always computed eagerly) List nnRoute = new ArrayList<>(); @@ -125,7 +125,7 @@ private RoutingResponse handleLazyRoute(List fullNnRoute, String routeId = UUID.randomUUID().toString(); // Partial response: user point + first lazyN products - List partial = fullNnRoute.subList(0, lazyN + 1); // inclusive of user point + List partial = new ArrayList<>(fullNnRoute.subList(0, Math.min(lazyN + 1, fullNnRoute.size()))); addAudioInstructions(partial); // BE 3.2 logger.info("Lazy routing: returnez {} noduri imediat, {} in background (routeId={})", @@ -139,9 +139,9 @@ private RoutingResponse handleLazyRoute(List fullNnRoute, asyncService.completeRouteAsync(routeId, new ArrayList<>(fullNnRoute), new ArrayList<>(warnings)); RoutingResponse partialResponse = new RoutingResponse(); - partialResponse.setStatus("success"); // Fixed: Now correctly setting status to "partial" + partialResponse.setStatus("success"); partialResponse.setRouteId(routeId); - partialResponse.setRoute(new ArrayList<>(partial)); + partialResponse.setRoute(partial); partialResponse.setWarnings(warnings); partialResponse.setPartial(true); diff --git a/src/test/java/com/p2ps/controller/RoutingControllerTest.java b/src/test/java/com/p2ps/controller/RoutingControllerTest.java index 0dfe676c..68ca3977 100644 --- a/src/test/java/com/p2ps/controller/RoutingControllerTest.java +++ b/src/test/java/com/p2ps/controller/RoutingControllerTest.java @@ -70,7 +70,6 @@ void shouldReturnSuccessStatusAndMockRouteWhenCalculateRouteIsCalled() { RoutingResponse mockResponse = new RoutingResponse(); mockResponse.setStatus("success"); mockResponse.setRoute(List.of( - new RoutePoint("user_loc", "Punctul Albastru (Tu)", 47.151726, 27.587914), new RoutePoint("item_101", "Lapte", 47.151800, 27.588000), new RoutePoint("item_102", "Paine", 47.151850, 27.588050), new RoutePoint("item_103", "Mere", 47.151900, 27.588100) @@ -84,8 +83,8 @@ void shouldReturnSuccessStatusAndMockRouteWhenCalculateRouteIsCalled() { assertEquals("success", response.getStatus()); assertNotNull(response.getRoute()); - assertEquals(4, response.getRoute().size()); - assertEquals("user_loc", response.getRoute().getFirst().getItemId()); + assertEquals(3, response.getRoute().size()); + assertNotEquals("user_loc", response.getRoute().getFirst().getItemId()); assertFalse(response.isPartial()); } @@ -95,9 +94,7 @@ void shouldReturnMockRouteForEmptyItemList() { RoutingResponse mockResponse = new RoutingResponse(); mockResponse.setStatus("success"); - mockResponse.setRoute(List.of( - new RoutePoint("user_loc", "Tu", 47.151726, 27.587914) - )); + mockResponse.setRoute(List.of()); // user point removed, route empty if no products mockResponse.setWarnings(List.of()); when(routingService.calculateOptimalRoute(request)).thenReturn(mockResponse); diff --git a/src/test/java/com/p2ps/service/RoutingServiceTest.java b/src/test/java/com/p2ps/service/RoutingServiceTest.java index 7b8a2489..79b01c83 100644 --- a/src/test/java/com/p2ps/service/RoutingServiceTest.java +++ b/src/test/java/com/p2ps/service/RoutingServiceTest.java @@ -257,7 +257,7 @@ void calculateOptimalRoute_lazy_shouldReturnPartialResponseWithRouteId() { assertEquals("success", response.getStatus()); assertTrue(response.isPartial()); assertNotNull(response.getRouteId()); - assertEquals(6, response.getRoute().size()); + assertEquals(5, response.getRoute().size()); // 5 products, user point removed } // ------------------------------------------------------------------------- @@ -474,7 +474,7 @@ void calculateOptimalRoute_shouldHandleDuplicateProductIds() { // Depending on implementation, we might have 1 product in route (unique) or 2. // Current logic in getProductLocations doesn't de-duplicate, it just returns what the DB returns. // If DB returns one row (because item_id is unique per store), then we have 1 product. - assertTrue(response.getRoute().size() >= 2); // user + at least one product + assertTrue(response.getRoute().size() >= 1); // at least one product (user removed) } @Test From 1fdd76623b967488f7543390487a277e482f5e04 Mon Sep 17 00:00:00 2001 From: bmbianca Date: Tue, 2 Jun 2026 18:07:49 +0300 Subject: [PATCH 3/3] fix tests --- .../com/p2ps/service/RoutingAsyncService.java | 5 +++++ .../java/com/p2ps/service/RoutingService.java | 21 +++++++++++++++---- .../service/LocationProcessorWorkerTest.java | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/p2ps/service/RoutingAsyncService.java b/src/main/java/com/p2ps/service/RoutingAsyncService.java index 2cc0bc8d..b45d958b 100644 --- a/src/main/java/com/p2ps/service/RoutingAsyncService.java +++ b/src/main/java/com/p2ps/service/RoutingAsyncService.java @@ -69,6 +69,11 @@ public void completeRouteAsync(String routeId, double fullDistance = optimizer.routeDistance(optimized); + // Remove user point from the list sent to UI (issue: redundant with blue ping) + if (!optimized.isEmpty() && "user_loc".equals(optimized.getFirst().getItemId())) { + optimized.removeFirst(); + } + // Updated instantiation to use constructor and setters RoutingResponse fullResponse = new RoutingResponse(); fullResponse.setStatus("success"); diff --git a/src/main/java/com/p2ps/service/RoutingService.java b/src/main/java/com/p2ps/service/RoutingService.java index 0c3ee988..d78790ad 100644 --- a/src/main/java/com/p2ps/service/RoutingService.java +++ b/src/main/java/com/p2ps/service/RoutingService.java @@ -95,6 +95,15 @@ public RoutingResponse calculateOptimalRoute(RoutingRequest request) { List optimizedRoute = optimizer.threeOptImprove(nnRoute); logImprovement(nnRoute, optimizedRoute); addAudioInstructions(optimizedRoute); // BE 3.2 + + // --- Injectarea metricilor pentru răspunsul complet (Eager) --- + double distEager = optimizer.routeDistance(optimizedRoute); + + // Remove user point from the list sent to UI (issue: redundant with blue ping) + if (!optimizedRoute.isEmpty() && "user_loc".equals(optimizedRoute.getFirst().getItemId())) { + optimizedRoute.removeFirst(); + } + logger.info("Ruta calculata: {} puncte, {} warnings", optimizedRoute.size(), warnings.size()); RoutingResponse response = new RoutingResponse(); @@ -103,8 +112,6 @@ public RoutingResponse calculateOptimalRoute(RoutingRequest request) { response.setWarnings(warnings); response.setPartial(false); - // --- Injectarea metricilor pentru răspunsul complet (Eager) --- - double distEager = optimizer.routeDistance(optimizedRoute); response.setTotalDistanceMeters(distEager); response.setTotalStops(optimizedRoute.size()); response.setEstimatedTimeSeconds((int) (distEager / 1.4)); // viteză estimată 1.4 m/s @@ -131,6 +138,14 @@ private RoutingResponse handleLazyRoute(List fullNnRoute, logger.info("Lazy routing: returnez {} noduri imediat, {} in background (routeId={})", partial.size(), fullNnRoute.size() - partial.size(), routeId); + // --- Injectarea metricilor pentru ruta parțială returnată imediat (Lazy) --- + double distLazy = optimizer.routeDistance(partial); + + // Remove user point from the list sent to UI (issue: redundant with blue ping) + if (!partial.isEmpty() && "user_loc".equals(partial.getFirst().getItemId())) { + partial.removeFirst(); + } + // Set pending marker in Redis String pendingKey = RoutingAsyncService.PENDING_KEY_PREFIX + routeId; redis.opsForValue().set(pendingKey, "true", RoutingAsyncService.PENDING_TTL); @@ -145,8 +160,6 @@ private RoutingResponse handleLazyRoute(List fullNnRoute, partialResponse.setWarnings(warnings); partialResponse.setPartial(true); - // --- Injectarea metricilor pentru ruta parțială returnată imediat (Lazy) --- - double distLazy = optimizer.routeDistance(partial); partialResponse.setTotalDistanceMeters(distLazy); partialResponse.setTotalStops(partial.size()); partialResponse.setEstimatedTimeSeconds((int) (distLazy / 1.4)); diff --git a/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java b/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java index 16d1bce3..bbd0a152 100644 --- a/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java +++ b/src/test/java/com/p2ps/service/LocationProcessorWorkerTest.java @@ -291,7 +291,7 @@ void detectDatabaseType_Success() throws Exception { org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> worker.initialize()); - assertTrue(worker.isLowConfidence(0.1d, 1)); // Just a dummy call to isLowConfidence to show worker is active + assertTrue(worker.isLowConfidence(0.05d, 1)); // 0.05 < 0.1 threshold -> true // Verify database type was detected correctly verify(connection, atLeastOnce()).getMetaData(); }