Skip to content

Commit 59b5cf8

Browse files
committed
fix(Datasource\CarbonIntensity\ElectricityMaps): factorize and fix date management
1 parent 7d7ac09 commit 59b5cf8

12 files changed

Lines changed: 96 additions & 89 deletions

File tree

install/install/create_automatic_actions.php

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,30 +65,6 @@
6565
'param' => 10000, // Maximum rows to generate per execution
6666
],
6767
],
68-
// [
69-
// 'itemtype' => CronTask::class,
70-
// 'name' => 'DownloadRte',
71-
// 'frequency' => DAY_TIMESTAMP,
72-
// 'options' => [
73-
// 'mode' => GlpiCronTask::MODE_EXTERNAL,
74-
// 'allowmode' => GlpiCronTask::MODE_INTERNAL + GlpiCronTask::MODE_EXTERNAL,
75-
// 'logs_lifetime' => 30,
76-
// 'comment' => __('Collect carbon intensities from RTE', 'carbon'),
77-
// 'param' => 10000, // Maximum rows to generate per execution
78-
// ]
79-
// ],
80-
// [
81-
// 'itemtype' => CronTask::class,
82-
// 'name' => 'DownloadElectricityMap',
83-
// 'frequency' => DAY_TIMESTAMP / 2, // Twice a day
84-
// 'options' => [
85-
// 'mode' => GlpiCronTask::MODE_EXTERNAL,
86-
// 'allowmode' => GlpiCronTask::MODE_INTERNAL + GlpiCronTask::MODE_EXTERNAL,
87-
// 'logs_lifetime' => 30,
88-
// 'comment' => __('Collect carbon intensities from ElectricityMap', 'carbon'),
89-
// 'param' => 10000, // Maximum rows to generate per execution
90-
// ]
91-
// ],
9268
[
9369
'itemtype' => CronTask::class,
9470
'name' => 'EmbodiedImpact',
@@ -105,7 +81,11 @@
10581

10682
foreach ($automatic_actions as $action) {
10783
$task = new GlpiCronTask();
108-
if ($task->getFromDBByCrit(['name' => $action['name']]) !== false) {
84+
$crit = [
85+
'itemtype' => $action['itemtype'],
86+
'name' => $action['name'],
87+
];
88+
if ($task->getFromDBByCrit($crit) !== false) {
10989
$task->delete(['id' => $task->getID()]);
11090
}
11191
$success = GlpiCronTask::Register(

install/migration/update_1.1.1_to_1.2.0/06_migrate_automatic_actions.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
$task->update([
4242
'id' => $task->getID(),
4343
'itemtype' => 'GlpiPlugin\\Carbon\\DataSource\\CarbonIntensity\\Rte\\CronTask',
44+
'name' => 'Download',
4445
]);
4546
}
4647

@@ -53,5 +54,6 @@
5354
$task->update([
5455
'id' => $task->getID(),
5556
'itemtype' => 'GlpiPlugin\\Carbon\\DataSource\\CarbonIntensity\\ElectricityMaps\\CronTask',
57+
'name' => 'Download',
5658
]);
5759
}

src/CarbonIntensity.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,11 @@ public function getDownloadStartDate(): ?DateTimeImmutable
300300
* Save in database the carbon intensities
301301
* Give up on failures
302302
*
303-
* @param Source_zone $source_zone Source_zone
303+
* @param Source_Zone $source_zone Source_zone
304304
* @param array $data as an array of arrays ['datetime' => string, 'intensity' => float]
305305
* @return int count of actually saved items,
306306
*/
307-
public function save(Source_zone $source_zone, array $data): int
307+
public function save(Source_Zone $source_zone, array $data): int
308308
{
309309
/** @var DBmysql $DB */
310310
global $DB;

src/Command/CollectCarbonIntensityCommand.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
class CollectCarbonIntensityCommand extends AbstractCommand
5959
{
6060
/** @var int ID of the data source being processed */
61-
private int $source_id;
6261
/** @var Source_Zone The relatin between a source and a zone to describe which data to download and save */
6362
private Source_Zone $source_zone;
6463
private ?ClientInterface $client = null;
@@ -132,7 +131,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
132131
$output->writeln("<error>$message</error>");
133132
return Command::FAILURE;
134133
}
135-
$this->source_id = $data_source->getID();
136134

137135
$zone_code = $input->getArgument('zone');
138136
$use_cache = $input->getOption('cache');

src/DataSource/AbstractCronTask.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,36 @@
3232

3333
namespace GlpiPlugin\Carbon\DataSource;
3434

35+
use CommonDBTM;
3536
use CommonGLPI;
37+
use GlpiPlugin\Carbon\DataSource\CarbonIntensity\ClientFactory;
38+
use GlpiPlugin\Carbon\Source_Zone;
3639

3740
abstract class AbstractCronTask extends CommonGLPI implements CronTaskInterface
3841
{
42+
protected static string $client_name;
43+
3944
public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0)
4045
{
4146
return '';
4247
}
48+
49+
public function showForCronTask(CommonDBTM $item)
50+
{
51+
switch ($item->fields['name']) {
52+
case 'Download':
53+
$client = ClientFactory::create(static::$client_name);
54+
$source_name = $client->getSourceName();
55+
foreach ($client->getSupportedZones() as $zone_name) {
56+
$source_zone = new Source_Zone();
57+
if (!$source_zone->getFromDbBySourceAndZone($source_name, $zone_name)) {
58+
continue;
59+
}
60+
if (!$source_zone->fields['is_download_enabled']) {
61+
continue;
62+
}
63+
$source_zone->showGaps();
64+
}
65+
}
66+
}
4367
}

src/DataSource/CarbonIntensity/AbstractClient.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ abstract public function getDataInterval(): string;
5858

5959
abstract protected function formatOutput(array $response, int $step): array;
6060

61+
abstract public function getHardStartDate(): DateTimeImmutable;
62+
6163
/**
6264
* Download all data for a single day from the datasource
6365
*

src/DataSource/CarbonIntensity/ElectricityMaps/Client.php

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use DateTimeImmutable;
3838
use DateTimeInterface;
3939
use DateTimeZone;
40+
use DBmysql;
4041
use GLPIKey;
4142
use GlpiPlugin\Carbon\CarbonIntensity;
4243
use GlpiPlugin\Carbon\DataSource\CarbonIntensity\AbortException;
@@ -269,6 +270,7 @@ public function fetchDay(DateTimeImmutable $day, Source_Zone $source_zone): arra
269270

270271
public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, Source_Zone $source_zone): array
271272
{
273+
$this->step = 60;
272274
$is_free_plan = Config::getConfigurationValue('electricitymap_fake_data') ?? 0;
273275
$dataset = $is_free_plan ? 'fake' : 'real';
274276
$zone = new Zone();
@@ -277,7 +279,8 @@ public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, So
277279
$cache_file = $this->getCacheFilename(
278280
$base_path,
279281
$start,
280-
$stop
282+
$stop,
283+
$start->getTimezone()
281284
);
282285
// If cached file exists, use it
283286
if (file_exists($cache_file)) {
@@ -306,6 +309,7 @@ public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, So
306309
$glpikey = new GLPIKey();
307310
$api_key = Config::getConfigurationValue('electricitymap_api_key');
308311
$api_key = $glpikey->decrypt($api_key);
312+
$format = 'Y-m-d\+H:ip';
309313
while ($current_date < $request_stop) {
310314
$stop = clone $current_date;
311315
$stop->add($step);
@@ -319,8 +323,8 @@ public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, So
319323
// ];
320324
$url = $this->base_url . self::PAST_URL;
321325
$url .= '?zone=' . $source_zone->fields['code'];
322-
$url .= '&start=' . $current_date->format('Y-m-d\+H:i');
323-
$url .= '&end=' . $stop->format('Y-m-d\+H:i');
326+
$url .= '&start=' . $current_date->format($format);
327+
$url .= '&end=' . $stop->format($format);
324328
$url .= '&temporalGranularity=' . 'hourly';
325329
$url .= '&emissionFactorType=' . 'lifecycle';
326330
$response = $this->client->request('GET', $url, [/*'query' => $params,*/ 'headers' => ['auth-token' => $api_key]]);
@@ -360,10 +364,13 @@ public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, So
360364

361365
protected function formatOutput(array $response, int $step): array
362366
{
367+
// Convert string dates into datetime objects,
368+
// using timezone expressed as type Continent/City instead of offset
369+
// This is needed to detect later the switching to winter time
370+
$response = $this->shiftToLocalTimezone($response);
363371
$intensities = [];
364-
$timezone = new DateTimeZone('UTC');
365-
foreach ($response['history'] as $record) {
366-
$datetime = DateTime::createFromFormat('Y-m-d\TH:i:s+', $record['datetime'], $timezone);
372+
foreach ($response['data'] as $record) {
373+
$datetime = $record['datetime'];
367374
if (!$datetime instanceof DateTimeInterface) {
368375
var_dump(DateTime::getLastErrors());
369376
continue;
@@ -379,18 +386,42 @@ protected function formatOutput(array $response, int $step): array
379386
return $intensities;
380387
}
381388

389+
/**
390+
* convert dates to the timezone of GLPI
391+
*
392+
* @param array $response
393+
* @return array array of records: ['date_heure' => string, 'taux_co2' => number, 'datetime' => DateTime]
394+
*/
395+
protected function shiftToLocalTimezone(array $response): array
396+
{
397+
/** @var DBmysql $DB */
398+
global $DB;
399+
400+
$shifted_response = [];
401+
$local_timezone = new DateTimeZone($DB->guessTimezone());
402+
array_walk($response['data'], function ($item, $key) use (&$shifted_response, $local_timezone) {
403+
$shifted_date_object = DateTime::createFromFormat('Y-m-d\TH:i:s.vp', $item['datetime'])
404+
->setTimezone($local_timezone);
405+
$shifted_date_string = $shifted_date_object->format('Y-m-d H:i:sP');
406+
if (isset($shifted_response[$shifted_date_string]) && $shifted_response['carbonIntensity'] !== $item['carbonIntensity']) {
407+
trigger_error("Duplicate record with different carbon intensity detected.");
408+
}
409+
$item['datetime'] = $shifted_date_object;
410+
$shifted_response[$shifted_date_string] = $item;
411+
});
412+
413+
return ['zone' => $response['zone'], 'data' => $shifted_response];
414+
}
415+
382416
/**
383417
* Try to determine the data quality of record
384418
*
385419
* @param array $record
386-
* @return int
420+
* @return int see AbstractTracked::DATA_QUALITY_* constants
387421
*/
388422
protected function getDataQuality(array $record): int
389423
{
390-
$data_quality = 0;
391-
if (!$record['isEstimated']) {
392-
$data_quality = AbstractTracked::DATA_QUALITY_RAW_REAL_TIME_MEASUREMENT;
393-
}
424+
$data_quality = $record['isEstimated'] ? AbstractTracked::DATA_QUALITY_ESTIMATED : AbstractTracked::DATA_QUALITY_RAW_REAL_TIME_MEASUREMENT;
394425

395426
return $data_quality;
396427
}

src/DataSource/CarbonIntensity/ElectricityMaps/CronTask.php

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,18 @@
3232

3333
namespace GlpiPlugin\Carbon\DataSource\CarbonIntensity\ElectricityMaps;
3434

35-
use CommonDBTM;
3635
use CommonGLPI;
3736
use CronTask as GlpiCronTask;
3837
use GlpiPlugin\Carbon\CarbonIntensity;
3938
use GlpiPlugin\Carbon\CronTask as CarbonCronTask;
4039
use GlpiPlugin\Carbon\DataSource\AbstractCronTask;
4140
use GlpiPlugin\Carbon\DataSource\CarbonIntensity\ClientFactory;
4241
use GlpiPlugin\Carbon\DataSource\CronTaskInterface;
43-
use GlpiPlugin\Carbon\DataSource\RestApiClient;
44-
use GlpiPlugin\Carbon\Source_Zone;
4542

4643
class CronTask extends AbstractCronTask implements CronTaskInterface
4744
{
45+
protected static string $client_name = 'ElectricityMaps';
46+
4847
public static function getIcon()
4948
{
5049
return 'fa-solid fa-gears';
@@ -61,7 +60,7 @@ public static function enumerateTasks(): array
6160
return [
6261
[
6362
'itemtype' => self::class,
64-
'name' => 'DownloadElectricityMap',
63+
'name' => 'Download',
6564
'frequency' => DAY_TIMESTAMP / 2,
6665
'options' => [
6766
'mode' => GlpiCronTask::MODE_EXTERNAL,
@@ -83,7 +82,7 @@ public static function enumerateTasks(): array
8382
public static function cronInfo(string $name): array
8483
{
8584
switch ($name) {
86-
case 'DownloadElectricityMap':
85+
case 'Download':
8786
return [
8887
'description' => __('Download carbon emissions from Electricity Maps', 'carbon'),
8988
'parameter' => __('Maximum number of entries to download', 'carbon'),
@@ -97,25 +96,9 @@ public static function cronInfo(string $name): array
9796
*
9897
* @return int
9998
*/
100-
public static function cronDownloadElectricityMap(GlpiCronTask $task): int
99+
public static function cronDownload(GlpiCronTask $task): int
101100
{
102-
$client = ClientFactory::create('ElectricityMaps');
101+
$client = ClientFactory::create(static::$client_name);
103102
return CarbonCronTask::downloadCarbonIntensityFromSource($task, $client, new CarbonIntensity());
104103
}
105-
106-
public function showForCronTask(CommonDBTM $item)
107-
{
108-
switch ($item->fields['name']) {
109-
case 'DownloadElectricityMap':
110-
$client = new Client(new RestApiClient());
111-
$source_name = ($client)->getSourceName();
112-
foreach ($client->getSupportedZones() as $zone_name) {
113-
$source_zone = new Source_Zone();
114-
if (!$source_zone->getFromDbBySourceAndZone($source_name, $zone_name)) {
115-
continue;
116-
}
117-
$source_zone->showGaps();
118-
}
119-
}
120-
}
121104
}

src/DataSource/CarbonIntensity/Rte/Client.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ public function fetchRange(DateTimeImmutable $start, DateTimeImmutable $stop, So
317317
}
318318
} else {
319319
$downloaded_year_month = $start->format('Y-m');
320-
if (count($response) > 0 && $downloaded_year_month < date('Y-m')) {
320+
if ($downloaded_year_month < date('Y-m')) {
321321
// Cache only if the month being processed is older than the month of now
322322
$json = json_encode($response);
323323
file_put_contents($cache_file, $json);

src/DataSource/CarbonIntensity/Rte/CronTask.php

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,18 @@
3232

3333
namespace GlpiPlugin\Carbon\DataSource\CarbonIntensity\Rte;
3434

35-
use CommonDBTM;
3635
use CommonGLPI;
3736
use CronTask as GlpiCronTask;
3837
use GlpiPlugin\Carbon\CarbonIntensity;
3938
use GlpiPlugin\Carbon\CronTask as CarbonCronTask;
4039
use GlpiPlugin\Carbon\DataSource\AbstractCronTask;
4140
use GlpiPlugin\Carbon\DataSource\CarbonIntensity\ClientFactory;
4241
use GlpiPlugin\Carbon\DataSource\CronTaskInterface;
43-
use GlpiPlugin\Carbon\DataSource\RestApiClient;
44-
use GlpiPlugin\Carbon\Source_Zone;
4542

4643
class CronTask extends AbstractCronTask implements CronTaskInterface
4744
{
45+
protected static string $client_name = 'Rte';
46+
4847
public static function getIcon()
4948
{
5049
return 'fa-solid fa-gears';
@@ -55,29 +54,13 @@ public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0)
5554
return self::createTabEntry(__('Resource diagnosis', 'carbon'), 0);
5655
}
5756

58-
public function showForCronTask(CommonDBTM $item)
59-
{
60-
switch ($item->fields['name']) {
61-
case 'DownloadRte':
62-
$client = new Client(new RestApiClient());
63-
$source_name = ($client)->getSourceName();
64-
foreach ($client->getSupportedZones() as $zone_name) {
65-
$source_zone = new Source_Zone();
66-
if (!$source_zone->getFromDbBySourceAndZone($source_name, $zone_name)) {
67-
continue;
68-
}
69-
$source_zone->showGaps();
70-
}
71-
}
72-
}
73-
7457
public static function enumerateTasks(): array
7558
{
7659
// TODO: This data shoud replace the occurrence in CronTask::cronInfo()
7760
return [
7861
[
7962
'itemtype' => self::class,
80-
'name' => 'DownloadRte',
63+
'name' => 'Download',
8164
'frequency' => DAY_TIMESTAMP,
8265
'options' => [
8366
'mode' => GlpiCronTask::MODE_EXTERNAL,
@@ -99,7 +82,7 @@ public static function enumerateTasks(): array
9982
public static function cronInfo(string $name): array
10083
{
10184
switch ($name) {
102-
case 'DownloadRte':
85+
case 'Download':
10386
return [
10487
'description' => __('Download carbon emissions from RTE', 'carbon'),
10588
'parameter' => __('Maximum number of entries to download', 'carbon'),
@@ -113,9 +96,9 @@ public static function cronInfo(string $name): array
11396
*
11497
* @return int
11598
*/
116-
public static function cronDownloadRte(GlpiCronTask $task): int
99+
public static function cronDownload(GlpiCronTask $task): int
117100
{
118-
$client = ClientFactory::create('Rte');
101+
$client = ClientFactory::create(static::$client_name);
119102
return CarbonCronTask::downloadCarbonIntensityFromSource($task, $client, new CarbonIntensity());
120103
}
121104
}

0 commit comments

Comments
 (0)