Project report
weather_app/pom.xml
4.0.0 org.springframework.boot spring-boot-starter-parent 3.3.1 com.service weather 0.0.1-SNAPSHOT weather Weather website 17 org.springframework.boot spring-boot-starter-web com.h2database h2 runtime org.springframework.boot spring-boot-starter-test test jakarta.persistence jakarta.persistence-api 3.1.0 org.mockito mockito-core test org.mockito mockito-junit-jupiter test org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-maven-plugin
weather_app/src/main/java/com/service/weather/controller/WeatherController.java
weather_app/src/main/java/com/service/weather/controller/WeatherController.java
package
com
.
service
.
weather
.
controller
;
import
com
.
service
.
weather
.
model
.
WeatherRecord
;
import
com
.
service
.
weather
.
service
.
WeatherService
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
*
;
/**
* The WeatherController class handles HTTP requests related to weather data.
* It provides an endpoint to fetch weather information based on a given zip code.
*/
@
RestController
@
RequestMapping
(
"/api/weather"
)
public
class
WeatherController
{
@
Autowired
private
WeatherService
weatherService
;
@
GetMapping
public
WeatherRecord
getWeatherByZipCode
(
@
RequestParam
String
zipCode
)
{
return
weatherService
.
getWeatherByZipCode
(
zipCode
);
}
}
weather_app/src/main/java/com/service/weather/controller/ZipCodeController.java
weather_app/src/main/java/com/service/weather/controller/ZipCodeController.java
package
com
.
service
.
weather
.
controller
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
com
.
service
.
weather
.
service
.
ZipCodeService
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
*
;
import
java
.
util
.
List
;
/**
* The ZipCodeController class handles HTTP requests related to zip codes.
* It provides endpoints to fetch all zip codes, add a new zip code, and remove an existing zip code.
*/
@
RestController
@
RequestMapping
(
"/api/zipcodes"
)
public
class
ZipCodeController
{
@
Autowired
private
ZipCodeService
zipCodeService
;
@
GetMapping
public
List
<
ZipCode
>
getAllZipCodes
()
{
return
zipCodeService
.
getAllZipCodes
();
}
@
PostMapping
public
ZipCode
addZipCode
(
@
RequestBody
ZipCode
zipCode
)
{
return
zipCodeService
.
addZipCode
(
zipCode
);
}
@
DeleteMapping
(
"/{id}"
)
public
void
removeZipCode
(
@
PathVariable
Long
id
)
{
zipCodeService
.
removeZipCode
(
id
);
}
}
weather_app/src/main/java/com/service/weather/model/OpenWeatherMapResponse.java
weather_app/src/main/java/com/service/weather/model/OpenWeatherMapResponse.java
package
com
.
service
.
weather
.
model
;
import
com
.
fasterxml
.
jackson
.
annotation
.
JsonIgnoreProperties
;
import
com
.
fasterxml
.
jackson
.
annotation
.
JsonProperty
;
import
java
.
util
.
List
;
/**
* The OpenWeatherMapResponse class represents the response structure from the OpenWeatherMap API.
*/
@
JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
class
OpenWeatherMapResponse
{
private
Main
main
;
private
Wind
wind
;
private
List
<
Weather
>
weather
;
private
String
name
;
public
Main
getMain
()
{
return
main
;
}
public
void
setMain
(
Main
main
)
{
this
.
main
=
main
;
}
public
Wind
getWind
()
{
return
wind
;
}
public
void
setWind
(
Wind
wind
)
{
this
.
wind
=
wind
;
}
public
List
<
Weather
>
getWeather
()
{
return
weather
;
}
public
void
setWeather
(
List
<
Weather
>
weather
)
{
this
.
weather
=
weather
;
}
public
String
getName
()
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
@
JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
static
class
Main
{
private
double
temp
;
@
JsonProperty
(
"feels_like"
)
private
double
feels_like
;
private
double
temp_min
;
private
double
temp_max
;
private
int
humidity
;
public
double
getTemp
()
{
return
temp
;
}
public
void
setTemp
(
double
temp
)
{
this
.
temp
=
temp
;
}
public
double
getFeels_like
()
{
return
feels_like
;
}
public
void
setFeels_like
(
double
feelsLike
)
{
this
.
feels_like
=
feelsLike
;
}
public
double
getTemp_min
()
{
return
temp_min
;
}
public
void
setTemp_min
(
double
temp_min
)
{
this
.
temp_min
=
temp_min
;
}
public
double
getTemp_max
()
{
return
temp_max
;
}
public
void
setTemp_max
(
double
temp_max
)
{
this
.
temp_max
=
temp_max
;
}
public
int
getHumidity
()
{
return
humidity
;
}
public
void
setHumidity
(
int
humidity
)
{
this
.
humidity
=
humidity
;
}
}
@
JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
static
class
Wind
{
private
double
speed
;
// Getters and setters
public
double
getSpeed
()
{
return
speed
;
}
public
void
setSpeed
(
double
speed
)
{
this
.
speed
=
speed
;
}
}
@
JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
static
class
Weather
{
private
int
id
;
private
String
icon
;
// Getters and setters
public
int
getId
()
{
return
id
;
}
public
void
setId
(
int
id
)
{
this
.
id
=
id
;
}
public
String
getIcon
()
{
return
icon
;
}
public
void
setIcon
(
String
icon
)
{
this
.
icon
=
icon
;
}
}
}
weather_app/src/main/java/com/service/weather/model/WeatherRecord.java
weather_app/src/main/java/com/service/weather/model/WeatherRecord.java
package
com
.
service
.
weather
.
model
;
import
jakarta
.
persistence
.
Entity
;
import
jakarta
.
persistence
.
GeneratedValue
;
import
jakarta
.
persistence
.
GenerationType
;
import
jakarta
.
persistence
.
Id
;
/**
* The WeatherRecord class represents a record of weather data.
*/
@
Entity
public
class
WeatherRecord
{
@
Id
@
GeneratedValue
(
strategy
=
GenerationType
.
AUTO
)
private
Long
id
;
private
String
zipCode
;
private
double
temperature
;
private
double
feelsLike
;
private
double
tempMin
;
private
double
tempMax
;
private
int
humidity
;
private
double
windSpeed
;
private
String
city
;
private
String
icon
;
private
int
conditionCode
;
public
WeatherRecord
()
{}
public
WeatherRecord
(
String
zipCode
,
double
temperature
,
double
feelsLike
,
double
tempMin
,
double
tempMax
,
int
humidity
,
double
windSpeed
,
String
city
,
String
icon
,
int
conditionCode
)
{
this
.
zipCode
=
zipCode
;
this
.
temperature
=
temperature
;
this
.
feelsLike
=
feelsLike
;
this
.
tempMin
=
tempMin
;
this
.
tempMax
=
tempMax
;
this
.
humidity
=
humidity
;
this
.
windSpeed
=
windSpeed
;
this
.
city
=
city
;
this
.
icon
=
icon
;
this
.
conditionCode
=
conditionCode
;
}
public
Long
getId
()
{
return
id
;
}
public
void
setId
(
Long
id
)
{
this
.
id
=
id
;
}
public
String
getZipCode
()
{
return
zipCode
;
}
public
void
setZipCode
(
String
zipCode
)
{
this
.
zipCode
=
zipCode
;
}
public
double
getTemperature
()
{
return
temperature
;
}
public
void
setTemperature
(
double
temperature
)
{
this
.
temperature
=
temperature
;
}
public
double
getFeelsLike
()
{
return
feelsLike
;
}
public
void
setFeelsLike
(
double
feelsLike
)
{
this
.
feelsLike
=
feelsLike
;
}
public
double
getTempMin
()
{
return
tempMin
;
}
public
void
setTempMin
(
double
tempMin
)
{
this
.
tempMin
=
tempMin
;
}
public
double
getTempMax
()
{
return
tempMax
;
}
public
void
setTempMax
(
double
tempMax
)
{
this
.
tempMax
=
tempMax
;
}
public
int
getHumidity
()
{
return
humidity
;
}
public
void
setHumidity
(
int
humidity
)
{
this
.
humidity
=
humidity
;
}
public
double
getWindSpeed
()
{
return
windSpeed
;
}
public
void
setWindSpeed
(
double
windSpeed
)
{
this
.
windSpeed
=
windSpeed
;
}
public
String
getCity
()
{
return
city
;
}
public
void
setCity
(
String
city
)
{
this
.
city
=
city
;
}
public
String
getIcon
()
{
return
icon
;
}
public
void
setIcon
(
String
icon
)
{
this
.
icon
=
icon
;
}
public
int
getConditionCode
()
{
return
conditionCode
;
}
public
void
setConditionCode
(
int
conditionCode
)
{
this
.
conditionCode
=
conditionCode
;
}
}
weather_app/src/main/java/com/service/weather/model/ZipCode.java
weather_app/src/main/java/com/service/weather/model/ZipCode.java
package
com
.
service
.
weather
.
model
;
import
jakarta
.
persistence
.
Entity
;
import
jakarta
.
persistence
.
GeneratedValue
;
import
jakarta
.
persistence
.
GenerationType
;
import
jakarta
.
persistence
.
Id
;
/**
* The ZipCode class represents a record of Zipcode data.
*/
@
Entity
public
class
ZipCode
{
@
Id
@
GeneratedValue
(
strategy
=
GenerationType
.
AUTO
)
private
Long
id
;
private
String
city
;
private
String
zipCode
;
public
ZipCode
()
{
}
public
ZipCode
(
Long
id
,
String
city
,
String
zipCode
)
{
this
.
id
=
id
;
this
.
city
=
city
;
this
.
zipCode
=
zipCode
;
}
public
Long
getId
()
{
return
id
;
}
public
void
setId
(
Long
id
)
{
this
.
id
=
id
;
}
public
String
getCity
()
{
return
city
;
}
public
void
setCity
(
String
city
)
{
this
.
city
=
city
;
}
public
String
getZipCode
()
{
return
zipCode
;
}
public
void
setZipCode
(
String
zipCode
)
{
this
.
zipCode
=
zipCode
;
}
}
weather_app/src/main/java/com/service/weather/repository/ZipCodeRepository.java
weather_app/src/main/java/com/service/weather/repository/ZipCodeRepository.java
package
com
.
service
.
weather
.
repository
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
org
.
springframework
.
data
.
jpa
.
repository
.
JpaRepository
;
import
org
.
springframework
.
stereotype
.
Repository
;
@
Repository
public
interface
ZipCodeRepository
extends
JpaRepository
<
ZipCode
,
Long
>
{
}
weather_app/src/main/java/com/service/weather/service/DataInitializer.java
weather_app/src/main/java/com/service/weather/service/DataInitializer.java
package
com
.
service
.
weather
.
service
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
com
.
service
.
weather
.
repository
.
ZipCodeRepository
;
import
jakarta
.
annotation
.
PostConstruct
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
stereotype
.
Service
;
/**
* The DataInitializer class is responsible for initializing the database with some default zip codes.
* It checks if the database is empty and, if so, adds a predefined set of zip codes.
*/
@
Service
public
class
DataInitializer
{
@
Autowired
private
ZipCodeRepository
zipCodeRepository
;
@
PostConstruct
public
void
init
()
{
// Check if zip codes are already in the database
if
(
zipCodeRepository
.
count
()
==
0
)
{
// Add initial zip codes
zipCodeRepository
.
save
(
new
ZipCode
(
null
,
"New York"
,
"10001"
));
zipCodeRepository
.
save
(
new
ZipCode
(
null
,
"San Francisco"
,
"94114"
));
zipCodeRepository
.
save
(
new
ZipCode
(
null
,
"Plano"
,
"75075"
));
}
}
}
weather_app/src/main/java/com/service/weather/service/WeatherService.java
weather_app/src/main/java/com/service/weather/service/WeatherService.java
package
com
.
service
.
weather
.
service
;
import
com
.
service
.
weather
.
model
.
OpenWeatherMapResponse
;
import
com
.
service
.
weather
.
model
.
WeatherRecord
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Value
;
import
org
.
springframework
.
stereotype
.
Service
;
import
org
.
springframework
.
web
.
client
.
RestTemplate
;
/**
* The WeatherService class is responsible for fetching and processing weather data from the OpenWeatherMap API.
* It retrieves weather information based on a provided zip code and converts the response into a WeatherRecord object.
*/
@
Service
public
class
WeatherService
{
@
Autowired
private
RestTemplate
restTemplate
;
@
Value
(
"${openweathermap.api.key}"
)
private
String
apiKey
;
private
final
String
BASE_URL
=
"http://api.openweathermap.org/data/2.5/weather"
;
private
final
String
ICON_URL
=
"https://openweathermap.org/img/wn/"
;
/**
* Fetches weather data for a given zip code from the OpenWeatherMap API.
*
*
@param
zipCode the zip code for which to fetch the weather
*
@return
a WeatherRecord object containing the fetched weather data
*/
public
WeatherRecord
getWeatherByZipCode
(
String
zipCode
)
{
String
url
=
String
.
format
(
"%s?zip=%s,us&units=imperial&appid=%s"
,
BASE_URL
,
zipCode
,
apiKey
);
OpenWeatherMapResponse
response
=
restTemplate
.
getForObject
(
url
,
OpenWeatherMapResponse
.
class
);
if
(
response
!=
null
)
{
WeatherRecord
record
=
new
WeatherRecord
();
record
.
setZipCode
(
zipCode
);
record
.
setTemperature
(
response
.
getMain
().
getTemp
());
record
.
setFeelsLike
(
response
.
getMain
().
getFeels_like
());
record
.
setTempMin
(
response
.
getMain
().
getTemp_min
());
record
.
setTempMax
(
response
.
getMain
().
getTemp_max
());
record
.
setHumidity
(
response
.
getMain
().
getHumidity
());
record
.
setWindSpeed
(
response
.
getWind
().
getSpeed
());
record
.
setCity
(
response
.
getName
());
String
iconCode
=
response
.
getWeather
().
get
(
0
).
getIcon
();
String
iconUrl
=
String
.
format
(
"%s%[email protected]"
,
ICON_URL
,
iconCode
);
record
.
setIcon
(
iconUrl
);
record
.
setConditionCode
(
response
.
getWeather
().
get
(
0
).
getId
());
return
record
;
}
else
{
throw
new
RuntimeException
(
"Unable to fetch weather data"
);
}
}
}
weather_app/src/main/java/com/service/weather/service/ZipCodeService.java
weather_app/src/main/java/com/service/weather/service/ZipCodeService.java
package
com
.
service
.
weather
.
service
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
com
.
service
.
weather
.
repository
.
ZipCodeRepository
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
stereotype
.
Service
;
import
java
.
util
.
List
;
/**
* The ZipCodeService class provides business logic for handling zip code operations.
* It interacts with the ZipCodeRepository to perform CRUD operations on zip code data.
*/
@
Service
public
class
ZipCodeService
{
@
Autowired
private
ZipCodeRepository
zipCodeRepository
;
public
List
<
ZipCode
>
getAllZipCodes
()
{
return
zipCodeRepository
.
findAll
();
}
public
ZipCode
addZipCode
(
ZipCode
zipCode
)
{
return
zipCodeRepository
.
save
(
zipCode
);
}
public
void
removeZipCode
(
Long
id
)
{
zipCodeRepository
.
deleteById
(
id
);
}
}
weather_app/src/main/java/com/service/weather/WeatherApplication.java
weather_app/src/main/java/com/service/weather/WeatherApplication.java
package
com
.
service
.
weather
;
import
org
.
springframework
.
boot
.
SpringApplication
;
import
org
.
springframework
.
boot
.
autoconfigure
.
SpringBootApplication
;
import
org
.
springframework
.
context
.
annotation
.
Bean
;
import
org
.
springframework
.
context
.
annotation
.
ComponentScan
;
import
org
.
springframework
.
web
.
client
.
RestTemplate
;
@
SpringBootApplication
@
ComponentScan
(
basePackages
=
"com.service.weather"
)
public
class
WeatherApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
WeatherApplication
.
class
,
args
);
}
@
Bean
public
RestTemplate
restTemplate
()
{
return
new
RestTemplate
();
}
}
weather_app/src/main/resources/application.properties
server..port=8090 spring.application.name=weather # H2 Database Configuration spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.jpa.database-platform=org.hibernate.dialect.H2Dialect # JPA Configuration spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true openweathermap.api.key=5f40dfe3704a02e2e7a64d58dcefc566
weather_app/src/main/resources/static/css/styles.css
body { font-family: 'Arial', sans-serif; margin: 0; padding: 0; display: flex; flex-direction: column; align-items: center; background: linear-gradient(to right, #6a11cb, #2575fc); color: #fff; height: 100vh; justify-content: space-between; } .container { width: 90%; max-width: 600px; padding: 20px; background: rgba(255, 255, 255, 0.1); border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } header { text-align: center; margin-bottom: 20px; } h1 { font-size: 2.5em; margin: 0; color: #fff; } h2 { margin-bottom: 10px; } form { display: flex; justify-content: center; margin-bottom: 20px; } input { padding: 10px; font-size: 16px; border: none; border-radius: 5px; margin-right: 10px; } button { padding: 10px 20px; font-size: 16px; cursor: pointer; border: none; border-radius: 5px; background-color: #2575fc; color: #fff; transition: background-color 0.3s ease; } button:hover { background-color: #6a11cb; } #weatherResult, #savedWeatherResults { font-size: 18px; color: #fff; } #zipCodeList { list-style: none; padding: 0; width: 100%; } #zipCodeList li { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; background: rgba(255, 255, 255, 0.2); padding: 10px; border-radius: 5px; } #zipCodeList li span { flex: 1; } .weather-container { display: flex; border: 2px solid #ccc; border-radius: 10px; padding: 20px; max-width: 800px; margin: 20px auto; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); background: rgba(255, 255, 255, 0.1); } .weather-header, .weather-details { flex: 1; margin: 10px; } .weather-header { display: flex; flex-direction: column; align-items: center; justify-content: center; border-right: 2px solid #ccc; padding-right: 20px; } .city-name { font-size: 1.5em; font-weight: bold; } .weather-icon { width: 50px; height: 50px; } .temperature { font-size: 2em; color: #f39c12; } .weather-details { background-color: #f2f2f2; padding: 10px; border-radius: 10px; display: flex; flex-direction: column; justify-content: center; color: #000; } .weather-details p { margin: 5px 0; } #zipCodeList li button { margin-left: 10px; padding: 5px 10px; border: none; border-radius: 5px; background-color: #ff4e50; color: #fff; cursor: pointer; transition: background-color 0.3s ease; } #zipCodeList li button:hover { background-color: #fc913a; } .scroll-container { display: flex; overflow-x: auto; padding: 10px 0; } #popularZipCodeList { display: flex; flex-direction: row; list-style: none; padding: 0; margin: 0; } #popularZipCodeList li { display: flex; flex-direction: column; align-items: center; justify-content: center; margin-right: 10px; background: rgba(255, 255, 255, 0.2); padding: 10px; border-radius: 5px; color: #fff; min-width: 100px; border: 1px solid #fff; } #popularZipCodeList li span { margin-bottom: 5px; } #popularZipCodeList li button { margin-top: 5px; padding: 5px 10px; border: none; border-radius: 5px; background-color: #ff4e50; color: #fff; cursor: pointer; transition: background-color 0.3s ease; } #popularZipCodeList li button:hover { background-color: #fc913a; }
weather_app/src/main/resources/static/index.html
Weather App
Search Weather by Zip Code
Get WeatherAdd Zip Code to List
Add to ListSaved Zip Codes
Popular Weather Locations
© 2024 Weather App. All rights reserved.
weather_app/src/main/resources/static/js/script.js
document.addEventListener('DOMContentLoaded', function() { const weatherForm = document.getElementById('weatherForm'); const addZipForm = document.getElementById('addZipForm'); const zipCodeInput = document.getElementById('zipCode'); const addZipCodeInput = document.getElementById('addZipCode'); const weatherResult = document.getElementById('weatherResult'); const zipCodeList = document.getElementById('zipCodeList'); const savedWeatherResults = document.getElementById('savedWeatherResults'); const popularZipCodeList = document.getElementById('popularZipCodeList'); // Load stored zip codes and display weather for each loadStoredZipCodes(); // Load popular zip codes from the database loadZipCodesFromDB(); // Add event listener to the weather form for fetching weather data weatherForm.addEventListener('submit', function(event) { event.preventDefault(); const zipCode = zipCodeInput.value; fetchWeather(zipCode, weatherResult); }); // Add event listener to the add zip code form for adding a new zip code to the list addZipForm.addEventListener('submit', function(event) { event.preventDefault(); const zipCode = addZipCode.value; addZipCodeToList(zipCode); }); // Function to fetch weather data for a given zip code and display it function fetchWeather(zipCode, resultElement) { fetch(`/api/weather?zipCode=${zipCode}`) .then(response => { if (response.status === 500) { throw new Error('Invalid zip code'); } return response.json(); }) .then(data => { // Check for severe weather conditions if (isSevereWeather(data.conditionCode)) { alert(`Severe weather condition detected in ${data.city} (ZIP: ${zipCode}): ${getDescriptionForConditionCode(data.conditionCode)}`); } const weatherIcon = data.icon; const weatherInfo = ` <div id="weatherResult" class="weather-container"> <div class="weather-header"> <div class="city-name">${data.city}</div> <img src="${weatherIcon}" alt="Weather Icon" class="weather-icon" onerror="this.onerror=null; this.src='/images/default.png';"> <div class="temperature">${data.temperature} °F</div> </div> <div class="weather-details"> <p>Feels Like: ${data.feelsLike} °F</p> <p>Min Temp: ${data.tempMin} °F</p> <p>Max Temp: ${data.tempMax} °F</p> <p>Humidity: ${data.humidity} %</p> <p>Wind Speed: ${data.windSpeed} mph</p> </div> </div> `; resultElement.innerHTML = weatherInfo; }) .catch(error => { console.error('Error fetching weather data:', error); if (error.message === 'Invalid zip code') { alert('The provided zip code does not exist. Please enter a valid zip code.'); resultElement.innerHTML = 'Error: Invalid zip code'; } else { resultElement.innerHTML = 'Error fetching weather data'; } }); } // Function to check if the weather condition is severe based on the condition code function isSevereWeather(conditionCode) { const severeConditions = [200, 201, 202, 210, 211, 212, 221, 230, 231, 232, 502, 503, 504, 511, 522, 531, 601, 602, 622, 711, 731, 741, 751, 761, 762, 771, 781]; return severeConditions.includes(conditionCode); } // Function to get the description for a specific weather condition code function getDescriptionForConditionCode(conditionCode) { const conditionDescriptions = { 200: 'thunderstorm with light rain', 201: 'thunderstorm with rain', 202: 'thunderstorm with heavy rain', 210: 'light thunderstorm', 211: 'thunderstorm', 212: 'heavy thunderstorm', 221: 'ragged thunderstorm', 230: 'thunderstorm with light drizzle', 231: 'thunderstorm with drizzle', 232: 'thunderstorm with heavy drizzle', 502: 'heavy intensity rain', 503: 'very heavy rain', 504: 'extreme rain', 511: 'freezing rain', 522: 'heavy intensity shower rain', 531: 'ragged shower rain', 601: 'snow', 602: 'heavy snow', 622: 'heavy shower snow', 711: 'smoke', 731: 'sand/dust whirls', 741: 'fog', 751: 'sand', 761: 'dust', 762: 'volcanic ash', 771: 'squalls', 781: 'tornado' }; return conditionDescriptions[conditionCode] || 'Unknown severe condition'; } // Function to add a new zip code to the list and fetch its weather data function addZipCodeToList(zipCode) { const tempResultElement = document.createElement('div'); fetch(`/api/weather?zipCode=${zipCode}`) .then(response => { if (response.status === 500) { throw new Error('Invalid zip code'); } return response.json(); }) .then(data => { let storedZipCodes = JSON.parse(localStorage.getItem('zipCodes')) || []; if (!storedZipCodes.includes(zipCode)) { storedZipCodes.push(zipCode); localStorage.setItem('zipCodes', JSON.stringify(storedZipCodes)); displayStoredZipCodes(storedZipCodes); fetchWeatherForSavedZipCode(zipCode); } }) .catch(error => { console.error('Error fetching weather data:', error); if (error.message === 'Invalid zip code') { alert('The provided zip code does not exist. Please enter a valid zip code.'); } }); } // Function to load stored zip codes from localStorage and display their weather data function loadStoredZipCodes() { const storedZipCodes = JSON.parse(localStorage.getItem('zipCodes')) || []; displayStoredZipCodes(storedZipCodes); storedZipCodes.forEach(zipCode => fetchWeatherForSavedZipCode(zipCode)); } // Function to load popular zip codes from the database and display their weather data function loadZipCodesFromDB() { fetch('/api/zipcodes') .then(response => response.json()) .then(data => { const zipCodes = data.map(item => item.zipCode); displayPopularZipCodes(zipCodes); zipCodes.forEach(zipCode => fetchWeatherForPopularZipCode(zipCode)); }) .catch(error => console.error('Error fetching zip codes from DB:', error)); } // Function to display stored zip codes in the UI function displayStoredZipCodes(zipCodes) { zipCodeList.innerHTML = ''; zipCodes.forEach(zipCode => { const listItem = document.createElement('li'); const zipCodeText = document.createElement('span'); zipCodeText.textContent = zipCode; const resultElement = document.createElement('div'); resultElement.id = `weatherResult-${zipCode}`; resultElement.classList.add('weather-info'); const deleteButton = document.createElement('button'); deleteButton.textContent = 'Delete from list'; deleteButton.addEventListener('click', () => removeZipCode(zipCode)); listItem.appendChild(zipCodeText); listItem.appendChild(resultElement); listItem.appendChild(deleteButton); zipCodeList.appendChild(listItem); }); } // Function to display popular zip codes in the UI function displayPopularZipCodes(zipCodes) { popularZipCodeList.innerHTML = ''; zipCodes.forEach(zipCode => { const listItem = document.createElement('li'); const zipCodeText = document.createElement('span'); zipCodeText.textContent = zipCode; const resultElement = document.createElement('div'); resultElement.id = `popularWeatherResult-${zipCode}`; resultElement.classList.add('weather-info'); listItem.appendChild(zipCodeText); listItem.appendChild(resultElement); popularZipCodeList.appendChild(listItem); }); } // Function to fetch weather data for a popular zip code and display it function fetchWeatherForPopularZipCode(zipCode) { const resultElement = document.getElementById(`popularWeatherResult-${zipCode}`); if (resultElement) { fetchWeather(zipCode, resultElement); } } // Function to fetch weather data for a saved zip code and display it function fetchWeatherForSavedZipCode(zipCode) { const resultElement = document.getElementById(`weatherResult-${zipCode}`); if (resultElement) { fetchWeather(zipCode, resultElement); } } // Function to remove a zip code from the list function removeZipCode(zipCode) { let storedZipCodes = JSON.parse(localStorage.getItem('zipCodes')) || []; storedZipCodes = storedZipCodes.filter(storedZipCode => storedZipCode !== zipCode); localStorage.setItem('zipCodes', JSON.stringify(storedZipCodes)); displayStoredZipCodes(storedZipCodes); } });
weather_app/src/test/java/com/service/weather/DataInitializerTest.java
weather_app/src/test/java/com/service/weather/DataInitializerTest.java
package
com
.
service
.
weather
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
com
.
service
.
weather
.
repository
.
ZipCodeRepository
;
import
com
.
service
.
weather
.
service
.
DataInitializer
;
import
org
.
junit
.
jupiter
.
api
.
BeforeEach
;
import
org
.
junit
.
jupiter
.
api
.
Test
;
import
org
.
junit
.
jupiter
.
api
.
extension
.
ExtendWith
;
import
org
.
mockito
.
InjectMocks
;
import
org
.
mockito
.
Mock
;
import
org
.
mockito
.
junit
.
jupiter
.
MockitoExtension
;
import
static
org
.
mockito
.
Mockito
.
*
;
/**
* The DataInitializerTest class tests the DataInitializer class to ensure that it initializes the database correctly.
* It uses Mockito to mock the ZipCodeRepository and verifies interactions with it.
*/
@
ExtendWith
(
MockitoExtension
.
class
)
public
class
DataInitializerTest
{
@
Mock
private
ZipCodeRepository
zipCodeRepository
;
@
InjectMocks
private
DataInitializer
dataInitializer
;
@
BeforeEach
public
void
setUp
()
{
// Ensure the mock repository is empty before each test
when
(
zipCodeRepository
.
count
()).
thenReturn
(
0L
);
}
@
Test
public
void
testInit
()
{
when
(
zipCodeRepository
.
count
()).
thenReturn
(
0L
);
dataInitializer
.
init
();
verify
(
zipCodeRepository
,
times
(
1
)).
count
();
verify
(
zipCodeRepository
,
times
(
1
)).
save
(
argThat
(
zipCode
->
zipCode
.
getCity
().
equals
(
"New York"
)
&&
zipCode
.
getZipCode
().
equals
(
"10001"
)
));
verify
(
zipCodeRepository
,
times
(
1
)).
save
(
argThat
(
zipCode
->
zipCode
.
getCity
().
equals
(
"San Francisco"
)
&&
zipCode
.
getZipCode
().
equals
(
"94114"
)
));
verify
(
zipCodeRepository
,
times
(
1
)).
save
(
argThat
(
zipCode
->
zipCode
.
getCity
().
equals
(
"Plano"
)
&&
zipCode
.
getZipCode
().
equals
(
"75075"
)
));
}
@
Test
public
void
testInitWithExistingData
()
{
// Simulate existing data in the repository
when
(
zipCodeRepository
.
count
()).
thenReturn
(
3L
);
dataInitializer
.
init
();
verify
(
zipCodeRepository
,
never
()).
save
(
any
(
ZipCode
.
class
));
}
}
weather_app/src/test/java/com/service/weather/WeatherApplicationTests.java
weather_app/src/test/java/com/service/weather/WeatherApplicationTests.java
package
com
.
service
.
weather
;
import
com
.
service
.
weather
.
model
.
OpenWeatherMapResponse
;
import
com
.
service
.
weather
.
model
.
WeatherRecord
;
import
com
.
service
.
weather
.
service
.
WeatherService
;
import
org
.
junit
.
jupiter
.
api
.
BeforeEach
;
import
org
.
junit
.
jupiter
.
api
.
Test
;
import
org
.
junit
.
jupiter
.
api
.
extension
.
ExtendWith
;
import
org
.
mockito
.
InjectMocks
;
import
org
.
mockito
.
Mock
;
import
org
.
mockito
.
junit
.
jupiter
.
MockitoExtension
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Value
;
import
org
.
springframework
.
web
.
client
.
RestTemplate
;
import
java
.
util
.
Arrays
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
assertEquals
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
assertNotNull
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyString
;
import
static
org
.
mockito
.
Mockito
.
when
;
@
ExtendWith
(
MockitoExtension
.
class
)
class
WeatherApplicationTests
{
@
Mock
private
RestTemplate
restTemplate
;
@
InjectMocks
private
WeatherService
weatherService
;
@
Value
(
"${openweathermap.api.key}"
)
private
String
apiKey
;
private
final
String
zipCode
=
"90210"
;
private
OpenWeatherMapResponse
mockResponse
;
@
BeforeEach
public
void
setUp
()
{
mockResponse
=
new
OpenWeatherMapResponse
();
OpenWeatherMapResponse
.
Main
main
=
new
OpenWeatherMapResponse
.
Main
();
main
.
setTemp
(
72.0
);
main
.
setFeels_like
(
70.0
);
main
.
setTemp_min
(
68.0
);
main
.
setTemp_max
(
75.0
);
main
.
setHumidity
(
50
);
OpenWeatherMapResponse
.
Wind
wind
=
new
OpenWeatherMapResponse
.
Wind
();
wind
.
setSpeed
(
5.0
);
OpenWeatherMapResponse
.
Weather
weather
=
new
OpenWeatherMapResponse
.
Weather
();
weather
.
setIcon
(
"10d"
);
weather
.
setId
(
800
);
mockResponse
.
setMain
(
main
);
mockResponse
.
setWind
(
wind
);
mockResponse
.
setWeather
(
Arrays
.
asList
(
weather
));
mockResponse
.
setName
(
"New York"
);
when
(
restTemplate
.
getForObject
(
anyString
(),
any
())).
thenReturn
(
mockResponse
);
}
@
Test
public
void
testGetWeatherByZipCode
()
{
WeatherRecord
result
=
weatherService
.
getWeatherByZipCode
(
zipCode
);
assertNotNull
(
result
);
assertEquals
(
zipCode
,
result
.
getZipCode
());
assertEquals
(
72.0
,
result
.
getTemperature
());
assertEquals
(
70.0
,
result
.
getFeelsLike
());
assertEquals
(
68.0
,
result
.
getTempMin
());
assertEquals
(
75.0
,
result
.
getTempMax
());
assertEquals
(
50
,
result
.
getHumidity
());
assertEquals
(
5.0
,
result
.
getWindSpeed
());
assertEquals
(
"New York"
,
result
.
getCity
());
assertEquals
(
"https://openweathermap.org/img/wn/[email protected]"
,
result
.
getIcon
());
assertEquals
(
800
,
result
.
getConditionCode
());
}
@
Test
public
void
testGetWeatherByZipCodeWithEmptyResponse
()
{
when
(
restTemplate
.
getForObject
(
anyString
(),
any
())).
thenReturn
(
null
);
RuntimeException
exception
=
null
;
try
{
weatherService
.
getWeatherByZipCode
(
zipCode
);
}
catch
(
RuntimeException
e
)
{
exception
=
e
;
}
assertNotNull
(
exception
);
assertEquals
(
"Unable to fetch weather data"
,
exception
.
getMessage
());
}
}
weather_app/src/test/java/com/service/weather/ZipCodeServiceTest.java
weather_app/src/test/java/com/service/weather/ZipCodeServiceTest.java
package
com
.
service
.
weather
;
import
com
.
service
.
weather
.
model
.
ZipCode
;
import
com
.
service
.
weather
.
repository
.
ZipCodeRepository
;
import
com
.
service
.
weather
.
service
.
ZipCodeService
;
import
org
.
junit
.
jupiter
.
api
.
BeforeEach
;
import
org
.
junit
.
jupiter
.
api
.
Test
;
import
org
.
junit
.
jupiter
.
api
.
extension
.
ExtendWith
;
import
org
.
mockito
.
InjectMocks
;
import
org
.
mockito
.
Mock
;
import
org
.
mockito
.
junit
.
jupiter
.
MockitoExtension
;
import
java
.
util
.
Arrays
;
import
java
.
util
.
List
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
*
;
import
static
org
.
mockito
.
Mockito
.
*
;
@
ExtendWith
(
MockitoExtension
.
class
)
public
class
ZipCodeServiceTest
{
@
Mock
private
ZipCodeRepository
zipCodeRepository
;
@
InjectMocks
private
ZipCodeService
zipCodeService
;
private
ZipCode
zipCode1
;
private
ZipCode
zipCode2
;
@
BeforeEach
public
void
setUp
()
{
zipCode1
=
new
ZipCode
(
1L
,
"City1"
,
"10001"
);
zipCode2
=
new
ZipCode
(
2L
,
"City2"
,
"10002"
);
}
@
Test
public
void
testGetAllZipCodes
()
{
when
(
zipCodeRepository
.
findAll
()).
thenReturn
(
Arrays
.
asList
(
zipCode1
,
zipCode2
));
List
<
ZipCode
>
zipCodes
=
zipCodeService
.
getAllZipCodes
();
assertEquals
(
2
,
zipCodes
.
size
());
verify
(
zipCodeRepository
,
times
(
1
)).
findAll
();
}
@
Test
public
void
testAddZipCode
()
{
when
(
zipCodeRepository
.
save
(
zipCode1
)).
thenReturn
(
zipCode1
);
ZipCode
savedZipCode
=
zipCodeService
.
addZipCode
(
zipCode1
);
assertNotNull
(
savedZipCode
);
assertEquals
(
"10001"
,
savedZipCode
.
getZipCode
());
verify
(
zipCodeRepository
,
times
(
1
)).
save
(
zipCode1
);
}
@
Test
public
void
testRemoveZipCode
()
{
doNothing
().
when
(
zipCodeRepository
).
deleteById
(
1L
);
zipCodeService
.
removeZipCode
(
1L
);
verify
(
zipCodeRepository
,
times
(
1
)).
deleteById
(
1L
);
}
}
weather_app/weather.iml