diff --git a/composer.json b/composer.json index 1b6f4f0..2697262 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": ">=8.1", "myclabs/deep-copy": "^1.13", - "programmatordev/php-api-sdk": "^2.0", + "programmatordev/php-api-sdk": "^2.1", "symfony/options-resolver": "^6.4|^7.3" }, "require-dev": { diff --git a/docs/03-supported-apis.md b/docs/03-supported-apis.md index de77325..6742d70 100644 --- a/docs/03-supported-apis.md +++ b/docs/03-supported-apis.md @@ -5,6 +5,7 @@ - [getWeather](#getweather) - [getWeatherByDate](#getweatherbydate) - [getWeatherSummaryByDate](#getweathersummarybydate) + - [getWeatherOverviewByDate](#getweatheroverviewbydate) - [Weather](#weather) - [getCurrent](#getcurrent) - [getForecast](#getforecast) @@ -68,6 +69,21 @@ Returns a [`WeatherSummary`](05-entities.md#weathersummary) object: $weatherSummary = $api->oneCall()->getWeatherSummaryByDate(50, 50, new \DateTime('1985-07-19')); ``` +#### `getWeatherOverviewByDate` + +```php +getWeatherOverviewByDate(float $latitude, float $longitude, \DateTimeInterface $date): WeatherOverview +``` + +Get the weather overview with a human-readable summary for today and tomorrow's forecast, +using OpenWeather AI. + +Returns a [`WeatherOverview`](05-entities.md#weatheroverview) object: + +```php +$weatherOverview = $api->oneCall()->getWeatherOverviewByDate(50, 50, new \DateTime('today')); +``` + ### Weather #### `getCurrent` diff --git a/docs/05-entities.md b/docs/05-entities.md index 5b136e2..fb84b9f 100644 --- a/docs/05-entities.md +++ b/docs/05-entities.md @@ -78,6 +78,13 @@ - `getAtmosphericPressure()`: `int` - `getWind()`: [`Wind`](#wind) +### WeatherOverview + +- `getCoordinate()`: [`Coordinate`](#coordinate) +- `getTimezone()`: [`Timezone`](#timezone) +- `getDateTime()`: `\DateTimeImmutable` +- `getOverview()`: `string` + ### WeatherData - `getDateTime()`: `\DateTimeImmutable` diff --git a/src/Entity/AirPollution/AirPollutionCollection.php b/src/Entity/AirPollution/AirPollutionCollection.php index 6dcbf46..c91c222 100644 --- a/src/Entity/AirPollution/AirPollutionCollection.php +++ b/src/Entity/AirPollution/AirPollutionCollection.php @@ -18,7 +18,7 @@ class AirPollutionCollection public function __construct(array $data) { - $this->numResults = \count($data['list']); + $this->numResults = count($data['list']); $this->coordinate = new Coordinate($data['coord']); $this->data = $this->createEntityList(AirPollutionData::class, $data['list']); } diff --git a/src/Entity/AirPollution/AirQuality.php b/src/Entity/AirPollution/AirQuality.php index ddc2d6c..8e3c50e 100644 --- a/src/Entity/AirPollution/AirQuality.php +++ b/src/Entity/AirPollution/AirQuality.php @@ -28,12 +28,12 @@ private function findQualitativeName(int $index): string { // levels based on https://openweathermap.org/api/air-pollution return match ($index) { - 0 => 'Undefined', 1 => 'Good', 2 => 'Fair', 3 => 'Moderate', 4 => 'Poor', - 5 => 'Very Poor' + 5 => 'Very Poor', + default => 'Undefined' }; } } \ No newline at end of file diff --git a/src/Entity/OneCall/DayData.php b/src/Entity/OneCall/DayData.php index 11cd28e..fd3adec 100644 --- a/src/Entity/OneCall/DayData.php +++ b/src/Entity/OneCall/DayData.php @@ -28,7 +28,7 @@ public function __construct(array $data) $this->temperature = new Temperature($data['temp']); $this->temperatureFeelsLike = new Temperature($data['feels_like']); - $this->precipitationProbability = \round($data['pop'] * 100); + $this->precipitationProbability = round($data['pop'] * 100); $this->summary = $data['summary']; $this->moonPhase = new MoonPhase($data); $this->moonriseAt = \DateTimeImmutable::createFromFormat('U', $data['moonrise']); diff --git a/src/Entity/OneCall/HourData.php b/src/Entity/OneCall/HourData.php index e417dc2..fa59ccf 100644 --- a/src/Entity/OneCall/HourData.php +++ b/src/Entity/OneCall/HourData.php @@ -19,7 +19,7 @@ public function __construct(array $data) $this->temperature = $data['temp']; $this->temperatureFeelsLike = $data['feels_like']; $this->visibility = $data['visibility']; - $this->precipitationProbability = \round($data['pop'] * 100); + $this->precipitationProbability = round($data['pop'] * 100); } public function getTemperature(): float diff --git a/src/Entity/OneCall/MoonPhase.php b/src/Entity/OneCall/MoonPhase.php index 3d036f8..b13828b 100644 --- a/src/Entity/OneCall/MoonPhase.php +++ b/src/Entity/OneCall/MoonPhase.php @@ -23,7 +23,7 @@ public function __construct(array $data) { $this->value = $data['moon_phase']; $this->systemName = $this->findSystemName($this->value); - $this->name = \ucwords(\strtolower(\str_replace('_', ' ', $this->systemName))); + $this->name = ucwords(strtolower(str_replace('_', ' ', $this->systemName))); } public function getValue(): float diff --git a/src/Entity/OneCall/WeatherOverview.php b/src/Entity/OneCall/WeatherOverview.php new file mode 100644 index 0000000..6e2fed5 --- /dev/null +++ b/src/Entity/OneCall/WeatherOverview.php @@ -0,0 +1,53 @@ +coordinate = new Coordinate($data); + + $this->timezone = new Timezone([ + 'timezone_offset' => \DateTimeImmutable::createFromFormat('P', $data['tz'])->getOffset() + ]); + + $this->dateTime = \DateTimeImmutable::createFromFormat( + 'Y-m-d H:i:s P', + sprintf('%s 00:00:00 %s', $data['date'], $data['tz']) + ); + + $this->overview = $data['weather_overview']; + } + + public function getCoordinate(): Coordinate + { + return $this->coordinate; + } + + public function getTimezone(): Timezone + { + return $this->timezone; + } + + public function getDateTime(): \DateTimeImmutable + { + return $this->dateTime; + } + + public function getOverview(): string + { + return $this->overview; + } +} \ No newline at end of file diff --git a/src/Entity/OneCall/WeatherSummary.php b/src/Entity/OneCall/WeatherSummary.php index 4396839..4fb3af0 100644 --- a/src/Entity/OneCall/WeatherSummary.php +++ b/src/Entity/OneCall/WeatherSummary.php @@ -36,7 +36,7 @@ public function __construct(array $data) $this->dateTime = \DateTimeImmutable::createFromFormat( 'Y-m-d H:i:s P', - \sprintf('%s 00:00:00 %s', $data['date'], $data['tz']) + sprintf('%s 00:00:00 %s', $data['date'], $data['tz']) ); $this->cloudiness = \round($data['cloud_cover']['afternoon']); @@ -52,11 +52,11 @@ public function __construct(array $data) 'max' => $data['temperature']['max'] ]); - $this->atmosphericPressure = \round($data['pressure']['afternoon']); + $this->atmosphericPressure = round($data['pressure']['afternoon']); $this->wind = new Wind([ 'speed' => $data['wind']['max']['speed'], - 'deg' => \round($data['wind']['max']['direction']) + 'deg' => round($data['wind']['max']['direction']) ]); } @@ -70,9 +70,6 @@ public function getTimezone(): Timezone return $this->timezone; } - /** - * DateTime in UTC - */ public function getDateTime(): \DateTimeImmutable { return $this->dateTime; diff --git a/src/Entity/Weather/WeatherData.php b/src/Entity/Weather/WeatherData.php index fae2605..021c22f 100644 --- a/src/Entity/Weather/WeatherData.php +++ b/src/Entity/Weather/WeatherData.php @@ -54,7 +54,7 @@ public function __construct(array $data) $this->wind = new Wind($data['wind']); $this->precipitationProbability = isset($data['pop']) - ? \round($data['pop'] * 100) + ? round($data['pop'] * 100) : null; $this->rainVolume = $data['rain']['1h'] ?? $data['rain']['3h'] ?? null; diff --git a/src/Exception/ApiErrorException.php b/src/Exception/ApiErrorException.php index d10f5f3..88209f3 100644 --- a/src/Exception/ApiErrorException.php +++ b/src/Exception/ApiErrorException.php @@ -8,7 +8,9 @@ class ApiErrorException extends \Exception public function __construct(array $error) { - parent::__construct($error['message'], $error['cod']); + $code = $error['cod'] ?? $error['code']; + + parent::__construct($error['message'], $code); $this->parameters = $error['parameters'] ?? null; } diff --git a/src/Resource/OneCallResource.php b/src/Resource/OneCallResource.php index 837fd08..375dede 100644 --- a/src/Resource/OneCallResource.php +++ b/src/Resource/OneCallResource.php @@ -5,6 +5,7 @@ use ProgrammatorDev\Api\Method; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\Weather; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherMoment; +use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherOverview; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherSummary; use ProgrammatorDev\OpenWeatherMap\Resource\Util\LanguageTrait; use ProgrammatorDev\OpenWeatherMap\Resource\Util\UnitSystemTrait; @@ -77,4 +78,24 @@ public function getWeatherSummaryByDate(float $latitude, float $longitude, \Date return new WeatherSummary($data); } + + /** + * Get the weather overview with a human-readable summary for today and tomorrow's forecast, using OpenWeather AI + * + * @throws ClientExceptionInterface + */ + public function getWeatherOverviewByDate(float $latitude, float $longitude, \DateTimeInterface $date): WeatherOverview + { + $data = $this->api->request( + method: Method::GET, + path: '/data/3.0/onecall/overview', + query: [ + 'lat' => $latitude, + 'lon' => $longitude, + 'date' => $date->format('Y-m-d') + ] + ); + + return new WeatherOverview($data); + } } \ No newline at end of file diff --git a/src/Test/MockResponse.php b/src/Test/MockResponse.php index 1f9ce44..0e5180b 100644 --- a/src/Test/MockResponse.php +++ b/src/Test/MockResponse.php @@ -7,6 +7,7 @@ class MockResponse public const ONE_CALL_WEATHER = '{"lat":38.7078,"lon":-9.1366,"timezone":"Europe/Lisbon","timezone_offset":3600,"current":{"dt":1688384139,"sunrise":1688361368,"sunset":1688414697,"temp":25.1,"feels_like":25.21,"pressure":1017,"humidity":59,"dew_point":16.53,"uvi":9.78,"clouds":20,"visibility":10000,"wind_speed":7.2,"wind_deg":10,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}]},"minutely":[{"dt":1688384160,"precipitation":0},{"dt":1688384220,"precipitation":0},{"dt":1688384280,"precipitation":0},{"dt":1688384340,"precipitation":0},{"dt":1688384400,"precipitation":0},{"dt":1688384460,"precipitation":0},{"dt":1688384520,"precipitation":0},{"dt":1688384580,"precipitation":0},{"dt":1688384640,"precipitation":0},{"dt":1688384700,"precipitation":0},{"dt":1688384760,"precipitation":0},{"dt":1688384820,"precipitation":0},{"dt":1688384880,"precipitation":0},{"dt":1688384940,"precipitation":0},{"dt":1688385000,"precipitation":0},{"dt":1688385060,"precipitation":0},{"dt":1688385120,"precipitation":0},{"dt":1688385180,"precipitation":0},{"dt":1688385240,"precipitation":0},{"dt":1688385300,"precipitation":0},{"dt":1688385360,"precipitation":0},{"dt":1688385420,"precipitation":0},{"dt":1688385480,"precipitation":0},{"dt":1688385540,"precipitation":0},{"dt":1688385600,"precipitation":0},{"dt":1688385660,"precipitation":0},{"dt":1688385720,"precipitation":0},{"dt":1688385780,"precipitation":0},{"dt":1688385840,"precipitation":0.1032},{"dt":1688385900,"precipitation":0.115},{"dt":1688385960,"precipitation":0.1276},{"dt":1688386020,"precipitation":0.1402},{"dt":1688386080,"precipitation":0.1528},{"dt":1688386140,"precipitation":0.1654},{"dt":1688386200,"precipitation":0.178},{"dt":1688386260,"precipitation":0.1654},{"dt":1688386320,"precipitation":0.1528},{"dt":1688386380,"precipitation":0.1402},{"dt":1688386440,"precipitation":0.1276},{"dt":1688386500,"precipitation":0.115},{"dt":1688386560,"precipitation":0.1032},{"dt":1688386620,"precipitation":0},{"dt":1688386680,"precipitation":0},{"dt":1688386740,"precipitation":0},{"dt":1688386800,"precipitation":0},{"dt":1688386860,"precipitation":0},{"dt":1688386920,"precipitation":0},{"dt":1688386980,"precipitation":0},{"dt":1688387040,"precipitation":0},{"dt":1688387100,"precipitation":0},{"dt":1688387160,"precipitation":0},{"dt":1688387220,"precipitation":0},{"dt":1688387280,"precipitation":0},{"dt":1688387340,"precipitation":0},{"dt":1688387400,"precipitation":0},{"dt":1688387460,"precipitation":0},{"dt":1688387520,"precipitation":0},{"dt":1688387580,"precipitation":0},{"dt":1688387640,"precipitation":0},{"dt":1688387700,"precipitation":0},{"dt":1688387760,"precipitation":0}],"hourly":[{"dt":1688382000,"temp":25.38,"feels_like":25.44,"pressure":1017,"humidity":56,"dew_point":15.97,"uvi":8.42,"clouds":16,"visibility":10000,"wind_speed":4.94,"wind_deg":327,"wind_gust":6.14,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1688385600,"temp":25.1,"feels_like":25.21,"pressure":1017,"humidity":59,"dew_point":16.53,"uvi":9.78,"clouds":20,"visibility":10000,"wind_speed":5.92,"wind_deg":325,"wind_gust":7.02,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1688389200,"temp":25.69,"feels_like":25.75,"pressure":1017,"humidity":55,"dew_point":15.98,"uvi":9.92,"clouds":16,"visibility":10000,"wind_speed":6.69,"wind_deg":328,"wind_gust":7.82,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1688392800,"temp":26.43,"feels_like":26.43,"pressure":1017,"humidity":50,"dew_point":15.18,"uvi":8.86,"clouds":12,"visibility":10000,"wind_speed":7.32,"wind_deg":331,"wind_gust":8.69,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1688396400,"temp":26.82,"feels_like":27.09,"pressure":1017,"humidity":47,"dew_point":14.57,"uvi":6.86,"clouds":8,"visibility":10000,"wind_speed":7.95,"wind_deg":337,"wind_gust":9.66,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688400000,"temp":26.88,"feels_like":26.92,"pressure":1017,"humidity":43,"dew_point":13.26,"uvi":4.61,"clouds":4,"visibility":10000,"wind_speed":8.11,"wind_deg":341,"wind_gust":10.42,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688403600,"temp":26.09,"feels_like":26.09,"pressure":1017,"humidity":45,"dew_point":12.76,"uvi":2.47,"clouds":0,"visibility":10000,"wind_speed":8.65,"wind_deg":344,"wind_gust":11.66,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688407200,"temp":24.67,"feels_like":24.52,"pressure":1017,"humidity":51,"dew_point":13.62,"uvi":0.99,"clouds":0,"visibility":10000,"wind_speed":8.8,"wind_deg":347,"wind_gust":12.69,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688410800,"temp":22.93,"feels_like":22.85,"pressure":1018,"humidity":60,"dew_point":14.39,"uvi":0.26,"clouds":0,"visibility":10000,"wind_speed":8.19,"wind_deg":348,"wind_gust":13.04,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688414400,"temp":21.16,"feels_like":21.13,"pressure":1018,"humidity":69,"dew_point":14.95,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":6.88,"wind_deg":347,"wind_gust":12.91,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688418000,"temp":20,"feels_like":20.04,"pressure":1019,"humidity":76,"dew_point":15.45,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":6.37,"wind_deg":346,"wind_gust":12.91,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688421600,"temp":19.41,"feels_like":19.5,"pressure":1019,"humidity":80,"dew_point":15.55,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.94,"wind_deg":348,"wind_gust":12.71,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688425200,"temp":19.04,"feels_like":19.11,"pressure":1020,"humidity":81,"dew_point":15.48,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.74,"wind_deg":348,"wind_gust":12.82,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688428800,"temp":18.7,"feels_like":18.77,"pressure":1020,"humidity":82,"dew_point":15.32,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.63,"wind_deg":346,"wind_gust":12.84,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688432400,"temp":18.45,"feels_like":18.52,"pressure":1020,"humidity":83,"dew_point":15.27,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.38,"wind_deg":344,"wind_gust":12.56,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688436000,"temp":18.15,"feels_like":18.24,"pressure":1019,"humidity":85,"dew_point":15.27,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.28,"wind_deg":344,"wind_gust":12.34,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688439600,"temp":17.93,"feels_like":18.02,"pressure":1019,"humidity":86,"dew_point":15.38,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.14,"wind_deg":342,"wind_gust":11.93,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688443200,"temp":17.73,"feels_like":17.83,"pressure":1019,"humidity":87,"dew_point":15.36,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.1,"wind_deg":340,"wind_gust":11.84,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688446800,"temp":17.58,"feels_like":17.67,"pressure":1019,"humidity":87,"dew_point":15.24,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":4.88,"wind_deg":339,"wind_gust":11.23,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688450400,"temp":17.79,"feels_like":17.87,"pressure":1019,"humidity":86,"dew_point":15.25,"uvi":0.17,"clouds":0,"visibility":10000,"wind_speed":5.07,"wind_deg":339,"wind_gust":11.53,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688454000,"temp":18.96,"feels_like":18.97,"pressure":1019,"humidity":79,"dew_point":14.99,"uvi":0.75,"clouds":0,"visibility":10000,"wind_speed":5.6,"wind_deg":344,"wind_gust":10.56,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688457600,"temp":20.57,"feels_like":20.48,"pressure":1020,"humidity":69,"dew_point":14.39,"uvi":2.05,"clouds":0,"visibility":10000,"wind_speed":6.35,"wind_deg":344,"wind_gust":9.7,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688461200,"temp":22.34,"feels_like":22.14,"pressure":1020,"humidity":58,"dew_point":13.35,"uvi":4.13,"clouds":0,"visibility":10000,"wind_speed":6.29,"wind_deg":345,"wind_gust":8.74,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688464800,"temp":23.94,"feels_like":23.7,"pressure":1020,"humidity":50,"dew_point":12.37,"uvi":6.49,"clouds":0,"visibility":10000,"wind_speed":6.28,"wind_deg":341,"wind_gust":8.01,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688468400,"temp":25.12,"feels_like":24.86,"pressure":1020,"humidity":45,"dew_point":11.69,"uvi":8.74,"clouds":0,"visibility":10000,"wind_speed":6.67,"wind_deg":336,"wind_gust":7.78,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688472000,"temp":25.82,"feels_like":25.55,"pressure":1019,"humidity":42,"dew_point":11.33,"uvi":10.16,"clouds":0,"visibility":10000,"wind_speed":6.93,"wind_deg":333,"wind_gust":7.78,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688475600,"temp":26.12,"feels_like":26.12,"pressure":1019,"humidity":41,"dew_point":11.21,"uvi":10.23,"clouds":0,"visibility":10000,"wind_speed":7.39,"wind_deg":331,"wind_gust":8.04,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688479200,"temp":26.13,"feels_like":26.13,"pressure":1019,"humidity":41,"dew_point":11.01,"uvi":9.13,"clouds":0,"visibility":10000,"wind_speed":7.75,"wind_deg":332,"wind_gust":8.6,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688482800,"temp":25.79,"feels_like":25.5,"pressure":1019,"humidity":41,"dew_point":10.99,"uvi":7.07,"clouds":0,"visibility":10000,"wind_speed":7.86,"wind_deg":333,"wind_gust":9,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688486400,"temp":25.14,"feels_like":24.83,"pressure":1018,"humidity":43,"dew_point":11.02,"uvi":4.68,"clouds":0,"visibility":10000,"wind_speed":7.9,"wind_deg":334,"wind_gust":9.28,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688490000,"temp":24.33,"feels_like":23.97,"pressure":1018,"humidity":44,"dew_point":10.98,"uvi":2.5,"clouds":0,"visibility":10000,"wind_speed":7.8,"wind_deg":336,"wind_gust":9.64,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688493600,"temp":23.02,"feels_like":22.63,"pressure":1018,"humidity":48,"dew_point":11.18,"uvi":1,"clouds":0,"visibility":10000,"wind_speed":7.77,"wind_deg":339,"wind_gust":10.17,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688497200,"temp":21.59,"feels_like":21.21,"pressure":1019,"humidity":54,"dew_point":11.6,"uvi":0.26,"clouds":0,"visibility":10000,"wind_speed":7.21,"wind_deg":342,"wind_gust":10.42,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688500800,"temp":19.87,"feels_like":19.58,"pressure":1019,"humidity":64,"dew_point":12.66,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":6.25,"wind_deg":340,"wind_gust":11.21,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1688504400,"temp":18.72,"feels_like":18.66,"pressure":1019,"humidity":77,"dew_point":14.27,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.96,"wind_deg":341,"wind_gust":12.03,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688508000,"temp":18.26,"feels_like":18.31,"pressure":1020,"humidity":83,"dew_point":15.08,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.73,"wind_deg":340,"wind_gust":12.21,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688511600,"temp":18.1,"feels_like":18.21,"pressure":1019,"humidity":86,"dew_point":15.47,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":5.61,"wind_deg":344,"wind_gust":12.34,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688515200,"temp":18.1,"feels_like":18.24,"pressure":1019,"humidity":87,"dew_point":15.75,"uvi":0,"clouds":1,"visibility":10000,"wind_speed":5.75,"wind_deg":344,"wind_gust":12.19,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688518800,"temp":18.25,"feels_like":18.38,"pressure":1019,"humidity":86,"dew_point":15.67,"uvi":0,"clouds":16,"visibility":10000,"wind_speed":5.71,"wind_deg":346,"wind_gust":11.73,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02n"}],"pop":0},{"dt":1688522400,"temp":18.21,"feels_like":18.31,"pressure":1018,"humidity":85,"dew_point":15.32,"uvi":0,"clouds":14,"visibility":10000,"wind_speed":5.25,"wind_deg":345,"wind_gust":11.6,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02n"}],"pop":0},{"dt":1688526000,"temp":17.82,"feels_like":17.9,"pressure":1018,"humidity":86,"dew_point":15.12,"uvi":0,"clouds":10,"visibility":10000,"wind_speed":4.67,"wind_deg":337,"wind_gust":11.06,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688529600,"temp":17.66,"feels_like":17.73,"pressure":1018,"humidity":86,"dew_point":15.1,"uvi":0,"clouds":9,"visibility":10000,"wind_speed":4.77,"wind_deg":339,"wind_gust":11.79,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688533200,"temp":17.54,"feels_like":17.6,"pressure":1018,"humidity":86,"dew_point":14.95,"uvi":0,"clouds":8,"visibility":10000,"wind_speed":4.65,"wind_deg":336,"wind_gust":10.53,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1688536800,"temp":17.83,"feels_like":17.86,"pressure":1018,"humidity":84,"dew_point":14.82,"uvi":0.17,"clouds":11,"visibility":10000,"wind_speed":5.08,"wind_deg":337,"wind_gust":11.85,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"pop":0},{"dt":1688540400,"temp":18.98,"feels_like":18.94,"pressure":1019,"humidity":77,"dew_point":14.66,"uvi":0.73,"clouds":71,"visibility":10000,"wind_speed":6.05,"wind_deg":327,"wind_gust":10.65,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1688544000,"temp":20.08,"feels_like":19.95,"pressure":1018,"humidity":69,"dew_point":13.96,"uvi":2,"clouds":85,"visibility":10000,"wind_speed":6.22,"wind_deg":352,"wind_gust":10.17,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1688547600,"temp":20.87,"feels_like":20.68,"pressure":1018,"humidity":64,"dew_point":13.5,"uvi":4.03,"clouds":90,"visibility":10000,"wind_speed":6.36,"wind_deg":346,"wind_gust":9.8,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1688551200,"temp":21.11,"feels_like":20.87,"pressure":1018,"humidity":61,"dew_point":13.04,"uvi":5.58,"clouds":92,"visibility":10000,"wind_speed":5.05,"wind_deg":348,"wind_gust":7.7,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0}],"daily":[{"dt":1688385600,"sunrise":1688361368,"sunset":1688414697,"moonrise":1688417100,"moonset":1688359440,"moon_phase":0.5,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":25.1,"min":18.28,"max":26.88,"night":19.41,"eve":24.67,"morn":18.39},"feels_like":{"day":25.21,"night":19.5,"eve":24.52,"morn":18.5},"pressure":1017,"humidity":59,"dew_point":16.53,"wind_speed":8.8,"wind_deg":347,"wind_gust":13.04,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"clouds":20,"pop":0,"uvi":9.92},{"dt":1688472000,"sunrise":1688447798,"sunset":1688501087,"moonrise":1688506560,"moonset":1688450100,"moon_phase":0.54,"summary":"There will be clear sky today","temp":{"day":25.82,"min":17.58,"max":26.13,"night":18.26,"eve":23.02,"morn":17.79},"feels_like":{"day":25.55,"night":18.31,"eve":22.63,"morn":17.87},"pressure":1019,"humidity":42,"dew_point":11.33,"wind_speed":7.9,"wind_deg":334,"wind_gust":12.84,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":0,"pop":0,"uvi":10.23},{"dt":1688558400,"sunrise":1688534230,"sunset":1688587476,"moonrise":1688595540,"moonset":1688541240,"moon_phase":0.58,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":25.2,"min":17.54,"max":25.28,"night":18.64,"eve":22.5,"morn":17.83},"feels_like":{"day":24.98,"night":18.7,"eve":22.32,"morn":17.86},"pressure":1018,"humidity":46,"dew_point":12.27,"wind_speed":7.86,"wind_deg":324,"wind_gust":12.34,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":71,"pop":0,"uvi":9.93},{"dt":1688644800,"sunrise":1688620663,"sunset":1688673862,"moonrise":1688683980,"moonset":1688632380,"moon_phase":0.62,"summary":"The day will start with partly cloudy through the late morning hours, transitioning to clearing","temp":{"day":25.05,"min":17.57,"max":25.05,"night":19.01,"eve":22.56,"morn":17.59},"feels_like":{"day":24.86,"night":19.06,"eve":22.39,"morn":17.75},"pressure":1016,"humidity":48,"dew_point":12.69,"wind_speed":7.09,"wind_deg":312,"wind_gust":9.69,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":2,"pop":0,"uvi":8.84},{"dt":1688731200,"sunrise":1688707097,"sunset":1688760247,"moonrise":0,"moonset":1688723400,"moon_phase":0.65,"summary":"There will be clear sky until morning, then partly cloudy","temp":{"day":24.48,"min":17.64,"max":24.48,"night":19.8,"eve":22.83,"morn":17.64},"feels_like":{"day":24.32,"night":20,"eve":22.81,"morn":17.81},"pressure":1017,"humidity":51,"dew_point":13,"wind_speed":5.56,"wind_deg":305,"wind_gust":7.26,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"clouds":15,"pop":0,"uvi":8.73},{"dt":1688817600,"sunrise":1688793533,"sunset":1688846630,"moonrise":1688772060,"moonset":1688814180,"moon_phase":0.69,"summary":"The day will start with partly cloudy through the late morning hours, transitioning to clearing","temp":{"day":24.93,"min":18.43,"max":25.64,"night":20.37,"eve":23.97,"morn":18.43},"feels_like":{"day":24.99,"night":20.47,"eve":23.96,"morn":18.65},"pressure":1019,"humidity":58,"dew_point":15.5,"wind_speed":5.99,"wind_deg":276,"wind_gust":6.46,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":5,"pop":0,"uvi":9},{"dt":1688904000,"sunrise":1688879970,"sunset":1688933011,"moonrise":1688860020,"moonset":1688904780,"moon_phase":0.73,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":24.48,"min":18.35,"max":25.38,"night":18.35,"eve":22.97,"morn":19.77},"feels_like":{"day":24.42,"night":18.07,"eve":22.55,"morn":20.15},"pressure":1021,"humidity":55,"dew_point":14.3,"wind_speed":7.17,"wind_deg":330,"wind_gust":10.71,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":70,"pop":0,"uvi":9},{"dt":1688990400,"sunrise":1688966408,"sunset":1689019390,"moonrise":1688947980,"moonset":1688995260,"moon_phase":0.75,"summary":"There will be clear sky today","temp":{"day":25.17,"min":16.95,"max":25.65,"night":20.15,"eve":24,"morn":17.24},"feels_like":{"day":25.18,"night":20.1,"eve":23.97,"morn":17.08},"pressure":1021,"humidity":55,"dew_point":15.07,"wind_speed":6.84,"wind_deg":315,"wind_gust":8.77,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":0,"pop":0,"uvi":9}],"alerts":[{"sender_name":"NWS Portland (Northwest Oregon and Southwest Washington)","event":"Heat Advisory","start":1688490000,"end":1688623200,"description":"...HEAT ADVISORY REMAINS IN EFFECT FROM 10 AM TUESDAY TO 11 PM\nPDT WEDNESDAY...\n* WHAT...Maximum temperatures of 92 to 102 degrees expected\nTuesday and Wednesday. Hottest temperatures will be across\ninner portions of the Portland, Vancouver, Hillsboro, and\nSalem metropolitan areas.\n* WHERE...In Oregon, Greater Portland Metro Area and Central\nWillamette Valley. In Washington, Greater Vancouver Area.\n* WHEN...From 10 AM Tuesday to 11 PM PDT Wednesday.\n* IMPACTS...Hot temperatures may cause heat illnesses to occur.\n* ADDITIONAL DETAILS...Temperatures will struggle to fall much\nbelow 70 degrees Tuesday night for the inner urban core of\nPortland.","tags":["Extreme temperature value"]}]}'; public const ONE_CALL_TIMEMACHINE = '{"lat":38.7078,"lon":-9.1366,"timezone":"Europe/Lisbon","timezone_offset":0,"data":[{"dt":1672531200,"sunrise":1672559671,"sunset":1672593902,"temp":17.48,"feels_like":17.16,"pressure":1019,"humidity":72,"dew_point":12.38,"clouds":20,"visibility":9999,"wind_speed":16.54,"wind_deg":337,"wind_gust":16.54,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02n"}]}]}'; public const ONE_CALL_DAY_SUMMARY = '{"lat":38.7077507,"lon":-9.1365919,"tz":"+00:00","date":"2023-01-01","units":"metric","cloud_cover":{"afternoon":75.0},"humidity":{"afternoon":71.0},"precipitation":{"total":2.53},"temperature":{"min":12.52,"max":18.29,"afternoon":18.26,"night":17.39,"evening":13.9,"morning":17.23},"pressure":{"afternoon":1017.0},"wind":{"max":{"speed":26.38,"direction":225.0}}}'; + public const ONE_CALL_OVERVIEW = '{"lat":38.7078,"lon":-9.1366,"tz":"+00:00","date":"2025-11-03","units":"metric","weather_overview":"Weather overview text"}'; public const WEATHER_CURRENT = '{"coord":{"lon":-9.1366,"lat":38.7078},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":27.12,"feels_like":28.16,"temp_min":22.76,"temp_max":29.9,"pressure":1013,"humidity":59,"sea_level":1013,"grnd_level":997},"visibility":10000,"wind":{"speed":9.26,"deg":360,"gust":2.34},"clouds":{"all":0},"rain":{"1h":0.17,"3h":0.81},"snow":{"1h":0.14,"3h":0.46},"dt":1687949133,"sys":{"type":1,"id":6901,"country":"PT","sunrise":1687929236,"sunset":1687982718},"timezone":3600,"id":6930126,"name":"Chiado","cod":200}'; public const WEATHER_FORECAST = '{"cod":"200","message":0,"cnt":1,"list":[{"dt":1687975200,"main":{"temp":26.2,"feels_like":26.2,"temp_min":25.64,"temp_max":26.2,"pressure":1013,"sea_level":1013,"grnd_level":1013,"humidity":56,"temp_kf":0.56},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":{"all":0},"wind":{"speed":8.88,"deg":340,"gust":13.77},"visibility":10000,"pop":0,"sys":{"pod":"d"},"dt_txt":"2023-06-28 18:00:00"}],"city":{"id":6930126,"name":"Chiado","coord":{"lat":38.7078,"lon":-9.1366},"country":"PT","population":500000,"timezone":3600,"sunrise":1687929236,"sunset":1687982718}}'; diff --git a/tests/Integration/OneCallResourceTest.php b/tests/Integration/OneCallResourceTest.php index 4b0de5b..2528545 100644 --- a/tests/Integration/OneCallResourceTest.php +++ b/tests/Integration/OneCallResourceTest.php @@ -4,6 +4,7 @@ use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\Weather; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherMoment; +use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherOverview; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherSummary; use ProgrammatorDev\OpenWeatherMap\Test\AbstractTest; use ProgrammatorDev\OpenWeatherMap\Test\MockResponse; @@ -36,5 +37,12 @@ public static function provideItemResponseData(): \Generator 'getWeatherSummaryByDate', [50, 50, new \DateTime()] ]; + yield 'get weather overview by date' => [ + WeatherOverview::class, + MockResponse::ONE_CALL_OVERVIEW, + 'oneCall', + 'getWeatherOverviewByDate', + [50, 50, new \DateTime()] + ]; } } \ No newline at end of file diff --git a/tests/Unit/OneCall/WeatherOverviewTest.php b/tests/Unit/OneCall/WeatherOverviewTest.php new file mode 100644 index 0000000..cae347e --- /dev/null +++ b/tests/Unit/OneCall/WeatherOverviewTest.php @@ -0,0 +1,27 @@ + 50, + 'lon' => 50, + 'tz' => '+00:00', + 'date' => '2025-01-01', + 'weather_overview' => 'Weather overview text' + ]); + + $this->assertInstanceOf(Coordinate::class, $entity->getCoordinate()); + $this->assertInstanceOf(Timezone::class, $entity->getTimezone()); + $this->assertInstanceOf(\DateTimeImmutable::class, $entity->getDateTime()); + $this->assertSame('Weather overview text', $entity->getOverview()); + } +} \ No newline at end of file diff --git a/tests/Unit/OneCall/WeatherSummaryTest.php b/tests/Unit/OneCall/WeatherSummaryTest.php index 7a438bb..642474c 100644 --- a/tests/Unit/OneCall/WeatherSummaryTest.php +++ b/tests/Unit/OneCall/WeatherSummaryTest.php @@ -2,10 +2,8 @@ namespace ProgrammatorDev\OpenWeatherMap\Test\Unit\OneCall; -use ProgrammatorDev\OpenWeatherMap\Entity\Condition; use ProgrammatorDev\OpenWeatherMap\Entity\Coordinate; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\Temperature; -use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherData; use ProgrammatorDev\OpenWeatherMap\Entity\OneCall\WeatherSummary; use ProgrammatorDev\OpenWeatherMap\Entity\Timezone; use ProgrammatorDev\OpenWeatherMap\Entity\Wind;