From 81676b13f7ddb1605bdd8af567f66b969aaf5945 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sat, 23 Oct 2021 12:28:39 +0200 Subject: [PATCH 1/3] MVV: switch to new endpoint with realtime data --- src/de/schildbach/pte/MvvProvider.java | 4 +-- .../pte/live/MvvProviderLiveTest.java | 27 +++++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/de/schildbach/pte/MvvProvider.java b/src/de/schildbach/pte/MvvProvider.java index 67a208d32..3a567faf6 100644 --- a/src/de/schildbach/pte/MvvProvider.java +++ b/src/de/schildbach/pte/MvvProvider.java @@ -39,7 +39,7 @@ * @author Andreas Schildbach */ public class MvvProvider extends AbstractEfaProvider { - private static final HttpUrl API_BASE = HttpUrl.parse("https://efa.mvv-muenchen.de/mobile/"); + private static final HttpUrl API_BASE = HttpUrl.parse("https://efa.mvv-muenchen.de/ng/"); public MvvProvider() { this(API_BASE); @@ -50,7 +50,7 @@ public MvvProvider(final HttpUrl apiBase) { setIncludeRegionId(false); setRequestUrlEncoding(Charsets.UTF_8); setStyles(STYLES); - setSessionCookieName("SIDefaalt"); // SIDefa + setSessionCookieName("SIDefa"); } @Override diff --git a/test/de/schildbach/pte/live/MvvProviderLiveTest.java b/test/de/schildbach/pte/live/MvvProviderLiveTest.java index ce53c10f9..c8c531320 100644 --- a/test/de/schildbach/pte/live/MvvProviderLiveTest.java +++ b/test/de/schildbach/pte/live/MvvProviderLiveTest.java @@ -46,7 +46,8 @@ public MvvProviderLiveTest() { @Test public void nearbyStations() throws Exception { - final NearbyLocationsResult result = queryNearbyStations(new Location(LocationType.STATION, "350")); + final NearbyLocationsResult result = + queryNearbyStations(new Location(LocationType.STATION, "91000350")); print(result); } @@ -68,13 +69,9 @@ public void nearbyLocationsByCoordinate() throws Exception { @Test public void queryDeparturesMarienplatz() throws Exception { - final QueryDeparturesResult result1 = queryDepartures("2", false); + final QueryDeparturesResult result1 = queryDepartures("91000002", false); assertEquals(QueryDeparturesResult.Status.OK, result1.status); print(result1); - - final QueryDeparturesResult result2 = queryDepartures("1000002", false); - assertEquals(QueryDeparturesResult.Status.OK, result2.status); - print(result2); } @Test @@ -99,14 +96,14 @@ public void suggestLocationsIncomplete() throws Exception { public void suggestLocationsWithUmlaut() throws Exception { final SuggestLocationsResult result = suggestLocations("Grüntal"); print(result); - assertThat(result.getLocations(), hasItem(new Location(LocationType.STATION, "1000619"))); + assertThat(result.getLocations(), hasItem(new Location(LocationType.STATION, "91000619"))); } @Test public void suggestLocationsFraunhofer() throws Exception { final SuggestLocationsResult result = suggestLocations("fraunhofer"); print(result); - assertThat(result.getLocations(), hasItem(new Location(LocationType.STATION, "1000150"))); + assertThat(result.getLocations(), hasItem(new Location(LocationType.STATION, "91000150"))); } @Test @@ -135,7 +132,7 @@ public void suggestAddress() throws Exception { final SuggestLocationsResult result = suggestLocations("München, Maximilianstr. 1"); print(result); assertThat(result.getLocations(), hasItem(new Location(LocationType.ADDRESS, - "streetID:3239:1:9162000:9162000:Maximilianstraße:München:Maximilianstraße::Maximilianstraße:80539:ANY:DIVA_ADDRESS:4468763:826437:MVTT:MVV"))); + "streetID:1500000561::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289507:5870064:MRCV:BAY"))); } @Test @@ -143,13 +140,13 @@ public void suggestStreet() throws Exception { final SuggestLocationsResult result = suggestLocations("München, Maximilianstr."); print(result); assertThat(result.getLocations(), hasItem(new Location(LocationType.ADDRESS, - "streetID:3239::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:4469138:826553:MVTT:MVV"))); + "streetID:1500000561::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289507:5870064:MRCV:BAY"))); } @Test public void shortTrip() throws Exception { - final Location from = new Location(LocationType.STATION, "2", "München", "Marienplatz"); - final Location to = new Location(LocationType.STATION, "10", "München", "Pasing"); + final Location from = new Location(LocationType.STATION, "91000002", "München", "Marienplatz"); + final Location to = new Location(LocationType.STATION, "91000010", "München", "Pasing"); final QueryTripsResult result = queryTrips(from, null, to, new Date(), true, null); print(result); final QueryTripsResult laterResult = queryMoreTrips(result.context, true); @@ -162,7 +159,7 @@ public void shortTrip() throws Exception { public void longTrip() throws Exception { final Location from = new Location(LocationType.STATION, "1005530", Point.from1E6(48002924, 11340144), "Starnberg", "Agentur für Arbeit"); - final Location to = new Location(LocationType.STATION, null, null, "Ackermannstraße"); + final Location to = new Location(LocationType.STATION, "91000309", null, "Ackermannstraße"); final QueryTripsResult result = queryTrips(from, null, to, new Date(), true, null); print(result); } @@ -217,7 +214,7 @@ public void tripBetweenStreets() throws Exception { @Test public void tripBetweenStationAndAddress() throws Exception { - final Location from = new Location(LocationType.STATION, "1220", null, "Josephsburg"); + final Location from = new Location(LocationType.STATION, "91001220", null, "Josephsburg"); final Location to = new Location(LocationType.ADDRESS, null, Point.from1E6(48188018, 11574239), null, "München Frankfurter Ring 35"); final QueryTripsResult result = queryTrips(from, null, to, new Date(), true, null); @@ -228,7 +225,7 @@ public void tripBetweenStationAndAddress() throws Exception { @Test public void tripInvalidStation() throws Exception { - final Location valid = new Location(LocationType.STATION, "2", "München", "Marienplatz"); + final Location valid = new Location(LocationType.STATION, "91000002", "München", "Marienplatz"); final Location invalid = new Location(LocationType.STATION, "99999", null, null); final QueryTripsResult result1 = queryTrips(valid, null, invalid, new Date(), true, null); assertEquals(QueryTripsResult.Status.UNKNOWN_TO, result1.status); From 863a456094ae034e226a2d4069d81b80aa999533 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 7 Nov 2021 15:04:46 +0100 Subject: [PATCH 2/3] MVV: workaround for queryMoreTrips --- src/de/schildbach/pte/MvvProvider.java | 123 +++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/src/de/schildbach/pte/MvvProvider.java b/src/de/schildbach/pte/MvvProvider.java index 3a567faf6..0a01f6adb 100644 --- a/src/de/schildbach/pte/MvvProvider.java +++ b/src/de/schildbach/pte/MvvProvider.java @@ -17,7 +17,12 @@ package de.schildbach.pte; +import java.io.IOException; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,12 +32,17 @@ import com.google.common.base.Charsets; import de.schildbach.pte.dto.Line; +import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Position; import de.schildbach.pte.dto.Product; +import de.schildbach.pte.dto.QueryTripsContext; +import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style.Shape; +import de.schildbach.pte.dto.Trip; +import de.schildbach.pte.dto.TripOptions; import okhttp3.HttpUrl; /** @@ -157,4 +167,117 @@ protected Position parsePosition(final String position) { public Point[] getArea() { return new Point[] { Point.fromDouble(48.140377, 11.560643) }; } + + /* + MVV's new EFA uses load balancing. Therefore, stateful API functionality only works + correctly if we coincidentally hit the same server again. The session ID cookie does include + the server ID that the session was created on, but apparently the load balancer does not + respect this. + + There were attempts to ask MVV to fix this issue, but they did not offer any help: + https://github.com/schildbach/public-transport-enabler/pull/414#issuecomment-954032588 + + Thus, we implement queryMoreTrips in a stateless manner by adjusting + the departure/arrival times ourselves. This is the same algorithm that is + also used in the Javascript code on the mobile MVV website at + https://m.mvv-muenchen.de/mvvMobile5/de/index.html#trips + */ + + private static class MvvContext implements QueryTripsContext { + public Location from; + public Location via; + public Location to; + public TripOptions options; + public QueryTripsResult result; + public List trips; + + @Override + public boolean canQueryLater() { + return true; + } + + @Override + public boolean canQueryEarlier() { + return true; + } + } + + private boolean requestingMoreTrips; + + @Override + public QueryTripsResult queryTrips(final Location from, final @Nullable Location via, final Location to, + final Date date, final boolean dep, final @Nullable TripOptions options) throws IOException { + QueryTripsResult result = super.queryTrips(from, via, to, date, dep, options); + + if (result.status == QueryTripsResult.Status.OK) { + MvvContext context = new MvvContext(); + context.from = from; + context.to = to; + context.via = via; + context.options = options; + context.trips = result.trips; + result = new QueryTripsResult(result.header, result.queryUri, result.from, + result.via, result.to, context, result.trips); + } + return result; + } + + @Override + public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later) throws IOException { + if (!(contextObj instanceof MvvContext)) { + throw new IllegalArgumentException("needs an MvvContext"); + } + MvvContext context = (MvvContext) contextObj; + + // get departure time of last trip / arrival time of last trip as reference time + int tripIndex; + if (later) { + Trip lastTrip = context.trips.get(context.trips.size() - 1); + // if the last included trip is a walking route, use the previous one + boolean lastTripIsIndividual = + lastTrip.legs.size() == 1 && lastTrip.legs.get(0) instanceof Trip.Individual; + tripIndex = context.trips.size() - (lastTripIsIndividual ? 2 : 1); + } else { + tripIndex = 0; + } + Trip refTrip = context.trips.get(tripIndex); + Date refTime = later ? refTrip.getFirstDepartureTime() : refTrip.getLastArrivalTime(); + + // adjust time by one minute so that we don't get the same trip again + refTime = addMinutesToDate(refTime, later ? 1 : -1); + + requestingMoreTrips = true; // set special options for more trips request + try { + QueryTripsResult result = super.queryTrips(context.from, context.via, context.to, + refTime, later, context.options); + + if (result.status == QueryTripsResult.Status.OK) { + context.trips.addAll(later ? context.trips.size() - 1 : 0, result.trips); + result = new QueryTripsResult(result.header, result.queryUri, result.from, + result.via, result.to, context, result.trips); + } + + return result; + } finally { + requestingMoreTrips = false; // reset options + } + } + + @Override + protected void appendTripRequestParameters(HttpUrl.Builder url, Location from, @Nullable Location via, Location to, Date time, boolean dep, @Nullable TripOptions options) { + super.appendTripRequestParameters(url, from, via, to, time, dep, options); + + if (requestingMoreTrips) { + // ensure that the first displayed trip is after the given departure time / + // last displayed trip is before the given arrival time + url.addEncodedQueryParameter("calcOneDirection", "1"); + } + } + + private Date addMinutesToDate(Date initial, int minutes) { + Calendar c = new GregorianCalendar(timeZone); + c.setTime(initial); + c.add(Calendar.MINUTE, minutes); + return c.getTime(); + } } From f9dfefe4e9349294d40d372cb3374b654df63225 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Wed, 21 Feb 2024 21:12:18 +0100 Subject: [PATCH 3/3] fix tests in MvvProviderLiveTest --- .../pte/live/MvvProviderLiveTest.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/de/schildbach/pte/live/MvvProviderLiveTest.java b/test/de/schildbach/pte/live/MvvProviderLiveTest.java index c8c531320..bb198b2f0 100644 --- a/test/de/schildbach/pte/live/MvvProviderLiveTest.java +++ b/test/de/schildbach/pte/live/MvvProviderLiveTest.java @@ -17,6 +17,7 @@ package de.schildbach.pte.live; +import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.hasItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -131,16 +132,24 @@ public void suggestLocationsMarienplatz() throws Exception { public void suggestAddress() throws Exception { final SuggestLocationsResult result = suggestLocations("München, Maximilianstr. 1"); print(result); - assertThat(result.getLocations(), hasItem(new Location(LocationType.ADDRESS, - "streetID:1500000561::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289507:5870064:MRCV:BAY"))); + assertThat(result.getLocations(), anyOf( + hasItem(new Location(LocationType.ADDRESS, + "streetID:1500000040::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289423:5870091:MRCV:BAY")), + hasItem(new Location(LocationType.ADDRESS, + "streetID:1500000040::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289423:5870091:MRCV:bay")) + )); } @Test public void suggestStreet() throws Exception { final SuggestLocationsResult result = suggestLocations("München, Maximilianstr."); print(result); - assertThat(result.getLocations(), hasItem(new Location(LocationType.ADDRESS, - "streetID:1500000561::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289507:5870064:MRCV:BAY"))); + assertThat(result.getLocations(), anyOf( + hasItem(new Location(LocationType.ADDRESS, + "streetID:1500000040::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289423:5870091:MRCV:BAY")), + hasItem(new Location(LocationType.ADDRESS, + "streetID:1500000040::9162000:-1:Maximilianstraße:München:Maximilianstraße::Maximilianstraße: 80539 80538:ANY:DIVA_STREET:1289423:5870091:MRCV:bay")) + )); } @Test @@ -177,7 +186,7 @@ public void tripBetweenCoordinates() throws Exception { @Test public void tripBetweenCoordinateAndStation() throws Exception { final Location from = new Location(LocationType.ADDRESS, null, Point.from1E6(48238341, 11478230)); - final Location to = new Location(LocationType.ANY, null, null, "Ostbahnhof"); + final Location to = new Location(LocationType.ANY, null, null, "München, Ostbahnhof"); final QueryTripsResult result = queryTrips(from, null, to, new Date(), true, null); print(result); final QueryTripsResult laterResult = queryMoreTrips(result.context, true);