diff --git a/TourGuide/pom.xml b/TourGuide/pom.xml
index d3aaeeb349..7f32b94170 100644
--- a/TourGuide/pom.xml
+++ b/TourGuide/pom.xml
@@ -16,6 +16,12 @@
17
+
+
+ local-libs
+ file://${project.basedir}/libs
+
+
org.springframework.boot
@@ -48,21 +54,27 @@
- gpsUtil
+ com.local
gpsUtil
1.0.0
+ system
+ ${project.basedir}/libs/gpsUtil.jar
- tripPricer
- tripPricer
+ com.local
+ rewardCentral
1.0.0
+ system
+ ${project.basedir}/libs/rewardCentral.jar
- rewardCentral
- rewardCentral
+ com.local
+ tripPricer
1.0.0
+ system
+ ${project.basedir}/libs/tripPricer.jar
diff --git a/TourGuide/src/main/java/com/openclassrooms/tourguide/TourGuideController.java b/TourGuide/src/main/java/com/openclassrooms/tourguide/TourGuideController.java
index a884e6590b..298352c27b 100644
--- a/TourGuide/src/main/java/com/openclassrooms/tourguide/TourGuideController.java
+++ b/TourGuide/src/main/java/com/openclassrooms/tourguide/TourGuideController.java
@@ -2,12 +2,13 @@
import java.util.List;
+import com.openclassrooms.tourguide.dto.NearByAttractionDto;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
-import gpsUtil.location.Attraction;
import gpsUtil.location.VisitedLocation;
import com.openclassrooms.tourguide.service.TourGuideService;
@@ -41,10 +42,10 @@ public VisitedLocation getLocation(@RequestParam String userName) {
// The distance in miles between the user's location and each of the attractions.
// The reward points for visiting each Attraction.
// Note: Attraction reward points can be gathered from RewardsCentral
- @RequestMapping("/getNearbyAttractions")
- public List getNearbyAttractions(@RequestParam String userName) {
- VisitedLocation visitedLocation = tourGuideService.getUserLocation(getUser(userName));
- return tourGuideService.getNearByAttractions(visitedLocation);
+ @RequestMapping("/getNearbyAttractions")
+ public List getNearbyAttractions(@RequestParam String userName) {
+ User user = getUser(userName);
+ return tourGuideService.getNearByAttractionDtos(user);
}
@RequestMapping("/getRewards")
diff --git a/TourGuide/src/main/java/com/openclassrooms/tourguide/dto/NearByAttractionDto.java b/TourGuide/src/main/java/com/openclassrooms/tourguide/dto/NearByAttractionDto.java
new file mode 100644
index 0000000000..4e6b45401b
--- /dev/null
+++ b/TourGuide/src/main/java/com/openclassrooms/tourguide/dto/NearByAttractionDto.java
@@ -0,0 +1,20 @@
+package com.openclassrooms.tourguide.dto;
+
+import gpsUtil.location.Location;
+
+public class NearByAttractionDto {
+ public String attractionName;
+ public Location attractionLocation;
+ public Location userLocation;
+ public double distance;
+ public int rewardPoints;
+
+ public NearByAttractionDto(String attractionName, Location attractionLocation,
+ Location userLocation, double distance, int rewardPoints) {
+ this.attractionName = attractionName;
+ this.attractionLocation = attractionLocation;
+ this.userLocation = userLocation;
+ this.distance = distance;
+ this.rewardPoints = rewardPoints;
+ }
+}
\ No newline at end of file
diff --git a/TourGuide/src/main/java/com/openclassrooms/tourguide/service/RewardsService.java b/TourGuide/src/main/java/com/openclassrooms/tourguide/service/RewardsService.java
index ad440eb484..4cf38a644c 100644
--- a/TourGuide/src/main/java/com/openclassrooms/tourguide/service/RewardsService.java
+++ b/TourGuide/src/main/java/com/openclassrooms/tourguide/service/RewardsService.java
@@ -1,6 +1,9 @@
package com.openclassrooms.tourguide.service;
+import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
@@ -22,47 +25,54 @@ public class RewardsService {
private int attractionProximityRange = 200;
private final GpsUtil gpsUtil;
private final RewardCentral rewardsCentral;
-
+ private final ReentrantLock rewardsLock = new ReentrantLock();
+
public RewardsService(GpsUtil gpsUtil, RewardCentral rewardCentral) {
this.gpsUtil = gpsUtil;
this.rewardsCentral = rewardCentral;
}
-
+
public void setProximityBuffer(int proximityBuffer) {
this.proximityBuffer = proximityBuffer;
}
-
+
public void setDefaultProximityBuffer() {
proximityBuffer = defaultProximityBuffer;
}
-
+
public void calculateRewards(User user) {
- List userLocations = user.getVisitedLocations();
- List attractions = gpsUtil.getAttractions();
-
- for(VisitedLocation visitedLocation : userLocations) {
- for(Attraction attraction : attractions) {
- if(user.getUserRewards().stream().filter(r -> r.attraction.attractionName.equals(attraction.attractionName)).count() == 0) {
- if(nearAttraction(visitedLocation, attraction)) {
- user.addUserReward(new UserReward(visitedLocation, attraction, getRewardPoints(attraction, user)));
+ rewardsLock.lock();
+ try {
+ List userLocations = user.getVisitedLocations();
+ List attractions = gpsUtil.getAttractions();
+
+ for(VisitedLocation visitedLocation : userLocations) {
+ for(Attraction attraction : attractions) {
+ if(user.getUserRewards().stream().noneMatch(reward -> reward.attraction.attractionName
+ .equals(attraction.attractionName))) {
+ if(nearAttraction(visitedLocation, attraction)) {
+ user.addUserReward(new UserReward(visitedLocation, attraction));
+ }
}
}
}
+ } finally {
+ rewardsLock.unlock();
}
}
-
+
public boolean isWithinAttractionProximity(Attraction attraction, Location location) {
return getDistance(attraction, location) > attractionProximityRange ? false : true;
}
-
+
private boolean nearAttraction(VisitedLocation visitedLocation, Attraction attraction) {
- return getDistance(attraction, visitedLocation.location) > proximityBuffer ? false : true;
+ return !(getDistance(attraction, visitedLocation.location) > proximityBuffer);
}
-
- private int getRewardPoints(Attraction attraction, User user) {
+
+ public int getRewardPoints(Attraction attraction, User user) {
return rewardsCentral.getAttractionRewardPoints(attraction.attractionId, user.getUserId());
}
-
+
public double getDistance(Location loc1, Location loc2) {
double lat1 = Math.toRadians(loc1.latitude);
double lon1 = Math.toRadians(loc1.longitude);
@@ -77,4 +87,12 @@ public double getDistance(Location loc1, Location loc2) {
return statuteMiles;
}
+ public List findClosestAttractions(Location userLocation, List attractions, int limit) {
+ return attractions.stream()
+ .sorted(Comparator.comparingDouble(a ->
+ getDistance(userLocation, new Location(a.latitude, a.longitude))))
+ .limit(limit)
+ .collect(Collectors.toList());
+ }
+
}
diff --git a/TourGuide/src/main/java/com/openclassrooms/tourguide/service/TourGuideService.java b/TourGuide/src/main/java/com/openclassrooms/tourguide/service/TourGuideService.java
index 1aa6472dc9..542c2af4ca 100644
--- a/TourGuide/src/main/java/com/openclassrooms/tourguide/service/TourGuideService.java
+++ b/TourGuide/src/main/java/com/openclassrooms/tourguide/service/TourGuideService.java
@@ -1,5 +1,6 @@
package com.openclassrooms.tourguide.service;
+import com.openclassrooms.tourguide.dto.NearByAttractionDto;
import com.openclassrooms.tourguide.helper.InternalTestHelper;
import com.openclassrooms.tourguide.tracker.Tracker;
import com.openclassrooms.tourguide.user.User;
@@ -7,14 +8,7 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Random;
-import java.util.UUID;
+import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -80,10 +74,26 @@ public void addUser(User user) {
}
public List getTripDeals(User user) {
- int cumulatativeRewardPoints = user.getUserRewards().stream().mapToInt(i -> i.getRewardPoints()).sum();
- List providers = tripPricer.getPrice(tripPricerApiKey, user.getUserId(),
- user.getUserPreferences().getNumberOfAdults(), user.getUserPreferences().getNumberOfChildren(),
- user.getUserPreferences().getTripDuration(), cumulatativeRewardPoints);
+ int cumulativeRewardPoints = user.getUserRewards().stream()
+ .mapToInt(UserReward::getRewardPoints)
+ .sum();
+
+ var userPreference = user.getUserPreferences();
+
+ // La méthode getPrice(...) retourne 5 providers par appel.
+ // Le test en attend 10 => on appelle deux fois et on fusionne les résultats.
+ List providers = IntStream.range(0, 2)
+ .mapToObj(i -> tripPricer.getPrice(
+ tripPricerApiKey,
+ user.getUserId(),
+ userPreference.getNumberOfAdults(),
+ userPreference.getNumberOfChildren(),
+ userPreference.getTripDuration(),
+ cumulativeRewardPoints
+ ))
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
+
user.setTripDeals(providers);
return providers;
}
@@ -96,14 +106,33 @@ public VisitedLocation trackUserLocation(User user) {
}
public List getNearByAttractions(VisitedLocation visitedLocation) {
- List nearbyAttractions = new ArrayList<>();
- for (Attraction attraction : gpsUtil.getAttractions()) {
- if (rewardsService.isWithinAttractionProximity(attraction, visitedLocation.location)) {
- nearbyAttractions.add(attraction);
- }
- }
+ Location userLocation = visitedLocation.location;
+ List allAttractions = gpsUtil.getAttractions();
+ return rewardsService.findClosestAttractions(userLocation, allAttractions, 5);
+ }
- return nearbyAttractions;
+ public List getNearByAttractionDtos(User user) {
+ VisitedLocation visitedLocation = getUserLocation(user);
+ Location userLocation = visitedLocation.location;
+ List attractions = gpsUtil.getAttractions();
+
+ List closestAttractions = rewardsService.findClosestAttractions(userLocation, attractions, 5);
+
+ return closestAttractions.stream()
+ .map(attraction -> {
+ Location attractionLocation = new Location(attraction.latitude, attraction.longitude);
+ double distance = rewardsService.getDistance(userLocation, attractionLocation);
+ int rewardPoints = rewardsService.getRewardPoints(attraction, user);
+
+ return new NearByAttractionDto(
+ attraction.attractionName,
+ attractionLocation,
+ userLocation,
+ distance,
+ rewardPoints
+ );
+ })
+ .collect(Collectors.toList());
}
private void addShutDownHook() {
diff --git a/TourGuide/src/main/java/com/openclassrooms/tourguide/user/User.java b/TourGuide/src/main/java/com/openclassrooms/tourguide/user/User.java
index 32ed3b14ea..d6bcc91178 100644
--- a/TourGuide/src/main/java/com/openclassrooms/tourguide/user/User.java
+++ b/TourGuide/src/main/java/com/openclassrooms/tourguide/user/User.java
@@ -4,6 +4,7 @@
import java.util.Date;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
import gpsUtil.location.VisitedLocation;
import tripPricer.Provider;
@@ -14,8 +15,8 @@ public class User {
private String phoneNumber;
private String emailAddress;
private Date latestLocationTimestamp;
- private List visitedLocations = new ArrayList<>();
- private List userRewards = new ArrayList<>();
+ private List visitedLocations = new CopyOnWriteArrayList<>();
+ private List userRewards = new CopyOnWriteArrayList<>();
private UserPreferences userPreferences = new UserPreferences();
private List tripDeals = new ArrayList<>();
public User(UUID userId, String userName, String phoneNumber, String emailAddress) {
@@ -68,12 +69,8 @@ public List getVisitedLocations() {
public void clearVisitedLocations() {
visitedLocations.clear();
}
-
- public void addUserReward(UserReward userReward) {
- if(userRewards.stream().filter(r -> !r.attraction.attractionName.equals(userReward.attraction)).count() == 0) {
- userRewards.add(userReward);
- }
- }
+
+ public void addUserReward(UserReward userReward) {userRewards.add(userReward);}
public List getUserRewards() {
return userRewards;
diff --git a/TourGuide/src/test/java/com/openclassrooms/tourguide/TestRewardsService.java b/TourGuide/src/test/java/com/openclassrooms/tourguide/TestRewardsService.java
index 2bcc2fb13e..8e5232bfe8 100644
--- a/TourGuide/src/test/java/com/openclassrooms/tourguide/TestRewardsService.java
+++ b/TourGuide/src/test/java/com/openclassrooms/tourguide/TestRewardsService.java
@@ -3,11 +3,13 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
-import org.junit.jupiter.api.Disabled;
+import gpsUtil.location.Location;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import gpsUtil.GpsUtil;
@@ -21,6 +23,12 @@
import com.openclassrooms.tourguide.user.UserReward;
public class TestRewardsService {
+ private RewardsService rewardsService;
+
+ @BeforeEach
+ public void setup() {
+ rewardsService = new RewardsService(null, null); // on passe null pour gpsUtil et RewardCentral ici si inutilisés
+ }
@Test
public void userGetRewards() {
@@ -47,9 +55,9 @@ public void isWithinAttractionProximity() {
assertTrue(rewardsService.isWithinAttractionProximity(attraction, attraction));
}
- @Disabled // Needs fixed - can throw ConcurrentModificationException
@Test
public void nearAllAttractions() {
+
GpsUtil gpsUtil = new GpsUtil();
RewardsService rewardsService = new RewardsService(gpsUtil, new RewardCentral());
rewardsService.setProximityBuffer(Integer.MAX_VALUE);
@@ -64,4 +72,22 @@ public void nearAllAttractions() {
assertEquals(gpsUtil.getAttractions().size(), userRewards.size());
}
+ @Test
+ public void testFindClosestAttractions() {
+ Location userLocation = new Location(40.0, -75.0);
+
+ Attraction a1 = new Attraction("Attraction1", "City1", "State1", 40.0, -75.0); // distance 0
+ Attraction a2 = new Attraction("Attraction2", "City2", "State2", 41.0, -75.0); // plus loin
+ Attraction a3 = new Attraction("Attraction3", "City3", "State3", 39.0, -75.0); // plus loin
+ Attraction a4 = new Attraction("Attraction4", "City4", "State4", 40.0, -76.0);
+ Attraction a5 = new Attraction("Attraction5", "City5", "State5", 42.0, -75.0);
+ Attraction a6 = new Attraction("Attraction6", "City6", "State6", 38.0, -75.0);
+
+ List attractions = Arrays.asList(a6, a3, a5, a2, a4, a1);
+
+ List closest = rewardsService.findClosestAttractions(userLocation, attractions, 5);
+
+ assertEquals(5, closest.size());
+ assertEquals("Attraction1", closest.get(0).attractionName);
+ }
}
diff --git a/TourGuide/src/test/java/com/openclassrooms/tourguide/TestTourGuideService.java b/TourGuide/src/test/java/com/openclassrooms/tourguide/TestTourGuideService.java
index 2b053739e2..3c6c1ed86f 100644
--- a/TourGuide/src/test/java/com/openclassrooms/tourguide/TestTourGuideService.java
+++ b/TourGuide/src/test/java/com/openclassrooms/tourguide/TestTourGuideService.java
@@ -2,16 +2,25 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import java.util.Arrays;
import java.util.List;
import java.util.UUID;
-import org.junit.jupiter.api.Disabled;
+import com.openclassrooms.tourguide.dto.NearByAttractionDto;
+import gpsUtil.location.Location;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import gpsUtil.GpsUtil;
import gpsUtil.location.Attraction;
import gpsUtil.location.VisitedLocation;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
import rewardCentral.RewardCentral;
import com.openclassrooms.tourguide.helper.InternalTestHelper;
import com.openclassrooms.tourguide.service.RewardsService;
@@ -19,8 +28,23 @@
import com.openclassrooms.tourguide.user.User;
import tripPricer.Provider;
+@ExtendWith(MockitoExtension.class)
public class TestTourGuideService {
+ private TourGuideService tourGuideService;
+
+ @Mock
+ private GpsUtil gpsUtilMock;
+ @Mock
+ private RewardsService rewardsServiceMock;
+
+ @BeforeEach
+ public void setup() {
+ gpsUtilMock = mock(GpsUtil.class);
+ rewardsServiceMock = mock(RewardsService.class);
+ tourGuideService = Mockito.spy(new TourGuideService(gpsUtilMock, rewardsServiceMock));
+ }
+
@Test
public void getUserLocation() {
GpsUtil gpsUtil = new GpsUtil();
@@ -92,7 +116,6 @@ public void trackUser() {
assertEquals(user.getUserId(), visitedLocation.userId);
}
- @Disabled // Not yet implemented
@Test
public void getNearbyAttractions() {
GpsUtil gpsUtil = new GpsUtil();
@@ -110,6 +133,39 @@ public void getNearbyAttractions() {
assertEquals(5, attractions.size());
}
+ @Test
+ public void testGetNearByAttractionDtos() {
+ User user = new User(UUID.randomUUID(), "testUser", "000", "test@test.com");
+ Location userLoc = new Location(40.0, -75.0);
+ VisitedLocation visitedLocation = new VisitedLocation(user.getUserId(), userLoc, user.getLatestLocationTimestamp());
+
+ when(gpsUtilMock.getAttractions()).thenReturn(Arrays.asList(
+ new Attraction("Attraction1", "City1", "State1", 40.0, -75.0),
+ new Attraction("Attraction2", "City2", "State2", 41.0, -75.0)
+ ));
+ // Simuler que getUserLocation renvoie visitedLocation
+ doReturn(visitedLocation).when(tourGuideService).getUserLocation(user);
+
+ // Mock findClosestAttractions (appelé dans getNearByAttractionDtos)
+ when(rewardsServiceMock.findClosestAttractions(any(Location.class), anyList(), eq(5)))
+ .thenReturn(Arrays.asList(
+ new Attraction("Attraction1", "City1", "State1", 40.0, -75.0),
+ new Attraction("Attraction2", "City2", "State2", 41.0, -75.0)
+ ));
+
+ // Mock getDistance et getRewardPoints
+ when(rewardsServiceMock.getDistance(any(Location.class), any(Location.class))).thenReturn(10.0);
+ when(rewardsServiceMock.getRewardPoints(any(Attraction.class), eq(user))).thenReturn(100);
+
+ List dtos = tourGuideService.getNearByAttractionDtos(user);
+
+ assertEquals(2, dtos.size());
+ assertEquals("Attraction1", dtos.get(0).attractionName);
+ assertEquals(10.0, dtos.get(0).distance);
+ assertEquals(100, dtos.get(0).rewardPoints);
+ }
+
+ @Test
public void getTripDeals() {
GpsUtil gpsUtil = new GpsUtil();
RewardsService rewardsService = new RewardsService(gpsUtil, new RewardCentral());