-<?php
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of the Rapsys AirBundle package.
+ *
+ * (c) Raphaël Gertz <symfony@rapsys.eu>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
namespace Rapsys\AirBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Command\DoctrineCommand;
+use Doctrine\Persistence\ManagerRegistry;
+
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
+use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
+
use Rapsys\AirBundle\Entity\Session;
class WeatherCommand extends DoctrineCommand {
//Hourly uri
'hourly' => [
75001 => 'https://www.accuweather.com/en/fr/paris-01-louvre/75001/hourly-weather-forecast/179142_pc?day=',
+ 75004 => 'https://www.accuweather.com/en/fr/paris-04-hotel-de-ville/75004/hourly-weather-forecast/179145_pc?day=',
75005 => 'https://www.accuweather.com/en/fr/paris-05-pantheon/75005/hourly-weather-forecast/179146_pc?day=',
+ 75006 => 'https://www.accuweather.com/fr/fr/paris-06-luxembourg/75006/hourly-weather-forecast/179147_pc?day=',
75007 => 'https://www.accuweather.com/en/fr/paris-07-palais-bourbon/75007/hourly-weather-forecast/179148_pc?day=',
75009 => 'https://www.accuweather.com/en/fr/paris-09-opera/75009/hourly-weather-forecast/179150_pc?day=',
+ 75010 => 'https://www.accuweather.com/en/fr/paris-10-entrepot/75010/hourly-weather-forecast/179151_pc?day=',
+ 75012 => 'https://www.accuweather.com/en/fr/paris-12-reuilly/75012/hourly-weather-forecast/179153_pc?day=',
+ 75013 => 'https://www.accuweather.com/en/fr/paris-13-gobelins/75013/hourly-weather-forecast/179154_pc?day=',
75015 => 'https://www.accuweather.com/en/fr/paris-15-vaugirard/75015/hourly-weather-forecast/179156_pc?day=',
75019 => 'https://www.accuweather.com/en/fr/paris-19-buttes-chaumont/75019/hourly-weather-forecast/179160_pc?day=',
75116 => 'https://www.accuweather.com/en/fr/paris-16-passy/75116/hourly-weather-forecast/179246_pc?day='
//Daily uri
'daily' => [
75001 => 'https://www.accuweather.com/en/fr/paris-01-louvre/75001/daily-weather-forecast/179142_pc',
+ 75004 => 'https://www.accuweather.com/en/fr/paris-04-hotel-de-ville/75004/daily-weather-forecast/179145_pc',
75005 => 'https://www.accuweather.com/en/fr/paris-05-pantheon/75005/daily-weather-forecast/179146_pc',
+ 75006 => 'https://www.accuweather.com/fr/fr/paris-06-luxembourg/75006/daily-weather-forecast/179147_pc',
75007 => 'https://www.accuweather.com/en/fr/paris-07-palais-bourbon/75007/daily-weather-forecast/179148_pc',
75009 => 'https://www.accuweather.com/en/fr/paris-09-opera/75009/daily-weather-forecast/179150_pc',
+ 75010 => 'https://www.accuweather.com/en/fr/paris-10-entrepot/75010/daily-weather-forecast/179151_pc',
+ 75012 => 'https://www.accuweather.com/en/fr/paris-12-reuilly/75012/daily-weather-forecast/179153_pc',
+ 75013 => 'https://www.accuweather.com/en/fr/paris-13-gobelins/75013/daily-weather-forecast/179154_pc',
75015 => 'https://www.accuweather.com/en/fr/paris-15-vaugirard/75015/daily-weather-forecast/179156_pc',
75019 => 'https://www.accuweather.com/en/fr/paris-19-buttes-chaumont/75019/daily-weather-forecast/179160_pc',
75116 => 'https://www.accuweather.com/en/fr/paris-16-passy/75116/daily-weather-forecast/179246_pc'
///Set curl handler
private $ch = null;
+ ///Set manager registry
+ private $doctrine;
+
+ ///Set filesystem
+ private $filesystem;
+
+ ///Weather command constructor
+ public function __construct(ManagerRegistry $doctrine, Filesystem $filesystem) {
+ parent::__construct($doctrine);
+
+ //Set entity manager
+ $this->doctrine = $doctrine;
+
+ //Set filesystem
+ $this->filesystem = $filesystem;
+ }
+
///Configure attribute command
protected function configure() {
//Configure the class
}
///Process the attribution
- protected function execute(InputInterface $input, OutputInterface $output) {
- //Fetch doctrine
- $doctrine = $this->getDoctrine();
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ //Kernel object
+ $kernel = $this->getApplication()->getKernel();
+
+ //Tmp directory
+ $tmpdir = $kernel->getContainer()->getParameter('kernel.project_dir').'/var/cache/weather';
+
+ //Set tmpdir
+ //XXX: worst case scenario we have 3 files per zipcode plus daily
+ if (!is_dir($tmpdir)) {
+ try {
+ //Create dir
+ $this->filesystem->mkdir($tmpdir, 0775);
+ } catch (IOException $exception) {
+ //Display error
+ echo 'Create dir '.$exception->getPath().' failed'."\n";
+
+ //Exit with failure
+ exit(self::FAILURE);
+ }
+ }
- //Get manager
- $manager = $doctrine->getManager();
+ //Cleanup kernel
+ unset($kernel);
//Tidy object
$tidy = new \tidy();
//Process hourly accuweather
if (($command = $input->getFirstArgument()) == 'rapsysair:weather:hourly' || $command == 'rapsysair:weather') {
//Fetch hourly sessions to attribute
- $types['hourly'] = $doctrine->getRepository(Session::class)->findAllPendingHourlyWeather();
+ $types['hourly'] = $this->doctrine->getRepository(Session::class)->findAllPendingHourlyWeather();
//Iterate on each session
foreach($types['hourly'] as $sessionId => $session) {
//Process daily accuweather
if ($command == 'rapsysair:weather:daily' || $command == 'rapsysair:weather') {
//Fetch daily sessions to attribute
- $types['daily'] = $doctrine->getRepository(Session::class)->findAllPendingDailyWeather();
+ $types['daily'] = $this->doctrine->getRepository(Session::class)->findAllPendingDailyWeather();
//Iterate on each session
foreach($types['daily'] as $sessionId => $session) {
}
}
- //Get filesystem
- $filesystem = new Filesystem();
-
- //Set tmpdir
- //XXX: worst case scenario we have 3 files per zipcode
- if (!is_dir($tmpdir = sys_get_temp_dir().'/accuweather')) {
- try {
- //Create dir
- $filesystem->mkdir($tmpdir, 0775);
- } catch (IOExceptionInterface $exception) {
- //Display error
- echo 'Create dir '.$exception->getPath().' failed'."\n";
-
- //Exit with failure
- exit(self::FAILURE);
- }
- }
-
//Init curl
$this->curl_init();
//Load simplexml
//XXX: trash all xmlns= broken tags
- $sx = new \SimpleXMLElement(str_replace(['xmlns=', 'xlink:href='], ['xns=', 'href='], $tidy));
+ $sx = new \SimpleXMLElement(str_replace(['xmlns=', 'xlink:href='], ['xns=', 'href='], (string)$tidy));
//Process daily
if ($day == 'daily') {
//Iterate on each link containing data
- foreach($sx->xpath('//a[@class="daily-forecast-card"]') as $node) {
+ foreach($sx->xpath('//a[contains(@class,"daily-forecast-card")]') as $node) {
//Get date
$dsm = trim($node->div[0]->h2[0]->span[1]);
//Get temperature
- $temperature = str_replace('°', '', $node->div[0]->div[0]->span[0]);
+ $temperature = str_replace('°', '', (string)$node->div[0]->div[0]->span[0]);
//Get rainrisk
- $rainrisk = str_replace('%', '', trim($node->div[2]))/100;
+ $rainrisk = trim(str_replace('%', '', (string)$node->div[1]))/100;
//Store data
$data[$zipcode][$dsm]['daily'] = [
#/html/body/div[1]/div[5]/div[1]/div[1]/div[1]/div[1]/div[1]/div/h2/span[1]
foreach($sx->xpath('//div[@data-shared="false"]') as $node) {
//Get hour
- $hour = trim($node->div[0]->div[0]->h2[0]->span[0]);
+ $hour = trim(str_replace(' h', '', (string)$node->div[0]->div[0]->div[0]->div[0]->div[0]->h2[0]));
- //Get dsm
- $dsm = trim($node->div[0]->div[0]->h2[0]->span[1]);
+ //Compute dsm from day (1=d,2=d+1,3=d+2)
+ $dsm = (new \DateTime('+'.($day - 1).' day'))->format('d/m');
//Get temperature
- $temperature = str_replace('°', '', $node->div[0]->div[0]->div[0]);
+ $temperature = str_replace('°', '', (string)$node->div[0]->div[0]->div[0]->div[0]->div[1]);
//Get realfeel
- $realfeel = str_replace(['RealFeel® ', '°'], '', trim($node->div[0]->div[0]->span[0]));
+ $realfeel = trim(str_replace(['RealFeel®', '°'], '', (string)$node->div[0]->div[0]->div[0]->div[1]->div[0]->div[0]->div[0]));
//Get rainrisk
- $rainrisk = str_replace('%', '', trim($node->div[0]->div[0]->div[1]))/100;
-
- //Label is Rain when we have a rainfall
- if (($pluviolabel = trim($node->div[1]->div[0]->div[0]->div[1]->p[1])) == 'Rain') {
- //Get rainfall
- $rainfall = str_replace(' mm', '', $node->div[1]->div[0]->div[0]->div[1]->p[1]->span[0]);
- //Cloud Cover, no rainfall
- } else {
- //Set rainfall to 0 (mm)
- $rainfall = 0;
+ $rainrisk = floatval(str_replace('%', '', trim((string)$node->div[0]->div[0]->div[0]->div[2]->div[0]))/100);
+
+ //Set rainfall to 0 (mm)
+ $rainfall = 0;
+
+ //Iterate on each entry
+ //TODO: wind and other infos are present in $node->div[1]->div[0]->div[1]->div[0]->p
+ foreach($node->div[1]->div[0]->div[1]->div[0]->p as $p) {
+ //Lookup for rain entry if present
+ if (in_array(trim((string)$p), ['Rain', 'Pluie'])) {
+ //Get rainfall
+ $rainfall = floatval(str_replace(' mm', '', (string)$p->span[0]));
+ }
}
//Store data
$hour = $type=='daily'?$type:$time->format('H');
//Check data availability
- //XXX: should never happen
- #if (!isset($data[$zipcode][$dsm][$hour])) {
- # //Skip unavailable data
- # continue;
- #}
+ //XXX: sometimes startup delay causes weather data to be unavailable for session first hour
+ if (!isset($data[$zipcode][$dsm][$hour])) {
+ //Skip unavailable data
+ continue;
+ }
//Set info alias
$info = $data[$zipcode][$dsm][$hour];
//Check if realfeel differ
if ($session->getRealfeel() !== $realfeel) {
//Set average realfeel
- #$meteo['realfeel'] = array_sum($meteo['realfeel'])/count($meteo['realfeel']);
$session->setRealfeel($realfeel);
}
//Check if temperature differ
if ($session->getTemperature() !== $temperature) {
//Set average temperature
- #$meteo['temperature'] = array_sum($meteo['temperature'])/count($meteo['temperature']);
$session->setTemperature($temperature);
}
}
//Flush to get the ids
- $manager->flush();
+ $this->doctrine->getManager()->flush();
//Close curl handler
$this->curl_close();