Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 5 additions & 25 deletions install/install/create_automatic_actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,30 +65,6 @@
'param' => 10000, // Maximum rows to generate per execution
],
],
// [
// 'itemtype' => CronTask::class,
// 'name' => 'DownloadRte',
// 'frequency' => DAY_TIMESTAMP,
// 'options' => [
// 'mode' => GlpiCronTask::MODE_EXTERNAL,
// 'allowmode' => GlpiCronTask::MODE_INTERNAL + GlpiCronTask::MODE_EXTERNAL,
// 'logs_lifetime' => 30,
// 'comment' => __('Collect carbon intensities from RTE', 'carbon'),
// 'param' => 10000, // Maximum rows to generate per execution
// ]
// ],
// [
// 'itemtype' => CronTask::class,
// 'name' => 'DownloadElectricityMap',
// 'frequency' => DAY_TIMESTAMP / 2, // Twice a day
// 'options' => [
// 'mode' => GlpiCronTask::MODE_EXTERNAL,
// 'allowmode' => GlpiCronTask::MODE_INTERNAL + GlpiCronTask::MODE_EXTERNAL,
// 'logs_lifetime' => 30,
// 'comment' => __('Collect carbon intensities from ElectricityMap', 'carbon'),
// 'param' => 10000, // Maximum rows to generate per execution
// ]
// ],
[
'itemtype' => CronTask::class,
'name' => 'EmbodiedImpact',
Expand All @@ -105,7 +81,11 @@

foreach ($automatic_actions as $action) {
$task = new GlpiCronTask();
if ($task->getFromDBByCrit(['name' => $action['name']]) !== false) {
$crit = [
'itemtype' => $action['itemtype'],
'name' => $action['name'],
];
if ($task->getFromDBByCrit($crit) !== false) {
$task->delete(['id' => $task->getID()]);
}
$success = GlpiCronTask::Register(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
$task->update([
'id' => $task->getID(),
'itemtype' => 'GlpiPlugin\\Carbon\\DataSource\\CarbonIntensity\\Rte\\CronTask',
'name' => 'Download',
]);
}

Expand All @@ -53,5 +54,6 @@
$task->update([
'id' => $task->getID(),
'itemtype' => 'GlpiPlugin\\Carbon\\DataSource\\CarbonIntensity\\ElectricityMaps\\CronTask',
'name' => 'Download',
]);
}
124 changes: 52 additions & 72 deletions src/CarbonIntensity.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
use Exception;
use Glpi\DBAL\QueryParam;
use GlpiPlugin\Carbon\DataSource\CarbonIntensity\ClientInterface;
use RuntimeException;
use Symfony\Component\Console\Helper\ProgressBar;

/**
Expand Down Expand Up @@ -124,15 +123,16 @@ public function rawSearchOptions()
/**
* get carbon intensity dates for a source and a zone
*
* @param string $zone_name Zone to examinate
* @param string $source_name Source to examinate
* @param Source_Zone $source_zone Source_Zone to examinate
* @return array
*/
private function getKnownDatesQuery(string $zone_name, string $source_name)
private function getKnownDatesQuery(Source_Zone $source_zone)
{
$intensity_table = CarbonIntensity::getTable();
$source_table = Source::getTable();
$zone_table = Zone::getTable();
$source_fk = getForeignKeyFieldForItemType(Source::class);
$zone_fk = getForeignKeyFieldForItemType(Zone::class);
return [
'SELECT' => [$intensity_table => ['id', 'date']],
'FROM' => $intensity_table,
Expand All @@ -151,25 +151,24 @@ private function getKnownDatesQuery(string $zone_name, string $source_name)
],
],
'WHERE' => [
Source::getTableField('name') => $source_name,
Zone::getTableField('name') => $zone_name,
$source_fk => $source_zone->fields[$source_fk],
$zone_fk => $source_zone->fields[$zone_fk],
],
];
}

/**
* Get the last known date of carbon emissiosn
*
* @param string $zone_name Zone to examinate
* @param string $source_name Source to examinate
* @param Source_Zone $source_zone Source_Zone to examinate
* @return DateTimeImmutable
*/
public function getLastKnownDate(string $zone_name, string $source_name): ?DateTimeImmutable
public function getLastKnownDate(Source_Zone $source_zone): ?DateTimeImmutable
{
/** @var DBmysql $DB */
global $DB;

$request = $this->getKnownDatesQuery($zone_name, $source_name);
$request = $this->getKnownDatesQuery($source_zone);
$request['ORDER'] = CarbonIntensity::getTableField('date') . ' DESC';
$request['LIMIT'] = '1';
$result = $DB->request($request)->current();
Expand All @@ -182,16 +181,15 @@ public function getLastKnownDate(string $zone_name, string $source_name): ?DateT
/**
* Get the first known date of carbon emissiosn
*
* @param string $zone_name Zone to examinate
* @param string $source_name Source to examinate
* @param Source_Zone $source_zone
* @return DateTimeImmutable
*/
public function getFirstKnownDate(string $zone_name, string $source_name): ?DateTimeImmutable
public function getFirstKnownDate(Source_Zone $source_zone): ?DateTimeImmutable
{
/** @var DBmysql $DB */
global $DB;

$request = $this->getKnownDatesQuery($zone_name, $source_name);
$request = $this->getKnownDatesQuery($source_zone);
$request['ORDER'] = CarbonIntensity::getTableField('date') . ' ASC';
$request['LIMIT'] = '1';
$result = $DB->request($request)->current();
Expand All @@ -206,29 +204,27 @@ public function getFirstKnownDate(string $zone_name, string $source_name): ?Date
* Download data for a single zone
*
* @param ClientInterface $data_source
* @param string $zone_name zone name
* @param Source_Zone $source_zone
* @param int $limit maximum count of items to process
* @param ProgressBar $progress_bar progress bar to update (CLI mode only)
* @return int count of item downloaded
*/
public function downloadOneZone(ClientInterface $data_source, string $zone_name, int $limit = 0, ?ProgressBar $progress_bar = null): int
public function downloadOneZone(ClientInterface $data_source, Source_Zone $source_zone, int $limit = 0, ?ProgressBar $progress_bar = null): int
{
$start_date = $this->getDownloadStartDate();
$start_date = max($this->getDownloadStartDate(), $data_source->getHardStartDate());

$total_count = 0;

// Check if there are gaps to fill
$source = new Source();
$source->getFromDBByCrit(['name' => $data_source->getSourceName()]);
$zone = new Zone();
$zone->getFromDBByCrit(['name' => $zone_name]);
$gaps = $this->findGaps($source->getID(), $zone->getID(), $start_date);
$gaps = $this->findGaps($source_zone, $start_date);
if (count($gaps) === 0) {
// Log a notice specifying the source and the zone
$zone = new Zone();
$zone->getFromDBByCrit(['id' => $source_zone->fields[Zone::getForeignKeyField()]]);
trigger_error(sprintf(
"No gap to fill for source %s and zone %s between %s and %s",
$data_source->getSourceName(),
$zone_name,
$zone->fields['name'],
$start_date->format('Y-m-d'),
'now'
), E_USER_WARNING);
Expand All @@ -249,7 +245,7 @@ public function downloadOneZone(ClientInterface $data_source, string $zone_name,
foreach ($gaps as $gap) {
$gap_start = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $gap['start']);
$gap_end = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $gap['end']);
$count = $data_source->fullDownload($zone_name, $gap_start, $gap_end, $this, $limit, $progress_bar);
$count = $data_source->fullDownload($source_zone, $gap_start, $gap_end, $this, $limit, $progress_bar);
$total_count += $count;
if ($total_count >= $limit) {
return $total_count;
Expand Down Expand Up @@ -281,65 +277,48 @@ public function getDownloadStartDate(): ?DateTimeImmutable
return $start_date;
}

/**
* Get date where data download shall end, excluding the incremental download mode for the specified data source
*
* @param string $zone_name zone to examine
* @param ClientInterface $data_source data source
* @return DateTimeImmutable
*/
public function getDownloadStopDate(string $zone_name, ClientInterface $data_source): DateTimeImmutable
{
$stop_date = $data_source->getMaxIncrementalAge();
$first_known_intensity_date = $this->getFirstKnownDate($zone_name, $data_source->getSourceName());
if ($first_known_intensity_date !== null) {
$first_known_intensity_date = $first_known_intensity_date->sub(new DateInterval('PT1H'));
$stop_date = min($stop_date, $first_known_intensity_date);
}

return $stop_date;
}
// /**
// * Get date where data download shall end, excluding the incremental download mode for the specified data source
// *
// * @param string $zone_name zone to examine
// * @param ClientInterface $data_source data source
// * @return DateTimeImmutable
// */
// public function getDownloadStopDate(string $zone_name, ClientInterface $data_source): DateTimeImmutable
// {
// $stop_date = $data_source->getMaxIncrementalAge();
// $first_known_intensity_date = $this->getFirstKnownDate($zone_name, $data_source->getSourceName());
// if ($first_known_intensity_date !== null) {
// $first_known_intensity_date = $first_known_intensity_date->sub(new DateInterval('PT1H'));
// $stop_date = min($stop_date, $first_known_intensity_date);
// }

// return $stop_date;
// }

/**
* Save in database the carbon intensities
* Give up on failures
*
* @param string $zone_name name of the zone to store intensities
* @param string $source_name name of the source to store intensities
* @param Source_Zone $source_zone Source_zone
* @param array $data as an array of arrays ['datetime' => string, 'intensity' => float]
* @return int count of actually saved items,
*/
public function save(string $zone_name, string $source_name, array $data): int
public function save(Source_Zone $source_zone, array $data): int
{
/** @var DBmysql $DB */
global $DB;

$count = 0;
$source = new Source();
$source->getFromDBByCrit([
'name' => $source_name,
]);
if ($source->isNewItem()) {
throw new RuntimeException('Attempt to save carbon intensity with a source which is not in the database');
// trigger_error('Attempt to save carbon intensity with a source which is not in the database', E_USER_ERROR);
// return 0;
}
$zone = new Zone();
$zone->getFromDBByCrit([
'name' => $zone_name,
]);
if ($zone->isNewItem()) {
throw new RuntimeException('Attempt to save carbon intensity with a zone which is not in the database');
// trigger_error('Attempt to save carbon intensity with a zone which is not in the database', E_USER_ERROR);
// return 0;
}

$source_fk = getForeignKeyFieldForItemType(Source::class);
$zone_fk = getForeignKeyFieldForItemType(Zone::class);
$query = $DB->buildInsert(
CarbonIntensity::getTable(),
[
'date' => new QueryParam(),
Source::getForeignKeyField() => new QueryParam(),
Zone::getForeignKeyField() => new QueryParam(),
$source_fk => new QueryParam(),
$zone_fk => new QueryParam(),
'intensity' => new QueryParam(),
'data_quality' => new QueryParam(),
],
Expand All @@ -351,8 +330,8 @@ public function save(string $zone_name, string $source_name, array $data): int
$stmt->bind_param(
'siidi',
$intensity['datetime'],
$source->fields['id'],
$zone->fields['id'],
$source_zone->fields[$source_fk],
$source_zone->fields[$zone_fk],
$intensity['intensity'],
$intensity['data_quality']
);
Expand All @@ -371,17 +350,18 @@ public function save(string $zone_name, string $source_name, array $data): int
/**
* Gets date intervals where data are missing
*
* @param int $source_id
* @param int $zone_id
* @param Source_Zone $source_zone
* @param DateTimeInterface $start
* @param DateTimeInterface|null $stop
* @return array
*/
public function findGaps(int $source_id, int $zone_id, DateTimeInterface $start, ?DateTimeInterface $stop = null): array
public function findGaps(Source_Zone $source_zone, DateTimeInterface $start, ?DateTimeInterface $stop = null): array
{
$source_fk = getForeignKeyFieldForItemType(Source::class);
$zone_fk = getForeignKeyFieldForItemType(Zone::class);
$criteria = [
Source::getForeignKeyField() => $source_id,
Zone::getForeignKeyField() => $zone_id,
$source_fk => $source_zone->fields[$source_fk],
$zone_fk => $source_zone->fields[$zone_fk],
];
$interval = new DateInterval('PT1H');
return Toolbox::findTemporalGapsInTable(self::getTable(), $start, $interval, $stop, $criteria);
Expand Down
33 changes: 13 additions & 20 deletions src/Command/CollectCarbonIntensityCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
class CollectCarbonIntensityCommand extends AbstractCommand
{
/** @var int ID of the data source being processed */
private int $source_id;
/** @var Source_Zone The relatin between a source and a zone to describe which data to download and save */
private Source_Zone $source_zone;
private ?ClientInterface $client = null;
private array $zones = [];

Expand Down Expand Up @@ -122,20 +123,15 @@
$message = __('Creating data source name', 'carbon');
$output->writeln("<info>$message</info>");

// Create the source if it does not exist
// Check the source exists
$data_source = new Source();
$source_name = $input->getArgument('source');
if (!$data_source->getFromDBByCrit(['name' => $source_name])) {
$data_source->add([
'name' => $source_name,
]);
if ($data_source->isNewItem()) {
$message = __("Source not found", 'carbon');
$output->writeln("<error>$message</error>");
return Command::FAILURE;
}
$message = __("This source does not exist", 'casrbon');
$output->writeln("<error>$message</error>");
return Command::FAILURE;
}
$this->source_id = $data_source->getID();

Check failure on line 134 in src/Command/CollectCarbonIntensityCommand.php

View workflow job for this annotation

GitHub Actions / GLPI 11.0.x - php:8.5 - mariadb:11.8 / Continuous integration

Access to an undefined property GlpiPlugin\Carbon\Command\CollectCarbonIntensityCommand::$source_id.

Check failure on line 134 in src/Command/CollectCarbonIntensityCommand.php

View workflow job for this annotation

GitHub Actions / GLPI 11.0.x - php:8.2 - mariadb:10.6 / Continuous integration

Access to an undefined property GlpiPlugin\Carbon\Command\CollectCarbonIntensityCommand::$source_id.

$zone_code = $input->getArgument('zone');
$use_cache = $input->getOption('cache');
Expand All @@ -143,22 +139,19 @@
$zone->getFromDBByCrit(['name' => $this->zones[$zone_code]]);
$carbon_intensity = new CarbonIntensity();

// Create relation between source and zone if t does not exist
// Check the relation between source and zone
$source_zone = new Source_Zone();
$input = [
$data_source::getForeignKeyField() => $data_source->getID(),
$zone::getForeignKeyField() => $zone->getID(),
];
$source_zone->getFromDbByCrit($input);
if ($source_zone->isNewItem()) {
$input['is_download_enabled'] = 1;
$input['code'] = $zone_code;
if ($source_zone->add($input) === false) {
$message = __("Creation of relation between source and zone failed", 'carbon');
$output->writeln("<error>$message</error>");
return Command::FAILURE;
}
$message = __("The zone is not handled by the data source", 'casrbon');
$output->writeln("<error>$message</error>");
return Command::FAILURE;
}
$this->source_zone = $source_zone;

$message = __("Reading data...", 'carbon');
$output->writeln("<info>$message</info>");
Expand All @@ -172,11 +165,11 @@
$this->client->disableCache();
}

$carbon_intensity->downloadOneZone($this->client, $this->zones[$zone_code], 0, new ProgressBar($this->output));
$carbon_intensity->downloadOneZone($this->client, $this->source_zone, 0, new ProgressBar($this->output));

// Find start and stop dates to cover
$start_date = $carbon_intensity->getDownloadStartDate();
$gaps = $carbon_intensity->findGaps($this->source_id, $zone->getID(), $start_date);
$gaps = $carbon_intensity->findGaps($this->source_zone, $start_date);

// Count the hours not covered by any sample
$not_downlaoded_hours = 0;
Expand Down
Loading
Loading