draft: continue cache

This commit is contained in:
Nemo D'ACREMONT 2024-11-30 18:24:40 +01:00
parent 6d0e8f9786
commit 932e272cc5
No known key found for this signature in database
GPG Key ID: 6E5BCE8022FA8276
9 changed files with 164 additions and 39 deletions

View File

@ -61,12 +61,20 @@ public class Main {
OpenMeteo openMeteo = new OpenMeteo(); OpenMeteo openMeteo = new OpenMeteo();
OpenWeatherMap openWeatherMap = new OpenWeatherMap(OpenWMapKey); OpenWeatherMap openWeatherMap = new OpenWeatherMap(OpenWMapKey);
WeatherDisplay display = new WeatherDisplayBasic(); WeatherDisplay display = new WeatherDisplayBasic();
WeatherDisplay display2 = new WeatherDisplayBasic();
WeatherDisplay display3 = new WeatherDisplayBasic();
display.addAPI(weatherAPI);
display.addAPI(weatherAPI); display.addAPI(weatherAPI);
display.addAPI(openMeteo); display.addAPI(openMeteo);
display.addAPI(openWeatherMap); display.addAPI(openWeatherMap);
display2.addAPI(weatherAPI);
display3.addAPI(weatherAPI);
// weatherAPI can't fetch for more than 3 days with free plan // weatherAPI can't fetch for more than 3 days with free plan
display.display(days, city); display.display(days, city);
display2.display(days, city);
display3.display(days, city);
} }
} }

View File

@ -17,6 +17,7 @@ import eirb.pg203.WeatherData.Condition;
public class OpenMeteo implements WeatherDataAPI { public class OpenMeteo implements WeatherDataAPI {
private static final String forecastBaseURL = "https://api.open-meteo.com/v1/forecast"; 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 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 // 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 { private JSONObject fetchWeather(int days, City city) throws IOException {
@ -28,8 +29,7 @@ public class OpenMeteo implements WeatherDataAPI {
) )
).toURL(); ).toURL();
JSONArray jsonArray = JSONFetcher.fetchArray(url); return JSONFetcher.fetch(url);
return jsonArray.getJSONObject(0);
} }
private static Condition getConditionFromCode(int WMOCode) { 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"); JSONObject daily = response.getJSONObject("daily");
float max_temp = daily.getJSONArray("temperature_2m_max").getFloat(day); float max_temp = daily.getJSONArray("temperature_2m_max").getFloat(day);
float min_temp = daily.getJSONArray("temperature_2m_min").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); int conditionCode = daily.getJSONArray("weather_code").getInt(day);
return new WeatherData( return new WeatherData(
new City(city), new City(cityName),
Instant.now(), Instant.now(),
temp_c, temp_c,
windSpeed, windSpeed,
@ -68,28 +68,33 @@ public class OpenMeteo implements WeatherDataAPI {
* @param day Day, 0 ≤ day ≤ 14 * @param day Day, 0 ≤ day ≤ 14
*/ */
@Override @Override
public WeatherData getTemperature(int day, String city) throws IOException { public WeatherData getTemperature(int day, String cityName) throws IOException {
JSONObject result = fetchWeather(day + 1, new City(city)); JSONObject result = fetchWeather(day + 1, new City(cityName));
return getWeatherDataFromForecast(result, day, city); return getWeatherDataFromForecast(result, day, cityName);
} }
@Override @Override
public WeatherData getTemperature(int day, int hour, String city) throws IOException{ public WeatherData getTemperature(int day, int hour, String cityName) throws IOException{
return getTemperature(day, city); return getTemperature(day, cityName);
} }
@Override @Override
public ArrayList<WeatherData> getTemperatures(int days, String city) throws IOException { public ArrayList<WeatherData> getTemperatures(int days, String cityName) throws IOException {
JSONObject result = fetchWeather(days, new City(city)); if (!this.cache.needsUpdate(cityName, days))
return this.cache.get(cityName, days);
JSONObject result = fetchWeather(days, new City(cityName));
ArrayList<WeatherData> weatherDatas = new ArrayList<>(); ArrayList<WeatherData> weatherDatas = new ArrayList<>();
for (int day = 0; day < days; ++day) { for (int day = 0; day < days; ++day) {
weatherDatas.add( weatherDatas.add(
getWeatherDataFromForecast(result, day, city) getWeatherDataFromForecast(result, day, cityName)
); );
} }
this.cache.set(cityName, days, weatherDatas, Instant.now());
return weatherDatas; return weatherDatas;
} }

View File

@ -19,6 +19,7 @@ import eirb.pg203.WeatherData.Condition;
public class OpenWeatherMap implements WeatherDataAPI { public class OpenWeatherMap implements WeatherDataAPI {
private static final String forecastBaseURL = "https://api.openweathermap.org/data/2.5/forecast"; private static final String forecastBaseURL = "https://api.openweathermap.org/data/2.5/forecast";
private String APIKey; private String APIKey;
private final WeatherDataCache cache = new WeatherDataCache();
OpenWeatherMap(String APIKey) { OpenWeatherMap(String APIKey) {
this.APIKey = APIKey; this.APIKey = APIKey;
@ -37,7 +38,7 @@ public class OpenWeatherMap implements WeatherDataAPI {
return JSONFetcher.fetch(url); 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"); JSONArray list = response.getJSONArray("list");
DayOfWeek targetedDay = Instant.now().plusSeconds(day * 24 * 3600).atZone(ZoneId.systemDefault()).getDayOfWeek(); DayOfWeek targetedDay = Instant.now().plusSeconds(day * 24 * 3600).atZone(ZoneId.systemDefault()).getDayOfWeek();
@ -85,7 +86,7 @@ public class OpenWeatherMap implements WeatherDataAPI {
return new WeatherData( return new WeatherData(
new City(city), new City(cityName),
Instant.now().plusSeconds(day * 24 * 3600), Instant.now().plusSeconds(day * 24 * 3600),
temp_c, temp_c,
windSpeed, windSpeed,
@ -98,29 +99,33 @@ public class OpenWeatherMap implements WeatherDataAPI {
* @param day Day, 0 &leq; day &leq; 14 * @param day Day, 0 &leq; day &leq; 14
*/ */
@Override @Override
public WeatherData getTemperature(int day, String city) throws IOException { public WeatherData getTemperature(int day, String cityName) throws IOException {
JSONObject result = fetchWeather(day+1, new City(city)); JSONObject result = fetchWeather(day+1, new City(cityName));
return getWeatherDataFromForecast(result, day, city); return getWeatherDataFromForecast(result, day, cityName);
} }
@Override @Override
public WeatherData getTemperature(int day, int hour, String city) throws IOException{ public WeatherData getTemperature(int day, int hour, String cityname) throws IOException{
return getTemperature(day, city); return getTemperature(day, cityname);
} }
@Override @Override
public ArrayList<WeatherData> getTemperatures(int days, String city) throws IOException { public ArrayList<WeatherData> getTemperatures(int days, String cityName) throws IOException {
JSONObject result = fetchWeather(days, new City(city)); if (!this.cache.needsUpdate(cityName, days))
return this.cache.get(cityName, days);
JSONObject result = fetchWeather(days, new City(cityName));
ArrayList<WeatherData> weatherDatas = new ArrayList<>(); ArrayList<WeatherData> weatherDatas = new ArrayList<>();
for (int day = 0; day < days ; ++day) { for (int day = 0; day < days ; ++day) {
weatherDatas.add( weatherDatas.add(
getWeatherDataFromForecast(result, day, city) getWeatherDataFromForecast(result, day, cityName)
); );
} }
this.cache.set(cityName, days, weatherDatas, Instant.now());
return weatherDatas; return weatherDatas;
} }

View File

@ -18,6 +18,7 @@ import java.util.ArrayList;
public class WeatherAPI implements WeatherDataAPI{ public class WeatherAPI implements WeatherDataAPI{
private final String weatherAPIKey; private final String weatherAPIKey;
private static final String forecastBaseURL = "https://api.weatherapi.com/v1/forecast.json"; private static final String forecastBaseURL = "https://api.weatherapi.com/v1/forecast.json";
private final WeatherDataCache cache = new WeatherDataCache();
WeatherAPI(String weatherAPIKey) { WeatherAPI(String weatherAPIKey) {
this.weatherAPIKey = weatherAPIKey; this.weatherAPIKey = weatherAPIKey;
@ -80,27 +81,32 @@ public class WeatherAPI implements WeatherDataAPI{
* @param day Day, 0 &leq; day &leq; 14 * @param day Day, 0 &leq; day &leq; 14
*/ */
@Override @Override
public WeatherData getTemperature(int day, String city) throws IOException { public WeatherData getTemperature(int day, String cityName) throws IOException {
JSONObject result = fetchWeather(day+1, city); JSONObject result = fetchWeather(day+1, cityName);
return getWeatherDataFromForecast(result, day, city); return getWeatherDataFromForecast(result, day, cityName);
} }
@Override @Override
public WeatherData getTemperature(int day, int hour, String city) throws IOException{ public WeatherData getTemperature(int day, int hour, String cityName) throws IOException{
return getTemperature(day, city); return getTemperature(day, cityName);
} }
@Override @Override
public ArrayList<WeatherData> getTemperatures(int days, String city) throws IOException { public ArrayList<WeatherData> getTemperatures(int days, String cityName) throws IOException {
JSONObject result = fetchWeather(days, city); if (!this.cache.needsUpdate(cityName, days))
return this.cache.get(cityName, days);
JSONObject result = fetchWeather(days, cityName);
ArrayList<WeatherData> weatherDatas = new ArrayList<>(); ArrayList<WeatherData> weatherDatas = new ArrayList<>();
for (int day = 0; day < days; ++day) { for (int day = 0; day < days; ++day) {
weatherDatas.add( weatherDatas.add(
getWeatherDataFromForecast(result, day, city) getWeatherDataFromForecast(result, day, cityName)
); );
} }
this.cache.set(cityName, days, weatherDatas, Instant.now());
return weatherDatas; return weatherDatas;
} }

View File

@ -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)
}

View File

@ -7,9 +7,7 @@ import org.json.JSONObject;
import com.sun.net.httpserver.Authenticator.Retry; import com.sun.net.httpserver.Authenticator.Retry;
import eirb.pg203.utils.JSONCachable; class WeatherData {
class WeatherData implements JSONCachable {
enum Condition { enum Condition {
SUNNY("☀️"), SUNNY("☀️"),
PARTIAL("🌤"), PARTIAL("🌤"),
@ -188,7 +186,6 @@ class WeatherData implements JSONCachable {
); );
} }
@Override
public JSONObject toJSON() { public JSONObject toJSON() {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
@ -203,8 +200,7 @@ class WeatherData implements JSONCachable {
return null; return null;
} }
@Override public static WeatherData fromJSON(JSONObject data) {
public JSONCachable fromJSON(JSONObject data) {
return null; return null;
} }

View File

@ -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<WeatherData> value;
private Instant timestamp;
CacheValue(ArrayList<WeatherData> value, Instant timestamp) {
this.value = value;
this.timestamp = timestamp;
}
public ArrayList<WeatherData> 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<WeatherData> 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<String, CacheValue> 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<WeatherData> get(String cityName, int days) {
CacheKey cacheKey = new CacheKey(cityName, days);
ArrayList<WeatherData> weatherData = this.cache.get(cacheKey).getWeatherData();
return weatherData;
}
public void set(String cityName, int days, ArrayList<WeatherData> value, Instant timestamp) {
CacheKey cacheKey = new CacheKey(cityName, days);
CacheValue cacheValue = new CacheValue(value, timestamp);
this.cache.put(cacheKey, cacheValue);
}
}

View File

@ -97,7 +97,6 @@ class WeatherDisplayBasic implements WeatherDisplay {
displayWeatherDatas(api.getAPIName(), weatherDataAPIArrayListHashMap.get(api), startColumnString, sourceColumnSize, dayColumnSize); displayWeatherDatas(api.getAPIName(), weatherDataAPIArrayListHashMap.get(api), startColumnString, sourceColumnSize, dayColumnSize);
this.displaySeparatorLine(days, sourceColumnSize, dayColumnSizeSeparator); this.displaySeparatorLine(days, sourceColumnSize, dayColumnSizeSeparator);
} }
} }
public void display(int days, String city) { public void display(int days, String city) {
@ -110,6 +109,7 @@ class WeatherDisplayBasic implements WeatherDisplay {
System.err.println(e); System.err.println(e);
} }
} }
this.displayAllWeatherDatas(weatherDatasMap, days); this.displayAllWeatherDatas(weatherDatasMap, days);
} }

View File

@ -21,6 +21,7 @@ public class JSONFetcher {
result.append(line); result.append(line);
} }
} }
System.out.println(url);
return result.toString(); return result.toString();
} }