Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions src/main/java/pl/agh/transit/ConnectionServiceHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package pl.agh.transit;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import pl.agh.transit.dto.TripUpdateDTO;
import pl.agh.transit.dto.StopTimeUpdateDTO;
import pl.agh.transit.gtfs_static.model.Route;
import pl.agh.transit.gtfs_static.model.Stop;
import pl.agh.transit.gtfs_static.model.Trip;
import pl.agh.transit.gtfs_static.model.StopTime;
import pl.agh.transit.gtfs_static.repository.RouteRepository;
import pl.agh.transit.gtfs_static.repository.StaticTripRepository;
import pl.agh.transit.gtfs_static.repository.StopRepository;
import pl.agh.transit.gtfs_static.repository.StopTimeRepository;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;

/**
* Helper class for shared connection service logic
* Extracts common operations used by multiple connection services
*/
@Slf4j
@Component
@AllArgsConstructor
public class ConnectionServiceHelper {
private final StopRepository stopRepository;
private final StaticTripRepository staticTripRepository;
private final RouteRepository routeRepository;
private final CalendarService calendarService;
private final StopTimeRepository stopTimeRepository;

/**
* Gets all stops matching the given stop name
*/
public List<Stop> getStopsForName(String stopName) {
List<Stop> stops = stopRepository.findByStopName(stopName);
log.debug("Found {} stops for: {}", stops.size(), stopName);
return stops;
}

/**
* Filters trips based on service calendar for the given date
*/
public List<TripUpdateDTO> filterTripsByServiceCalendar(List<TripUpdateDTO> trips, LocalDate travelDate) {
return trips.stream()
.filter(trip -> {
// Get the trip from static data to access serviceId
Optional<Trip> staticTrip =
staticTripRepository.findById(trip.getTripId());

if (staticTrip.isEmpty()) {
log.debug("[CAL-FILTER] Trip {} not found in static data", trip.getTripId());
return false;
}

String serviceId = staticTrip.get().getServiceId();
boolean isActive = calendarService.isServiceActiveOnDate(serviceId, travelDate);

if (!isActive) {
log.debug("[CAL-FILTER] Trip {} (service: {}) is NOT active on {}", trip.getTripId(), serviceId, travelDate);
}

return isActive;
})
.toList();
}

/**
* Parse time from GTFS Realtime or Static format and add delay
* Supports both formats: "HH:MM:SS" and "YYYY-MM-DD HH:MM:SS"
* For realtime data: adds delay if present
* For static data: returns time in seconds from midnight
*/
public LocalDateTime parseTimeWithDelay(String timeStr, LocalDate travelDate, Integer delay) {
try {
if (timeStr == null) {
return LocalDateTime.now();
}

timeStr = timeStr.trim();
LocalDateTime parsedTime;

// Check if it contains date (space separator indicates datetime format)
if (timeStr.contains(" ")) {
// Format: "YYYY-MM-DD HH:MM:SS"
parsedTime = LocalDateTime.parse(timeStr,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} else {
// Format: "HH:MM:SS" (time from midnight)
String[] parts = timeStr.split(":");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid time format: " + timeStr);
}
int hours = Integer.parseInt(parts[0]);
int minutes = Integer.parseInt(parts[1]);
int seconds = Integer.parseInt(parts[2]);
long timeInSeconds = hours * 3600L + minutes * 60L + seconds;
parsedTime = travelDate.atStartOfDay().plusSeconds(timeInSeconds);
}

// Add delay if present
if (delay != null) {
parsedTime = parsedTime.plusSeconds(delay);
}

return parsedTime;
} catch (Exception e) {
log.error("Failed to parse time '{}'. Falling back to current time.", timeStr, e);
return LocalDateTime.now();
}
}

/**
* Gets route name from realtime trip data, with fallback to static data
*/
public String getRealtimeRouteName(TripUpdateDTO trip) {
String routeName = "N/A";

if (trip.getRouteId() != null && !trip.getRouteId().isEmpty()) {
log.debug(" [7f-1] Trying route from realtime: {}", trip.getRouteId());
Optional<Route> route = routeRepository.findById(trip.getRouteId());
routeName = route.map(Route::getRouteShortName).orElse("N/A");
}

if ("N/A".equals(routeName)) {
log.debug(" [7f-2] Route not found in realtime, checking static trip: {}", trip.getTripId());
routeName = getStaticRouteNameForTrip(trip.getTripId());
}

log.debug(" [7f] Final route name: {}", routeName);
return routeName;
}

/**
* Gets route name for a trip from static GTFS data
*/
public String getStaticRouteNameForTrip(String tripId) {
Optional<Trip> trip = staticTripRepository.findById(tripId);
return trip.flatMap(t -> {
log.debug("[9h] Trip route ID: {}", t.getRouteId());
return routeRepository.findById(t.getRouteId());
})
.map(Route::getRouteShortName)
.orElse("N/A");
}

/**
* Checks if a connection departure time is not in the past
*/
public boolean isConnectionNotInPast(LocalDateTime departureTime, LocalDateTime travelDateTime) {
if (departureTime.isBefore(travelDateTime) || departureTime.isEqual(travelDateTime)) {
log.debug(" [7g3] Skipping connection - departure time {} is in the past (reference: {})", departureTime, travelDateTime);
return false;
}
return true;
}

/**
* Gets all stop times for a given stop ID
*/
public List<StopTime> getStopTimesForStop(String stopId) {
return stopTimeRepository.findByStopId(stopId);
}

/**
* Gets all stop times for a given trip ID
*/
public List<StopTime> getStopTimesForTrip(String tripId) {
return stopTimeRepository.findByTripId(tripId);
}

/**
* Checks if a trip is active on a given date based on service calendar
*/
public boolean isStaticServiceActiveForTrip(StopTime stopTime, LocalDate travelDate) {
Optional<Trip> trip = staticTripRepository.findById(stopTime.getTripId());

if (trip.isEmpty()) {
return false;
}

String serviceId = trip.get().getServiceId();
boolean isActive = calendarService.isServiceActiveOnDate(serviceId, travelDate);

if (!isActive) {
log.debug("[9-CAL] Trip {} (service: {}) is NOT active on {}", stopTime.getTripId(), serviceId, travelDate);
}

return isActive;
}

/**
* Finds a stop in a trip's stop list
*/
public Optional<StopTime> findStopInTrip(List<StopTime> tripStops, String stopId) {
return tripStops.stream()
.filter(st -> st.getStopId().equals(stopId))
.findFirst();
}
}
Loading