Project report

profilelily33
weather_app_final.zip

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 Weather

Add Zip Code to List

Add to List

Saved 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