diff --git a/src/main/java/eirb/pg203/Main.java b/src/main/java/eirb/pg203/Main.java index 1ed2048..1c0574c 100644 --- a/src/main/java/eirb/pg203/Main.java +++ b/src/main/java/eirb/pg203/Main.java @@ -61,12 +61,20 @@ public class Main { OpenMeteo openMeteo = new OpenMeteo(); OpenWeatherMap openWeatherMap = new OpenWeatherMap(OpenWMapKey); WeatherDisplay display = new WeatherDisplayBasic(); + WeatherDisplay display2 = new WeatherDisplayBasic(); + WeatherDisplay display3 = new WeatherDisplayBasic(); + display.addAPI(weatherAPI); display.addAPI(weatherAPI); display.addAPI(openMeteo); display.addAPI(openWeatherMap); + display2.addAPI(weatherAPI); + display3.addAPI(weatherAPI); + // weatherAPI can't fetch for more than 3 days with free plan display.display(days, city); + display2.display(days, city); + display3.display(days, city); } } diff --git a/src/main/java/eirb/pg203/OpenMeteo.java b/src/main/java/eirb/pg203/OpenMeteo.java index c33e999..6d45992 100644 --- a/src/main/java/eirb/pg203/OpenMeteo.java +++ b/src/main/java/eirb/pg203/OpenMeteo.java @@ -17,6 +17,7 @@ import eirb.pg203.WeatherData.Condition; public class OpenMeteo implements WeatherDataAPI { private static final String forecastBaseURL = "https://api.open-meteo.com/v1/forecast"; private static final String dailyQuery = "weather_code,temperature_2m_max,temperature_2m_min,wind_speed_10m_max,wind_direction_10m_dominant"; + private final WeatherDataCache cache = new WeatherDataCache(); // https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM private JSONObject fetchWeather(int days, City city) throws IOException { @@ -28,8 +29,7 @@ public class OpenMeteo implements WeatherDataAPI { ) ).toURL(); - JSONArray jsonArray = JSONFetcher.fetchArray(url); - return jsonArray.getJSONObject(0); + return JSONFetcher.fetch(url); } private static Condition getConditionFromCode(int WMOCode) { @@ -44,7 +44,7 @@ public class OpenMeteo implements WeatherDataAPI { } - private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String city) { + private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String cityName) { JSONObject daily = response.getJSONObject("daily"); float max_temp = daily.getJSONArray("temperature_2m_max").getFloat(day); float min_temp = daily.getJSONArray("temperature_2m_min").getFloat(day); @@ -55,7 +55,7 @@ public class OpenMeteo implements WeatherDataAPI { int conditionCode = daily.getJSONArray("weather_code").getInt(day); return new WeatherData( - new City(city), + new City(cityName), Instant.now(), temp_c, windSpeed, @@ -68,28 +68,33 @@ public class OpenMeteo implements WeatherDataAPI { * @param day Day, 0 ≤ day ≤ 14 */ @Override - public WeatherData getTemperature(int day, String city) throws IOException { - JSONObject result = fetchWeather(day + 1, new City(city)); + public WeatherData getTemperature(int day, String cityName) throws IOException { + JSONObject result = fetchWeather(day + 1, new City(cityName)); - return getWeatherDataFromForecast(result, day, city); + return getWeatherDataFromForecast(result, day, cityName); } @Override - public WeatherData getTemperature(int day, int hour, String city) throws IOException{ - return getTemperature(day, city); + public WeatherData getTemperature(int day, int hour, String cityName) throws IOException{ + return getTemperature(day, cityName); } @Override - public ArrayList getTemperatures(int days, String city) throws IOException { - JSONObject result = fetchWeather(days, new City(city)); + public ArrayList getTemperatures(int days, String cityName) throws IOException { + if (!this.cache.needsUpdate(cityName, days)) + return this.cache.get(cityName, days); + + JSONObject result = fetchWeather(days, new City(cityName)); ArrayList weatherDatas = new ArrayList<>(); for (int day = 0; day < days; ++day) { weatherDatas.add( - getWeatherDataFromForecast(result, day, city) + getWeatherDataFromForecast(result, day, cityName) ); } + this.cache.set(cityName, days, weatherDatas, Instant.now()); + return weatherDatas; } diff --git a/src/main/java/eirb/pg203/OpenWeatherMap.java b/src/main/java/eirb/pg203/OpenWeatherMap.java index 6ea2d91..fa4f520 100644 --- a/src/main/java/eirb/pg203/OpenWeatherMap.java +++ b/src/main/java/eirb/pg203/OpenWeatherMap.java @@ -19,6 +19,7 @@ import eirb.pg203.WeatherData.Condition; public class OpenWeatherMap implements WeatherDataAPI { private static final String forecastBaseURL = "https://api.openweathermap.org/data/2.5/forecast"; private String APIKey; + private final WeatherDataCache cache = new WeatherDataCache(); OpenWeatherMap(String APIKey) { this.APIKey = APIKey; @@ -37,7 +38,7 @@ public class OpenWeatherMap implements WeatherDataAPI { return JSONFetcher.fetch(url); } - private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String city) { + private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String cityName) { JSONArray list = response.getJSONArray("list"); DayOfWeek targetedDay = Instant.now().plusSeconds(day * 24 * 3600).atZone(ZoneId.systemDefault()).getDayOfWeek(); @@ -85,7 +86,7 @@ public class OpenWeatherMap implements WeatherDataAPI { return new WeatherData( - new City(city), + new City(cityName), Instant.now().plusSeconds(day * 24 * 3600), temp_c, windSpeed, @@ -98,29 +99,33 @@ public class OpenWeatherMap implements WeatherDataAPI { * @param day Day, 0 ≤ day ≤ 14 */ @Override - public WeatherData getTemperature(int day, String city) throws IOException { - JSONObject result = fetchWeather(day+1, new City(city)); + public WeatherData getTemperature(int day, String cityName) throws IOException { + JSONObject result = fetchWeather(day+1, new City(cityName)); - return getWeatherDataFromForecast(result, day, city); + return getWeatherDataFromForecast(result, day, cityName); } @Override - public WeatherData getTemperature(int day, int hour, String city) throws IOException{ - return getTemperature(day, city); + public WeatherData getTemperature(int day, int hour, String cityname) throws IOException{ + return getTemperature(day, cityname); } @Override - public ArrayList getTemperatures(int days, String city) throws IOException { - JSONObject result = fetchWeather(days, new City(city)); + public ArrayList getTemperatures(int days, String cityName) throws IOException { + if (!this.cache.needsUpdate(cityName, days)) + return this.cache.get(cityName, days); + JSONObject result = fetchWeather(days, new City(cityName)); ArrayList weatherDatas = new ArrayList<>(); for (int day = 0; day < days ; ++day) { weatherDatas.add( - getWeatherDataFromForecast(result, day, city) + getWeatherDataFromForecast(result, day, cityName) ); } + this.cache.set(cityName, days, weatherDatas, Instant.now()); + return weatherDatas; } diff --git a/src/main/java/eirb/pg203/WeatherAPI.java b/src/main/java/eirb/pg203/WeatherAPI.java index 5f76aae..8ea17a4 100644 --- a/src/main/java/eirb/pg203/WeatherAPI.java +++ b/src/main/java/eirb/pg203/WeatherAPI.java @@ -18,6 +18,7 @@ import java.util.ArrayList; public class WeatherAPI implements WeatherDataAPI{ private final String weatherAPIKey; private static final String forecastBaseURL = "https://api.weatherapi.com/v1/forecast.json"; + private final WeatherDataCache cache = new WeatherDataCache(); WeatherAPI(String weatherAPIKey) { this.weatherAPIKey = weatherAPIKey; @@ -80,27 +81,32 @@ public class WeatherAPI implements WeatherDataAPI{ * @param day Day, 0 ≤ day ≤ 14 */ @Override - public WeatherData getTemperature(int day, String city) throws IOException { - JSONObject result = fetchWeather(day+1, city); - return getWeatherDataFromForecast(result, day, city); + public WeatherData getTemperature(int day, String cityName) throws IOException { + JSONObject result = fetchWeather(day+1, cityName); + return getWeatherDataFromForecast(result, day, cityName); } @Override - public WeatherData getTemperature(int day, int hour, String city) throws IOException{ - return getTemperature(day, city); + public WeatherData getTemperature(int day, int hour, String cityName) throws IOException{ + return getTemperature(day, cityName); } @Override - public ArrayList getTemperatures(int days, String city) throws IOException { - JSONObject result = fetchWeather(days, city); + public ArrayList getTemperatures(int days, String cityName) throws IOException { + if (!this.cache.needsUpdate(cityName, days)) + return this.cache.get(cityName, days); + + JSONObject result = fetchWeather(days, cityName); ArrayList weatherDatas = new ArrayList<>(); for (int day = 0; day < days; ++day) { weatherDatas.add( - getWeatherDataFromForecast(result, day, city) - ); + getWeatherDataFromForecast(result, day, cityName) + ); } + this.cache.set(cityName, days, weatherDatas, Instant.now()); + return weatherDatas; } diff --git a/src/main/java/eirb/pg203/WeatherCachedAPI.java b/src/main/java/eirb/pg203/WeatherCachedAPI.java new file mode 100644 index 0000000..627308e --- /dev/null +++ b/src/main/java/eirb/pg203/WeatherCachedAPI.java @@ -0,0 +1,10 @@ +package eirb.pg203; + +import org.json.JSONObject; + +abstract class WeatherCachedAPI implements WeatherDataAPI { + private final WeatherDataCache cache = new WeatherDataCache(); + + public void loadCache(JSONObject cache) + +} diff --git a/src/main/java/eirb/pg203/WeatherData.java b/src/main/java/eirb/pg203/WeatherData.java index 67790be..29b806c 100644 --- a/src/main/java/eirb/pg203/WeatherData.java +++ b/src/main/java/eirb/pg203/WeatherData.java @@ -7,9 +7,7 @@ import org.json.JSONObject; import com.sun.net.httpserver.Authenticator.Retry; -import eirb.pg203.utils.JSONCachable; - -class WeatherData implements JSONCachable { +class WeatherData { enum Condition { SUNNY("☀️"), PARTIAL("🌤"), @@ -188,7 +186,6 @@ class WeatherData implements JSONCachable { ); } - @Override public JSONObject toJSON() { JSONObject jsonObject = new JSONObject(); @@ -203,8 +200,7 @@ class WeatherData implements JSONCachable { return null; } - @Override - public JSONCachable fromJSON(JSONObject data) { + public static WeatherData fromJSON(JSONObject data) { return null; } diff --git a/src/main/java/eirb/pg203/WeatherDataCache.java b/src/main/java/eirb/pg203/WeatherDataCache.java new file mode 100644 index 0000000..e61fb8d --- /dev/null +++ b/src/main/java/eirb/pg203/WeatherDataCache.java @@ -0,0 +1,94 @@ +package eirb.pg203; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class WeatherDataCache { + static class CacheValue { + private ArrayList value; + private Instant timestamp; + + CacheValue(ArrayList value, Instant timestamp) { + this.value = value; + this.timestamp = timestamp; + } + + public ArrayList getWeatherData() { + return value; + } + + public Instant getTimestamp() { + return timestamp; + } + + /* + * Will parse CacheValues with { "api": apiName } from JSON + */ + JSONObject toJSON(String apiName, String cityName) { + JSONObject jsonObject = new JSONObject(); + JSONArray values = new JSONArray(); + + jsonObject.put("city", cityName); + jsonObject.put("api", apiName); + jsonObject.put("timestamp", this.timestamp.getEpochSecond()); + + for (int i = 0 ; i < this.value.size() ; ++i) + values.put(this.value.get(i)); + + jsonObject.put("value", values); + + return jsonObject; + } + + public static CacheValue fromJSON(JSONObject data) { + ArrayList value = new ArrayList<>(); + + JSONArray values = data.getJSONArray("value"); + for (int i = 0 ; i < values.length() ; ++i) + value.add(WeatherData.fromJSON(values.getJSONObject(i))); + + Instant timestamp = Instant.ofEpochSecond(data.getLong("timestamp")); + + return new CacheValue(value, timestamp); + } + } + + private HashMap cache = new HashMap<>(); + private long cacheTTL = 3600; // Cache data Time To Live + + public boolean has(String cityName, int days) { + CacheValue cacheValue = this.cache.get(cityName); + + return cacheValue != null; + } + + public boolean needsUpdate(String cityName, int days) { + if (!has(cityName, days)) // if (cityName, days) isn't cached, needs udpate + return true; + + CacheKey cacheKey = new CacheKey(cityName, days); + CacheValue cacheValue = this.cache.get(cacheKey); + + long dt = Instant.now().getEpochSecond() - cacheValue.getTimestamp().getEpochSecond(); + + return dt > this.cacheTTL; // if older than TTL, needs update + } + + public ArrayList get(String cityName, int days) { + CacheKey cacheKey = new CacheKey(cityName, days); + ArrayList weatherData = this.cache.get(cacheKey).getWeatherData(); + + return weatherData; + } + + public void set(String cityName, int days, ArrayList value, Instant timestamp) { + CacheKey cacheKey = new CacheKey(cityName, days); + CacheValue cacheValue = new CacheValue(value, timestamp); + + this.cache.put(cacheKey, cacheValue); + } +} diff --git a/src/main/java/eirb/pg203/WeatherDisplayBasic.java b/src/main/java/eirb/pg203/WeatherDisplayBasic.java index d224da1..4ee2fca 100644 --- a/src/main/java/eirb/pg203/WeatherDisplayBasic.java +++ b/src/main/java/eirb/pg203/WeatherDisplayBasic.java @@ -97,10 +97,9 @@ class WeatherDisplayBasic implements WeatherDisplay { displayWeatherDatas(api.getAPIName(), weatherDataAPIArrayListHashMap.get(api), startColumnString, sourceColumnSize, dayColumnSize); this.displaySeparatorLine(days, sourceColumnSize, dayColumnSizeSeparator); } - } - public void display(int days, String city) { + public void display(int days, String city) { HashMap> weatherDatasMap = new HashMap<>(); for (WeatherDataAPI w: apis) { @@ -110,6 +109,7 @@ class WeatherDisplayBasic implements WeatherDisplay { System.err.println(e); } } + this.displayAllWeatherDatas(weatherDatasMap, days); } diff --git a/src/main/java/eirb/pg203/utils/JSONFetcher.java b/src/main/java/eirb/pg203/utils/JSONFetcher.java index bf3d928..54e108e 100644 --- a/src/main/java/eirb/pg203/utils/JSONFetcher.java +++ b/src/main/java/eirb/pg203/utils/JSONFetcher.java @@ -21,6 +21,7 @@ public class JSONFetcher { result.append(line); } } + System.out.println(url); return result.toString(); }