From 2486e64be289db044ba25dd1990a42e58b600ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Gulczy=C5=84ski?= Date: Fri, 15 Dec 2023 23:51:44 +0100 Subject: [PATCH] final commit project v1 --- .env | 2 + .env.example | 2 + .gitignore | 29 ++++ .idea/.gitignore | 8 + .idea/aws.xml | 17 ++ .idea/compiler.xml | 13 ++ .idea/encodings.xml | 7 + .idea/inspectionProfiles/Project_Default.xml | 64 ++++++++ .idea/jarRepositories.xml | 25 +++ .idea/misc.xml | 12 ++ README.md | 7 + pom.xml | 84 ++++++++++ .../java/org/adamgulczynski/DataFetcher.java | 52 ++++++ .../java/org/adamgulczynski/JsonReader.java | 54 +++++++ src/main/java/org/adamgulczynski/Main.java | 97 +++++++++++ .../java/org/adamgulczynski/WeatherData.java | 153 ++++++++++++++++++ .../org/adamgulczynski/WeatherParser.java | 20 +++ src/main/java/org/adamgulczynski/data.json | 37 +++++ src/test/java/WeatherDataTest.java | 110 +++++++++++++ .../org/adamgulczynski/DataFetcher.class | Bin 0 -> 2466 bytes .../org/adamgulczynski/JsonReader.class | Bin 0 -> 3175 bytes target/classes/org/adamgulczynski/Main.class | Bin 0 -> 4443 bytes .../org/adamgulczynski/WeatherData.class | Bin 0 -> 6414 bytes .../org/adamgulczynski/WeatherParser.class | Bin 0 -> 1203 bytes target/test-classes/DataFetcherTest.class | Bin 0 -> 852 bytes target/test-classes/WeatherDataTest.class | Bin 0 -> 3834 bytes 26 files changed, 793 insertions(+) create mode 100644 .env create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/aws.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/org/adamgulczynski/DataFetcher.java create mode 100644 src/main/java/org/adamgulczynski/JsonReader.java create mode 100644 src/main/java/org/adamgulczynski/Main.java create mode 100644 src/main/java/org/adamgulczynski/WeatherData.java create mode 100644 src/main/java/org/adamgulczynski/WeatherParser.java create mode 100644 src/main/java/org/adamgulczynski/data.json create mode 100644 src/test/java/WeatherDataTest.java create mode 100644 target/classes/org/adamgulczynski/DataFetcher.class create mode 100644 target/classes/org/adamgulczynski/JsonReader.class create mode 100644 target/classes/org/adamgulczynski/Main.class create mode 100644 target/classes/org/adamgulczynski/WeatherData.class create mode 100644 target/classes/org/adamgulczynski/WeatherParser.class create mode 100644 target/test-classes/DataFetcherTest.class create mode 100644 target/test-classes/WeatherDataTest.class diff --git a/.env b/.env new file mode 100644 index 0000000..50cbe1c --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +API_KEY=620e5e3fe298cdf4c0e435e2c8f4eb8e +BASE_PATH=/Users/adamgulczynski/IdeaProjects/project1/src/main/java/org/adamgulczynski/ \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..09c9fec --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +API_KEY= +BASE_PATH= \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f68d109 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/aws.xml b/.idea/aws.xml new file mode 100644 index 0000000..2c4ea32 --- /dev/null +++ b/.idea/aws.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..38438f7 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..fd9de1c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,64 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..5bded3b --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..aacacf5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fd9198 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Java pracownia project 1 + +### Działanie programu: +Po uruchomieniu programu, w konsoli należy wybrac jedno z miast (wielkość liter nie ma znaczenie) +Dane dotyczace miast znajduja sie w pliku `data.json` + +Informacje w konsoli sa wuyswietlane po kolei i przy każdym etapie prosza o konkretna czynnosc. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e07880d --- /dev/null +++ b/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + org.adamgulczynski + project1 + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + + + + + com.e-iceblue + e-iceblue + https://repo.e-iceblue.com/nexus/content/groups/public/ + + + + + + com.fasterxml.jackson.core + jackson-databind + 2.13.0 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.13.0 + + + + io.github.cdimascio + dotenv-java + 3.0.0 + + + + org.json + json + 20230227 + + + + org.apache.pdfbox + pdfbox + 3.0.1 + + + + e-iceblue + spire.pdf + 9.12.0 + + + + org.mockito + mockito-core + 3.12.4 + test + + + + junit + junit + 4.13.2 + test + + + + com.lowagie + itext + 2.1.7 + + + + + \ No newline at end of file diff --git a/src/main/java/org/adamgulczynski/DataFetcher.java b/src/main/java/org/adamgulczynski/DataFetcher.java new file mode 100644 index 0000000..bf4f14e --- /dev/null +++ b/src/main/java/org/adamgulczynski/DataFetcher.java @@ -0,0 +1,52 @@ +package org.adamgulczynski; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class DataFetcher { + private final String apiKey; + + public DataFetcher(String apiKey) { + this.apiKey = apiKey; + } + + public String fetchWeather(double latitude, double longitude) throws IllegalArgumentException { + + if (latitude > 90 || latitude < -90) { + throw new IllegalArgumentException(); + } else if (longitude > 180 || longitude < -180) { + throw new IllegalArgumentException(); + } + + String urlStr = "https://api.openweathermap.org/data/2.5/weather?lat=" + + latitude + "&lon=" + longitude + "&appid=" + this.apiKey; + + try { + URL url = new URL(urlStr); + + // Open a connection to the URL + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + // Set the request method to GET + connection.setRequestMethod("GET"); + + // Read the response from the server + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + String line; + StringBuilder response = new StringBuilder(); + + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + return response.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } +} diff --git a/src/main/java/org/adamgulczynski/JsonReader.java b/src/main/java/org/adamgulczynski/JsonReader.java new file mode 100644 index 0000000..61b3c38 --- /dev/null +++ b/src/main/java/org/adamgulczynski/JsonReader.java @@ -0,0 +1,54 @@ +package org.adamgulczynski; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonReader { + private Map> dataMap; + + public Map> readFile(String filePath) { + // Create a map to store the data + Map> cityDataMap = new HashMap<>(); + + try { + // Create ObjectMapper + ObjectMapper objectMapper = new ObjectMapper(); + + // Read JSON array from file into JsonNode + JsonNode jsonArray = objectMapper.readTree(new File(filePath)); + + for (JsonNode jsonNode : jsonArray) { + String city = jsonNode.get("city").asText(); + double lat = jsonNode.get("lat").asDouble(); + double lon = jsonNode.get("lon").asDouble(); + + // Create a map for latitude and longitude + Map latLngMap = new HashMap<>(); + latLngMap.put("latitude", lat); + latLngMap.put("longitude", lon); + + // Put the data into the main map + cityDataMap.put(city, latLngMap); + } + this.dataMap = cityDataMap; + } catch (IOException e) { + e.printStackTrace(); + } + return cityDataMap; + } + + public void printEntries() { + short index = 1; + Set keys = this.dataMap.keySet(); + for (String key : keys) { + System.out.println(index + ". " + key); + index++; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/adamgulczynski/Main.java b/src/main/java/org/adamgulczynski/Main.java new file mode 100644 index 0000000..db59259 --- /dev/null +++ b/src/main/java/org/adamgulczynski/Main.java @@ -0,0 +1,97 @@ +package org.adamgulczynski; + +import java.util.*; + +import io.github.cdimascio.dotenv.Dotenv; + +public class Main { + + public static void main(String[] args) { + + // get env variables + Dotenv dotenv = Dotenv.load(); + String basePath = dotenv.get("BASE_PATH"); + String apiKey = dotenv.get("API_KEY"); + + // read city details from json file + JsonReader readJSON = new JsonReader(); + Map> cityDataMap = readJSON.readFile(basePath + "data.json"); + + // print available cities + System.out.println("Dostepne miasta:"); + readJSON.printEntries(); + + DataFetcher dataFetcher = new DataFetcher(apiKey); + + Scanner scanner = new Scanner(System.in); + + List weatherDataList = new ArrayList<>(); + + while (true) { + System.out.print("P-Podaj miasto, Z-Zakoncz: "); + String userInput = scanner.nextLine(); + + if (Objects.equals(userInput, "P") || Objects.equals(userInput, "p")) { + String chosenCity = scanner.nextLine().toLowerCase(); + Map chosenCityData = (Map) cityDataMap.get(chosenCity); + if (cityDataMap.containsKey(chosenCity)) { + double latitude = (double) chosenCityData.get("latitude"); + double longitude = (double) chosenCityData.get("longitude"); + + String weatherDataString; + try { + weatherDataString = dataFetcher.fetchWeather(latitude, longitude); + } catch (IllegalArgumentException e) { + System.out.println("Niepoprawne dane w pliku json"); + continue; + } + + System.out.println(weatherDataString); + + // convert to WeatherData object + WeatherData weatherData = WeatherParser.parseWeatherToWeatherData(weatherDataString); + + // display the data + System.out.println(weatherData); + + // add data to a list, will be written to the file + weatherDataList.add(weatherData); + } else { + System.out.println("Nie ma takiego miasta."); + } + } else if (Objects.equals(userInput, "Z") || Objects.equals(userInput, "z")) { + label: + while (true) { + System.out.println("Do jakiego pliku chcesz zapisac dane? xml/json/pdf"); + String userChoice = scanner.nextLine().toLowerCase(); + + String path = basePath + "weather"; + + switch (userChoice) { + case "xml": + WeatherData.writeToXmlFile(path, weatherDataList); + break label; + case "json": + WeatherData.writeToJsonFile(path, weatherDataList); + break label; + case "pdf": + WeatherData.writeToPdfFile(path, weatherDataList); + break label; + default: + System.out.println("Bledny format."); + break; + } + + } + + break; + } else { + System.out.println("Bledna wartosc, podaj 'P' lub 'Z'"); + } + } + + scanner.close(); + + } + +} \ No newline at end of file diff --git a/src/main/java/org/adamgulczynski/WeatherData.java b/src/main/java/org/adamgulczynski/WeatherData.java new file mode 100644 index 0000000..e28be1e --- /dev/null +++ b/src/main/java/org/adamgulczynski/WeatherData.java @@ -0,0 +1,153 @@ +package org.adamgulczynski; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import java.io.*; + +import com.lowagie.text.*; +import com.lowagie.text.pdf.*; + +import java.util.List; + +public class WeatherData { + @JsonProperty("miasto") + private final String city; + + @JsonProperty("temperatura") + private final double temperature; + + @JsonProperty("wilgotnosc") + private final int humidity; + + @JsonProperty("wiatr") + private final double windSpeed; + + @JsonProperty("zachmurzenie") + private final int clouds; + + @JsonProperty("cisnienie") + private final int pressure; + + public WeatherData(String city, double temperature, int humidity, double windSpeed, int clouds, int pressure) { + this.city = city; + this.temperature = temperature; + this.humidity = humidity; + this.windSpeed = windSpeed; + this.clouds = clouds; + this.pressure = pressure; + } + + @Override + public String toString() { + return "Miasto: " + city + '\n' + + "Temperatura: " + temperature + " K\n" + + "Cisnienie: " + pressure + " hPa\n" + + "Wilgotnosc: " + humidity + "%\n" + + "Wiatr: " + windSpeed + " m/s\n" + + "Zachmurzenie: " + clouds + " %\n\n"; + } + + public String getCity() { + return city; + } + + public double getTemperature() { + return temperature; + } + + public int getHumidity() { + return humidity; + } + + public double getWindSpeed() { + return windSpeed; + } + + public int getClouds() { + return clouds; + } + + public int getPressure() { + return pressure; + } + + public static void writeToJsonFile(String filePath, List weatherDataList) { + ObjectMapper objectMapper = new ObjectMapper(); + + String path = filePath + ".json"; + try { + File file = new File(path); + ObjectWriter objectWriter = objectMapper.writer(new DefaultPrettyPrinter()); + objectWriter.writeValue(file, weatherDataList); + + System.out.println("Dane zapisane do: " + path); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void writeToXmlFile(String filePath, List weatherDataList) { + XmlMapper xmlMapper = new XmlMapper(); + + String path = filePath + ".xml"; + try { + File file = new File(path); + ObjectWriter objectWriter = xmlMapper.writerWithDefaultPrettyPrinter(); + objectWriter.writeValue(file, weatherDataList); + + System.out.println("Dane zapisane do: " + path); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void writeToTxtFile(String filePath, List weatherDataList) { + StringBuilder content = new StringBuilder(); + for (WeatherData data : weatherDataList) { + content.append(data.toString()); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath + ".txt"))) { + writer.write(content.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void writeToPdfFile(String filePath, List weatherDataList) { + // create .txt file + writeToTxtFile(filePath, weatherDataList); + + BufferedReader input = null; + Document output = null; + try { + input = new BufferedReader(new FileReader(filePath + ".txt")); + output = new Document(PageSize.LETTER, 40, 40, 40, 40); + + filePath = filePath + ".pdf"; + PdfWriter.getInstance(output, new FileOutputStream(filePath)); + + output.open(); + output.addAuthor("Adam Gulczynski"); + output.addSubject("Projekt"); + output.addTitle("Projekt"); + + String line = ""; + while (null != (line = input.readLine())) { + System.out.println(line); + Paragraph p = new Paragraph(line); + p.setAlignment(Element.ALIGN_JUSTIFIED); + output.add(p); + } + System.out.println("Dane zapisane do: " + filePath); + output.close(); + input.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/adamgulczynski/WeatherParser.java b/src/main/java/org/adamgulczynski/WeatherParser.java new file mode 100644 index 0000000..f05621e --- /dev/null +++ b/src/main/java/org/adamgulczynski/WeatherParser.java @@ -0,0 +1,20 @@ +package org.adamgulczynski; + +import org.json.JSONObject; + +public class WeatherParser { + + public static WeatherData parseWeatherToWeatherData(String response) { + + JSONObject jsonObject = new JSONObject(response); + + String city = jsonObject.getString("name"); + double temperature = jsonObject.getJSONObject("main").getDouble("temp"); + int humidity = jsonObject.getJSONObject("main").getInt("humidity"); + double windSpeed = jsonObject.getJSONObject("wind").getDouble("speed"); + int clouds = jsonObject.getJSONObject("clouds").getInt("all"); + int pressure = jsonObject.getJSONObject("main").getInt("pressure"); + + return new WeatherData(city, temperature, humidity, windSpeed, clouds, pressure); + } +} diff --git a/src/main/java/org/adamgulczynski/data.json b/src/main/java/org/adamgulczynski/data.json new file mode 100644 index 0000000..8e5a885 --- /dev/null +++ b/src/main/java/org/adamgulczynski/data.json @@ -0,0 +1,37 @@ +[ + { + "city": "warsaw", + "lat": 52.2297, + "lon": 21.0122 + }, + { + "city": "london", + "lat": 51.5072, + "lon": 0.1276 + }, + { + "city": "poznan", + "lat": 52.41, + "lon": 16.93 + }, + { + "city": "berlin", + "lat": 52.52, + "lon": 13.41 + }, + { + "city": "barcelona", + "lat": 41.39, + "lon": 2.17 + }, + { + "city": "paris", + "lat": 48.86, + "lon": 2.35 + }, + { + "city": "stockholm", + "lat": 59.33, + "lon": 18.07 + } +] diff --git a/src/test/java/WeatherDataTest.java b/src/test/java/WeatherDataTest.java new file mode 100644 index 0000000..129887c --- /dev/null +++ b/src/test/java/WeatherDataTest.java @@ -0,0 +1,110 @@ +import org.adamgulczynski.DataFetcher; +import org.adamgulczynski.WeatherData; +import org.adamgulczynski.WeatherParser; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +class WeatherDataTest { + + @Test + void testWeatherRequestWithGoodCoordinates() throws IOException { + String sampleBody = """ + { + "name": "london", + "main": { + "temp": 280.95, + "humidity": 79, + "pressure": 1038 + }, + "wind": { + "speed": 4.12 + }, + "clouds": { + "all": 100 + } + } + """; + + DataFetcher mockDataFetcher = mock(DataFetcher.class); + + double lat = 12.2; + double lon = 22.3; + when(mockDataFetcher.fetchWeather(lat, lon)) + .thenReturn(sampleBody); + + WeatherData weatherData = WeatherParser.parseWeatherToWeatherData(mockDataFetcher.fetchWeather(lat, lon)); + + assertEquals(weatherData.getTemperature(), 280.95); + assertEquals(weatherData.getHumidity(), 79); + assertEquals(weatherData.getWindSpeed(), 4.12); + assertEquals(weatherData.getPressure(), 1038); + assertEquals(weatherData.getClouds(), 100); + } + + @Test + void testWeatherRequestWithValidCoordinates() throws IOException { + String sampleBody = """ + { + "name": "london", + "main": { + "temp": 280.95, + "humidity": 79, + "pressure": 1038 + }, + "wind": { + "speed": 4.12 + }, + "clouds": { + "all": 100 + } + } + """; + + DataFetcher mockDataFetcher = mock(DataFetcher.class); + + double lat = 12.2; + double lon = 22.3; + when(mockDataFetcher.fetchWeather(lat, lon)) + .thenReturn(sampleBody); + + WeatherData weatherData = WeatherParser.parseWeatherToWeatherData(mockDataFetcher.fetchWeather(lat, lon)); + + assertEquals(weatherData.getTemperature(), 280.95); + assertEquals(weatherData.getHumidity(), 79); + assertEquals(weatherData.getWindSpeed(), 4.12); + assertEquals(weatherData.getPressure(), 1038); + assertEquals(weatherData.getClouds(), 100); + } + + @Test + void testWeatherRequestWithInvalidLatitude() { + DataFetcher mockDataFetcher = new DataFetcher("tes"); + + // Using invalid coordinates + double invalidLat = 500.0; + double validLon = 22.3; + + assertThrows(IllegalArgumentException.class, () -> { + mockDataFetcher.fetchWeather(invalidLat, validLon); + }); + } + + @Test + void testWeatherRequestWithInvalidLongitude() { + DataFetcher mockDataFetcher = new DataFetcher("tes"); + + // Using invalid coordinates + double validLat = 20.0; + double invalidLon = 522.3; + + assertThrows(IllegalArgumentException.class, () -> { + mockDataFetcher.fetchWeather(validLat, invalidLon); + }); + } +} \ No newline at end of file diff --git a/target/classes/org/adamgulczynski/DataFetcher.class b/target/classes/org/adamgulczynski/DataFetcher.class new file mode 100644 index 0000000000000000000000000000000000000000..eecb5745ce39f226a15adf5a080d72604ef10555 GIT binary patch literal 2466 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 literal 0 HcmV?d00001 diff --git a/target/classes/org/adamgulczynski/Main.class b/target/classes/org/adamgulczynski/Main.class new file mode 100644 index 0000000000000000000000000000000000000000..7a066c283e4aa8b60f0f483cf831ae30cc54c6dc GIT binary patch literal 4443 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 bK4+`XD)o7+`kX6RR!LPM7K)R^V$}Q(kfd`S literal 0 HcmV?d00001 diff --git a/target/classes/org/adamgulczynski/WeatherParser.class b/target/classes/org/adamgulczynski/WeatherParser.class new file mode 100644 index 0000000000000000000000000000000000000000..3ed0982d1f43cb489d1ff83a70e5789f48f57395 GIT binary patch literal 1203 zcmah}>rN9v6#l04GFz65i&el27Zq6w)C;0m#0x8?qynJ?!_R3uDT~|PW-l6iD*aOu zNqhhw#z!!o+16mB=x%mq&h49T&N;K+e}4T2;3eKT zk7O-mNUz$i9j-C-&6@ieaE1}jZxoIK&n;|LwzoQdS!9q^ApH!v+431oCG>5#Q8f2C z1{jjAXiDWfq+=MD7_^2AJ1z_}z2eS8af}d9Q`oKo%IUa*s|*7KbVi54>|MVI-W_s0Dkej}>IdnIkn+yJEc!~+6sl+yRW3rSc#&U|8@B2Y zqszaOw?)GE9VWpjls8h!)W8OG1|uXW_NwFjmhOy-ngv{Gh|pv=Z#q<%qa`+ zy3H#=JH_I`yqQalrgJ=}o#5VQ+9d(SY#N+KjoyB`wCa8gz{DWghOkbW9T+j|-Po^+ zRXmIR53q*k#CsUKctN@ZMQ+ef=N0-WegzX0og$QZSfopnG(q}xLQ#kdw9+X0b%Ym{7}Zzj$}lPnkMJmd(OS*`SqOpPj_aqz@T?uK6`uk^f(i%8CZl#;=Ul3ot85NJW{a?{Z9LOXb#I*(Xnda25vG` z_qpN+Lb(GGzZMDAsf>9bz3!Whn$zT|^{$mgf^;c=ANYH#Dgs8a+IXKI%%oq7mhuKv4q2ChctUjC|3$ z0Q;Dyx5#E`K*wFO#^eCDu|qqCdz97k%O@!0B8|_`9c$N||AMN9b1YfkP(QXlVd(<5 s&#^wOYtjv1p6mi@sA7X`rm;GybE#3l8d_+RmPNUYWj9y8PgcW&UplYFn*aa+ literal 0 HcmV?d00001 diff --git a/target/test-classes/WeatherDataTest.class b/target/test-classes/WeatherDataTest.class new file mode 100644 index 0000000000000000000000000000000000000000..03cb0e0efd14f6f931787f21eafbfcb1b5ffd7cc GIT binary patch literal 3834 zcmdT{SyLQU6#g29X=vJHLP!j*h$Mj|GLT?Qg2qXJkcpEJ0l{6HnafN=Pxqvk5Q1^v z_kH;XmJe3>5>cxxpDq0n{s*5ezuVK9X>bzbgJn5Ybf0_fxo7##(wCqA`2I%#$MA)Q zCN!%^=x9MwL2A)BZ=@~5E~Y2v7e(Gzki6HlP5-EZ=Dz;fRx!gv3vGOUfD2h^5i~Oz9?6?+J9(p;PBmYpS)5~HVdY|LjFC6BVNT7o)@@M+tA>F zLlnFeYFjq#LS36z5kfA92Zr|7MDmss6ui2SVOgP;K`EhK(o|>)ZgbpX+9(+1VqoPj zuGrp^nI1EIQrErh>h=3wp@{(jZahV43T#0~}eg0wp)44=joZ0{Qzi|d&7UDGa(NUv_v zaWihA@#T`R6hk4*LF;_iBWFh9@J=}Eij@I4)^N7`{4ZdcK*;|{5?_j(Q~(CDE( zCH%m(HQb3_6+3n8!fplK4IqsoI%~L|jH|06iHJGlM1;g$!Eij1DBx-&8`2azMMV0s zN5x(p`*2s|tj5F@Y$^(Wh9xLmgAs_ZfX9-VbMGCHKMh0Jui}7?VcZ=Bv>33`(~CjH z^o5%?DrWka$5?#Ru|4LE+2&&WTwqvSw#dBAN~iA8aR`SMwA0|psJ=sMS!rZMTDVur zXR}rLqdM-F5$KekV~tPC%1&ld7k``hx^q=(9Mv&)MdR71J0$;vjtR+cNa=ur85L}z&9Q2q z5gtc%V$>-x=FK_N7H5L;yl`iv76lzSCvRA@hHJ`o#7y`llWuRxZO}8qF~=175qnCU z3*^f5OB0S$7pr2bq&rBzsoviI}LQMs>Z6wJ(m~A`05jFcQ zS4o`&ng(_?258o>%=HLe1<9;^UW(>u&JSeHG_%H0&|2M;pQ}9wqZcgGg4Nv2B9WUTnuHKZZw{%ir+spCQFY%s^4pr@+H0**UyxyGSECrR3 zzt(@^4e$PVJXMDnB*L!xIBkRLc z@fkn3*B0eXv5Ez{nS`crY^i+mUdfR)Pb$r~A~}1$h4KxFCgk~)+rSMd@Tpe?Ah5s{ zP~>iNc=&fJY9*)Cq2@Z;*3kYF+D><3%c)LmJ>0Ude@Dw@Y+FOu-tTa06Xt4+o+E0P zdKrD47+AxgBoC%0*KkBq_jMu@F^_LaY>K$!sRF6Ge+TIsu!Vl~(7#@!uoK&O4eG{j zKKrpBdvPD{Bonv`Qy9P#477;BFrK6IpFT(NH1l18$ryD!jzugHffOEwg)+5o!!&GC zE!21r4l4Zgo#W{Qpd+hOKnEht(`ga}N_ibx|rcbcG4zHIqSKVA`4}ox%#K#D-*DytY%1 z(WxiuPCeDwsRErU(pZUR7U@*^Z=G7LIrYp&r&5|6kr7g-7_