View source
<?php
namespace Drupal\Tests\weather\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\weather\Entity\WeatherPlaceInterface;
class ParserTest extends BrowserTestBase {
use WeatherCommonTestTrait;
protected static $modules = [
'weather',
];
protected $defaultTheme = 'stark';
private function weatherGetForecastsFromDatabase($geoid, $utc_offset, $days, $detailed, $time) {
$current_local_time = gmdate('Y-m-d H:i:s', $time + $utc_offset * 60);
$connection = \Drupal::database();
$query = $connection
->select('weather_forecast', 'wfi');
$query
->condition('wfi.geoid', $geoid, '=');
$query
->condition('wfi.time_to', $current_local_time, '>=');
$query
->fields('wfi', [
'geoid',
'time_from',
'time_to',
'period',
'symbol',
'precipitation',
'wind_direction',
'wind_speed',
'temperature',
'pressure',
]);
$query
->range(0, 50);
$result = $query
->execute();
$first_forecast = $result
->fetchAll();
if ($first_forecast === FALSE) {
return [];
}
$weather = $this
->weatherCreateWeatherArray([
$first_forecast,
]);
$first_forecast_day = explode('-', key($weather));
$tomorrow_local_time = gmdate('Y-m-d H:i:s', gmmktime(0, 0, 0, $first_forecast_day[1], $first_forecast_day[2] + 1, $first_forecast_day[0]));
$forecasts_until_local_time = gmdate('Y-m-d 23:59:59', gmmktime(23, 59, 59, $first_forecast_day[1], $first_forecast_day[2] + $days - 1, $first_forecast_day[0]));
if ($detailed) {
if ($days > 0) {
$query = $connection
->select('weather_forecast', 'wfi');
$query
->condition('wfi.geoid', $geoid, '=');
$query
->condition('wfi.time_to', $forecasts_until_local_time, '>=');
$query
->fields('wfi', [
'geoid',
'time_from',
'time_to',
'period',
'symbol',
'precipitation',
'wind_direction',
'wind_speed',
'temperature',
'pressure',
]);
$query
->range(0, 50);
$result = $query
->execute();
$forecasts = $result
->fetchAll();
}
else {
$query = $connection
->select('weather_forecast', 'wfi');
$query
->condition('wfi.geoid', $geoid, '=');
$query
->condition('wfi.time_to', $current_local_time, '>=');
$query
->fields('wfi', [
'geoid',
'time_from',
'time_to',
'period',
'symbol',
'precipitation',
'wind_direction',
'wind_speed',
'temperature',
'pressure',
]);
$query
->range(0, 50);
$result = $query
->execute();
$forecasts = $result
->fetchAll();
}
$weather = $this
->weatherCreateWeatherArray($forecasts);
}
else {
if ($days > 1) {
$query = $connection
->select('weather_forecast', 'wfi');
$query
->condition('wfi.geoid', $geoid, '=');
$query
->condition('wfi.time_from', $tomorrow_local_time, '>=');
$query
->condition('wfi.time_to', $forecasts_until_local_time, '>=');
$query
->fields('wfi', [
'geoid',
'time_from',
'time_to',
'period',
'symbol',
'precipitation',
'wind_direction',
'wind_speed',
'temperature',
'pressure',
]);
$query
->range(0, 50);
$result = $query
->execute();
$forecasts = $result
->fetchAll();
$weather = array_merge($weather, $this
->weatherCreateWeatherArray($forecasts));
}
elseif ($days == 0) {
$query = $connection
->select('weather_forecast', 'wfi');
$query
->condition('wfi.geoid', $geoid, '=');
$query
->condition('wfi.time_from', $tomorrow_local_time, '>=');
$query
->fields('wfi', [
'geoid',
'time_from',
'time_to',
'period',
'symbol',
'precipitation',
'wind_direction',
'wind_speed',
'temperature',
'pressure',
]);
$query
->range(0, 50);
$result = $query
->execute();
$forecasts = $result
->fetchAll();
$weather = array_merge($weather, $this
->weatherCreateWeatherArray($forecasts));
}
}
return $weather;
}
public function weatherUpdatePlaces($fc) {
$place['geoid'] = $fc->location->location['geobase'] . "_" . $fc->location->location['geobaseid'];
$place['latitude'] = (string) $fc->location->location['latitude'];
$place['latitude'] = round($place['latitude'], 5);
$place['longitude'] = (string) $fc->location->location['longitude'];
$place['longitude'] = round($place['longitude'], 5);
$place['country'] = (string) $fc->location->country;
$place['name'] = (string) $fc->location->name;
$url = (string) $fc->credit->link['url'];
$link = substr($url, 23);
$link_parts = explode('/', $link);
array_shift($link_parts);
array_pop($link_parts);
$place['link'] = implode('/', $link_parts);
$info = $this
->weatherGetInformationAboutGeoid($place['geoid']);
if ($info === FALSE) {
$place['status'] = 'added';
\Drupal::database()
->insert('weather_place')
->fields($place)
->execute();
}
else {
$stored_info = (array) $info;
unset($stored_info['status']);
$diff = array_diff_assoc($stored_info, $place);
if (!empty($diff)) {
$place['status'] = WeatherPlaceInterface::STATUS_MODIFIED;
\Drupal::database()
->update('weather_place')
->condition('geoid', $place['geoid'])
->updateFields($place)
->execute();
}
}
}
public function weatherParseForecast($xml, $geoid = '') {
$use_errors = libxml_use_internal_errors(TRUE);
$fc = simplexml_load_string($xml);
libxml_use_internal_errors($use_errors);
if ($fc === FALSE) {
return FALSE;
}
$this
->weatherUpdatePlaces($fc);
if ($geoid == '') {
$geoid = $fc->location->location['geobase'] . "_" . $fc->location->location['geobaseid'];
}
$meta['geoid'] = $geoid;
$meta['utc_offset'] = (int) $fc->location->timezone['utcoffsetMinutes'];
$utctime = strtotime((string) $fc->meta->lastupdate . ' UTC') - 60 * $meta['utc_offset'];
$meta['last_update'] = gmdate('Y-m-d H:i:s', $utctime);
$utctime = strtotime((string) $fc->meta->nextupdate . ' UTC') - 60 * $meta['utc_offset'];
$meta['next_update'] = gmdate('Y-m-d H:i:s', $utctime);
$meta['next_download_attempt'] = $meta['next_update'];
\Drupal::database()
->merge('weather_forecast_information')
->key([
'geoid' => $meta['geoid'],
])
->insertFields($meta)
->updateFields($meta)
->execute();
\Drupal::database()
->delete('weather_forecast')
->condition('geoid', $meta['geoid'])
->execute();
foreach ($fc->forecast->tabular->time as $time) {
$forecast = [];
$forecast['geoid'] = $meta['geoid'];
$forecast['time_from'] = str_replace('T', ' ', (string) $time['from']);
$forecast['time_to'] = str_replace('T', ' ', (string) $time['to']);
$forecast['period'] = (string) $time['period'];
$forecast['symbol'] = (string) $time->symbol['var'];
if (strlen($forecast['symbol']) > 3) {
$forecast['symbol'] = substr($forecast['symbol'], 3, 3);
}
$forecast['precipitation'] = (double) $time->precipitation['value'];
$forecast['wind_direction'] = (int) $time->windDirection['deg'];
$forecast['wind_speed'] = (double) $time->windSpeed['mps'];
$forecast['temperature'] = (int) $time->temperature['value'];
$forecast['pressure'] = (int) $time->pressure['value'];
\Drupal::database()
->merge('weather_forecast')
->key([
'geoid' => $meta['geoid'],
'time_from' => $forecast['time_from'],
])
->insertFields($forecast)
->updateFields($forecast)
->execute();
}
return TRUE;
}
private function weatherDownloadForecast($geoid) {
$config = \Drupal::configFactory()
->getEditable('weather.settings');
$time = $config
->get('weather_time_for_testing');
if ($time !== \Drupal::time()
->getRequestTime()) {
$path = drupal_get_path('module', 'weather') . '/tests/src/Functional/data/' . $geoid . '.xml';
if (is_readable($path)) {
$xml = file_get_contents($path);
}
else {
$xml = '';
}
return $this
->weatherParseForecast($xml, $geoid);
}
$timeout = 10;
$url = $this
->weatherGetLinkForGeoId($geoid, 'yr');
$response = $this
->drupalGet($url, [
'timeout' => $timeout,
]);
if (!isset($response->error)) {
return $this
->weatherParseForecast($response->data, $geoid);
}
else {
\Drupal::logger('weather')
->error($response->error);
$user = \Drupal::currentUser();
$user
->hasPermission('administer site configuration');
if ($user
->hasPermission('administer custom weather block') or $user
->hasPermission('administer site configuration')) {
\Drupal::messenger()
->addMessage(t('Download of forecast failed: @error', [
'@error' => $response->error,
]), 'error');
}
}
}
private function weatherGetWeather($geoid, $days = 0, $detailed = TRUE) {
$config = \Drupal::configFactory()
->getEditable('weather.settings');
$time = $config
->get('weather_time_for_testing');
$query = \Drupal::database()
->select('weather_forecast_information', 'wfi');
$query
->fields('wfi', [
'geoid',
]);
$query
->condition('wfi.geoid', $geoid, '=');
$meta = $query
->execute();
$meta = $meta
->fetchAssoc();
$current_utc_time = gmdate('Y-m-d H:i:s', $time);
if (!empty($meta)) {
$meta['next_download_attempt'] = $time;
}
if ($meta === FALSE or $current_utc_time >= $meta['next_download_attempt']) {
$result = $this
->weatherDownloadForecast($geoid);
$query = \Drupal::database()
->select('weather_forecast_information', 'wfi');
$query
->fields('wfi', [
'geoid',
]);
$query
->condition('wfi.geoid', $geoid, '=');
$meta = $query
->execute();
$meta = $meta
->fetchAssoc();
if ($meta === FALSE) {
$meta['geoid'] = $geoid;
$meta['last_update'] = $current_utc_time;
$meta['next_update'] = $current_utc_time;
$meta['next_download_attempt'] = $current_utc_time;
$meta['utc_offset'] = 120;
}
if (empty($meta['next_update'])) {
$meta['next_update'] = $time;
}
if ($result == FALSE || $current_utc_time >= $meta['next_update']) {
$next_update = strtotime($meta['next_update'] . ' UTC');
$seconds_to_retry = 675;
while ($next_update + $seconds_to_retry <= $time) {
if ($seconds_to_retry < 86400) {
$seconds_to_retry = $seconds_to_retry * 2;
}
else {
$seconds_to_retry = $seconds_to_retry + 86400;
}
}
$meta['next_download_attempt'] = gmdate('Y-m-d H:i:s', $next_update + $seconds_to_retry);
\Drupal::database()
->delete('weather_forecast_information')
->condition('geoid', $meta['geoid'])
->execute();
\Drupal::database()
->insert('weather_forecast_information')
->fields($meta)
->execute();
}
}
if (empty($meta['utc_offset'])) {
$meta['utc_offset'] = 120;
}
$return_array['forecasts'] = $this
->weatherGetForecastsFromDatabase($geoid, $meta['utc_offset'], $days, $detailed, $time);
$return_array['utc_offset'] = $meta['utc_offset'];
return $return_array;
}
private function getInfoAboutForecast($time) {
$config = \Drupal::configFactory()
->getEditable('weather.settings');
$config
->set('weather_time_for_testing', $time)
->save();
$this
->weatherGetWeather('geonames_2911298', 1, FALSE);
$connection = \Drupal::database();
$query = $connection
->select('weather_forecast_information', 'wfi');
$query
->condition('wfi.geoid', 'geonames_2911298', '=');
$query
->fields('wfi', [
'geoid',
'last_update',
'next_update',
'next_download_attempt',
'utc_offset',
]);
$query
->range(0, 50);
$result = $query
->execute();
return $result
->fetch();
}
public function testParsingOfInformation() {
$info = $this
->getInfoAboutForecast(1381176000);
$this
->assertEquals('geonames_2911298', $info->geoid);
$this
->assertEquals('2013-10-07 15:30:00', $info->last_update);
$this
->assertEquals('2013-10-08 04:00:00', $info->next_update);
$this
->assertEquals('2013-10-08 04:00:00', $info->next_download_attempt);
$this
->assertEquals(120, $info->utc_offset);
}
public function testDifferentDaysOfForecasts() {
$days = [
'2013-10-07',
'2013-10-08',
'2013-10-09',
'2013-10-10',
'2013-10-11',
'2013-10-12',
'2013-10-13',
'2013-10-14',
'2013-10-15',
'2013-10-16',
'2013-10-17',
];
$config = \Drupal::configFactory()
->getEditable('weather.settings');
$config
->set('weather_time_for_testing', 1381176000)
->save();
$weather = $this
->weatherGetWeather('geonames_2911298', 0, TRUE);
$this
->assertSame(array_keys($weather['forecasts']), $days);
$config = \Drupal::configFactory()
->getEditable('weather.settings');
$config
->set('weather_time_for_testing', 1381572000)
->save();
$weather = $this
->weatherGetWeather('geonames_2911298', 0, TRUE);
$this
->assertSame(array_keys($weather['forecasts']), array_slice($days, 5));
}
}