commit d1752ada1e84bdf02d0e251c7d0b4e5ce2134361
parent a287fa62af88f84cc56e85f5672da60d94c70a68
Author: Kebigon <git@kebigon.xyz>
Date: Sun, 5 Apr 2020 18:29:26 +0900
Add fastest/cheapest/easiest route function to use in expressions
Diffstat:
4 files changed, 170 insertions(+), 149 deletions(-)
diff --git a/src/main/java/xyz/kebigon/housesearch/domain/SearchConditionsValidator.java b/src/main/java/xyz/kebigon/housesearch/domain/SearchConditionsValidator.java
@@ -1,5 +1,9 @@
package xyz.kebigon.housesearch.domain;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Stream;
+
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -13,137 +17,151 @@ import xyz.kebigon.housesearch.ApplicationContext;
@Slf4j
public class SearchConditionsValidator
{
- public static boolean validateBasicConditions(Posting posting, SearchConditions conditions)
- {
- final String url = posting.getUrl();
- if (url == null)
- {
- log.debug("Missing url for {}", posting);
- return false;
- }
-
- final Long price = posting.getPrice();
- if (price == null)
- {
- log.debug("Missing price for {}", posting);
- return false;
- }
- if (conditions.getMinPrice() != null && price < conditions.getMinPrice())
- return false;
- if (conditions.getMaxPrice() != null && price > conditions.getMaxPrice())
- return false;
-
- final Integer age = posting.getAge();
- if (age == null)
- {
- log.debug("Missing age for {}", posting);
- return false;
- }
- if (conditions.getMinAge() != null && age < conditions.getMinAge())
- return false;
- if (conditions.getMaxAge() != null && age > conditions.getMaxAge())
- return false;
-
- final Double landSurface = posting.getLandSurface();
- if (landSurface == null)
- {
- log.debug("Missing landSurface for {}", posting);
- return false;
- }
- if (conditions.getMinLandSurface() != null && landSurface < conditions.getMinLandSurface())
- return false;
- if (conditions.getMaxLandSurface() != null && landSurface > conditions.getMaxLandSurface())
- return false;
-
- final Double houseSurface = posting.getHouseSurface();
- if (houseSurface == null)
- {
- log.debug("Missing houseSurface for {}", posting);
- return false;
- }
- if (conditions.getMinHouseSurface() != null && houseSurface < conditions.getMinHouseSurface())
- return false;
- if (conditions.getMaxHouseSurface() != null && houseSurface > conditions.getMaxHouseSurface())
- return false;
-
- final Integer walkTimeToStation = posting.getWalkTimeToStation();
- if (walkTimeToStation == null)
- {
- log.debug("Missing walkTimeToStation for {}", posting);
- return false;
- }
- if (conditions.getMaxWalkTimeToStation() != null && walkTimeToStation > conditions.getMaxWalkTimeToStation())
- return false;
-
- final String station = posting.getStation();
- if (station == null)
- {
- log.debug("Missing station for {}", posting);
- return false;
- }
-
- return true;
- }
-
- public static boolean validateExpression(Posting posting, SearchConditions conditions)
- {
- final ExpressionParser expressionParser = new SpelExpressionParser();
-
- final StandardEvaluationContext context = new StandardEvaluationContext();
- context.setVariable("property", posting);
-
- try
- {
- context.registerFunction("timeToStation",
- SearchConditionsValidator.class.getDeclaredMethod("timeToStation", new Class[] { Posting.class, String.class }));
- context.registerFunction("fareToStation",
- SearchConditionsValidator.class.getDeclaredMethod("fareToStation", new Class[] { Posting.class, String.class }));
- context.registerFunction("transferToStation",
- SearchConditionsValidator.class.getDeclaredMethod("transferToStation", new Class[] { Posting.class, String.class }));
- }
- catch (NoSuchMethodException | SecurityException e)
- {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
-
- final Expression expression = expressionParser.parseExpression(conditions.getExpression());
- return expression.getValue(context, posting, Boolean.class);
- }
-
- @SuppressWarnings("unused")
- private static int timeToStation(Posting posting, String station)
- {
- final Route route = ApplicationContext.getYahooTransitBrowser() //
- .search(posting.getStation(), station).stream() //
- .min(Route.TIME_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
-
- posting.updateRoutes(route);
-
- return route.getTime() + posting.getWalkTimeToStation();
- }
-
- @SuppressWarnings("unused")
- private static int fareToStation(Posting posting, String station)
- {
- final Route route = ApplicationContext.getYahooTransitBrowser() //
- .search(posting.getStation(), station).stream() //
- .min(Route.FARE_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
-
- posting.updateRoutes(route);
-
- return route.getFare();
- }
-
- @SuppressWarnings("unused")
- private static int transferToStation(Posting posting, String station)
- {
- final Route route = ApplicationContext.getYahooTransitBrowser() //
- .search(posting.getStation(), station).stream() //
- .min(Route.TRANSFER_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
-
- posting.updateRoutes(route);
-
- return route.getTransfer();
- }
+ public static boolean validateBasicConditions(Posting posting, SearchConditions conditions)
+ {
+ final String url = posting.getUrl();
+ if (url == null)
+ {
+ log.debug("Missing url for {}", posting);
+ return false;
+ }
+
+ final Long price = posting.getPrice();
+ if (price == null)
+ {
+ log.debug("Missing price for {}", posting);
+ return false;
+ }
+ if (conditions.getMinPrice() != null && price < conditions.getMinPrice())
+ return false;
+ if (conditions.getMaxPrice() != null && price > conditions.getMaxPrice())
+ return false;
+
+ final Integer age = posting.getAge();
+ if (age == null)
+ {
+ log.debug("Missing age for {}", posting);
+ return false;
+ }
+ if (conditions.getMinAge() != null && age < conditions.getMinAge())
+ return false;
+ if (conditions.getMaxAge() != null && age > conditions.getMaxAge())
+ return false;
+
+ final Double landSurface = posting.getLandSurface();
+ if (landSurface == null)
+ {
+ log.debug("Missing landSurface for {}", posting);
+ return false;
+ }
+ if (conditions.getMinLandSurface() != null && landSurface < conditions.getMinLandSurface())
+ return false;
+ if (conditions.getMaxLandSurface() != null && landSurface > conditions.getMaxLandSurface())
+ return false;
+
+ final Double houseSurface = posting.getHouseSurface();
+ if (houseSurface == null)
+ {
+ log.debug("Missing houseSurface for {}", posting);
+ return false;
+ }
+ if (conditions.getMinHouseSurface() != null && houseSurface < conditions.getMinHouseSurface())
+ return false;
+ if (conditions.getMaxHouseSurface() != null && houseSurface > conditions.getMaxHouseSurface())
+ return false;
+
+ final Integer walkTimeToStation = posting.getWalkTimeToStation();
+ if (walkTimeToStation == null)
+ {
+ log.debug("Missing walkTimeToStation for {}", posting);
+ return false;
+ }
+ if (conditions.getMaxWalkTimeToStation() != null && walkTimeToStation > conditions.getMaxWalkTimeToStation())
+ return false;
+
+ final String station = posting.getStation();
+ if (station == null)
+ {
+ log.debug("Missing station for {}", posting);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static boolean validateExpression(Posting posting, SearchConditions conditions)
+ {
+ final ExpressionParser expressionParser = new SpelExpressionParser();
+
+ final StandardEvaluationContext context = new StandardEvaluationContext();
+ context.setVariable("property", posting);
+
+ try
+ {
+ context.registerFunction("fastestRoute",
+ SearchConditionsValidator.class.getDeclaredMethod("getFastestRoute", new Class[] { Posting.class, String[].class }));
+ context.registerFunction("cheapestRoute",
+ SearchConditionsValidator.class.getDeclaredMethod("getCheapestRoute", new Class[] { Posting.class, String[].class }));
+ context.registerFunction("easiestRoute",
+ SearchConditionsValidator.class.getDeclaredMethod("getEasiestRoute", new Class[] { Posting.class, String[].class }));
+
+ context.registerFunction("timeToStation",
+ SearchConditionsValidator.class.getDeclaredMethod("timeToStation", new Class[] { Posting.class, String.class }));
+ context.registerFunction("fareToStation",
+ SearchConditionsValidator.class.getDeclaredMethod("fareToStation", new Class[] { Posting.class, String.class }));
+ context.registerFunction("transferToStation",
+ SearchConditionsValidator.class.getDeclaredMethod("transferToStation", new Class[] { Posting.class, String.class }));
+ }
+ catch (NoSuchMethodException | SecurityException e)
+ {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ final Expression expression = expressionParser.parseExpression(conditions.getExpression());
+ return expression.getValue(context, posting, Boolean.class);
+ }
+
+ private static Stream<Route> getRoutes(Posting posting, String... stations)
+ {
+ return Arrays.stream(stations) //
+ .flatMap(station -> {
+ final Collection<Route> routes = ApplicationContext.getYahooTransitBrowser().search(posting.getStation(), station);
+ routes.forEach(posting::updateRoutes);
+ return routes.stream();
+ });
+ }
+
+ private static Route getFastestRoute(Posting posting, String... stations)
+ {
+ return getRoutes(posting, stations).min(Route.TIME_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
+ }
+
+ private static Route getCheapestRoute(Posting posting, String... stations)
+ {
+ return getRoutes(posting, stations).min(Route.FARE_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
+ }
+
+ private static Route getEasiestRoute(Posting posting, String... stations)
+ {
+ return getRoutes(posting, stations).min(Route.TRANSFER_COMPARATOR).orElse(Route.IMPOSSIBLE_ROUTE);
+ }
+
+ @SuppressWarnings("unused")
+ private static int timeToStation(Posting posting, String station)
+ {
+ return getFastestRoute(posting, station).getTime() + posting.getWalkTimeToStation();
+ }
+
+ @SuppressWarnings("unused")
+ private static int fareToStation(Posting posting, String station)
+ {
+ return getCheapestRoute(posting, station).getFare();
+ }
+
+ @SuppressWarnings("unused")
+ private static int transferToStation(Posting posting, String station)
+ {
+ return getEasiestRoute(posting, station).getTransfer();
+ }
}
diff --git a/src/main/packaged-resources/cfg/cheap-house-expression.cfg b/src/main/packaged-resources/cfg/cheap-house-expression.cfg
@@ -1,10 +1,8 @@
-# At least one good route
(
- (#timeToStation(#property, '東京駅') <= 85 && #fareToStation(#property, '東京駅') <= 650 && #transferToStation(#property, '東京駅') <= 1)
- || (#timeToStation(#property, '三越前') <= 85 && #fareToStation(#property, '三越前') <= 650 && #transferToStation(#property, '三越前') <= 1)
- || (#timeToStation(#property, '大手町(東京都)駅') <= 85 && #fareToStation(#property, '大手町(東京都)駅') <= 650 && #transferToStation(#property, '大手町(東京都)駅') <= 1)
+ (#fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').time + #property.walkTimeToStation) <= 80
+ && #fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').fare <= 650
+ && #fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').transfer <= 1
)
-# Mistaken with Tokyo's Akasaka
&& !url.startsWith('https://suumo.jp/chukoikkodate/gumma/sc_maebashi')
\ No newline at end of file
diff --git a/src/main/packaged-resources/cfg/searches.json b/src/main/packaged-resources/cfg/searches.json
@@ -4,7 +4,7 @@
"area": "KANTO",
"type": "USED_HOUSE",
"price.min": null,
- "price.max": 12000000,
+ "price.max": 11000000,
"age.min": null,
"age.max": 20,
"surface.land.min": 100,
diff --git a/src/main/packaged-resources/cfg/tokyo-house-expression.cfg b/src/main/packaged-resources/cfg/tokyo-house-expression.cfg
@@ -3,22 +3,27 @@
(
# Fast route
- (#timeToStation(#property, '東京駅') <= 40 && #fareToStation(#property, '東京駅') <= 750 && #transferToStation(#property, '東京駅') <= 1)
- || (#timeToStation(#property, '三越前') <= 40 && #fareToStation(#property, '三越前') <= 750 && #transferToStation(#property, '三越前') <= 1)
- || (#timeToStation(#property, '大手町(東京都)駅') <= 40 && #fareToStation(#property, '大手町(東京都)駅') <= 750 && #transferToStation(#property, '大手町(東京都)駅') <= 1)
+ (
+ (#fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').time + #property.walkTimeToStation) <= 40
+ && #fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').fare <= 750
+ && #fastestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').transfer <= 1
+ )
# Cheap route
- || (#timeToStation(#property, '東京駅') <= 60 && #fareToStation(#property, '東京駅') <= 500 && #transferToStation(#property, '東京駅') <= 1)
- || (#timeToStation(#property, '三越前') <= 60 && #fareToStation(#property, '三越前') <= 500 && #transferToStation(#property, '三越前') <= 1)
- || (#timeToStation(#property, '大手町(東京都)駅') <= 60 && #fareToStation(#property, '大手町(東京都)駅') <= 500 && #transferToStation(#property, '大手町(東京都)駅') <= 1)
+ || (
+ (#cheapestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').time + #property.walkTimeToStation) <= 60
+ && #cheapestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').fare <= 500
+ && #cheapestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').transfer <= 1
+ )
# Direct route
- || (#timeToStation(#property, '東京駅') <= 60 && #fareToStation(#property, '東京駅') <= 750 && #transferToStation(#property, '東京駅') == 0)
- || (#timeToStation(#property, '三越前') <= 60 && #fareToStation(#property, '三越前') <= 750 && #transferToStation(#property, '三越前') == 0)
- || (#timeToStation(#property, '大手町(東京都)駅') <= 60 && #fareToStation(#property, '大手町(東京都)駅') <= 750 && #transferToStation(#property, '大手町(東京都)駅') == 0)
+ || (
+ (#easiestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').time + #property.walkTimeToStation) <= 60
+ && #easiestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').fare <= 750
+ && #easiestRoute(#property, '東京駅', '三越前', '大手町(東京都)駅').transfer == 0
+ )
)
-# Mistaken with Tokyo's Akasaka
&& !url.startsWith('https://suumo.jp/chukoikkodate/gumma/sc_maebashi')
\ No newline at end of file