diff --git a/install/Install.php b/install/Install.php index 34c1cebb..1ba40627 100644 --- a/install/Install.php +++ b/install/Install.php @@ -105,11 +105,18 @@ public function install(array $args = []): bool global $DB; $dbFile = plugin_carbon_getSchemaPath(); - if ($dbFile === null || !$DB->runFile($dbFile)) { + if ($dbFile === null) { $this->migration->addWarningMessage("Error creating tables : " . $DB->error()); return false; } + try { + $DB->runFile($dbFile); + } catch (\RuntimeException $e) { + $this->migration->addWarningMessage("Error creating tables : " . $e->getMessage()); + return false; + } + // Execute all install sub tasks $install_dir = __DIR__ . '/install/'; $update_scripts = scandir($install_dir); diff --git a/install/data/carbon_intensity/carbon-intensity-electricity.txt b/install/data/carbon_intensity/carbon-intensity-electricity.txt new file mode 100644 index 00000000..a776620c --- /dev/null +++ b/install/data/carbon_intensity/carbon-intensity-electricity.txt @@ -0,0 +1,2 @@ +Data source: +https://ourworldindata.org/grapher/carbon-intensity-electricity \ No newline at end of file diff --git a/install/install/init_datasources.php b/install/install/init_datasources.php index b7e18c64..cff83fc3 100644 --- a/install/install/init_datasources.php +++ b/install/install/init_datasources.php @@ -104,18 +104,18 @@ Install::linkSourceZone($source_id, $zone_id, $code); // Insert into the database - $success = $DB->updateOrInsert($table, [ - 'intensity' => $intensity, - 'data_quality' => 2 // constant GlpiPlugin\Carbon\DataTracking::DATA_QUALITY_ESTIMATED - ], [ - 'date' => "$year-01-01 00:00:00", - 'plugin_carbon_sources_id' => $source_id, - 'plugin_carbon_zones_id' => $zone_id, - ]); - - if ($success === false) { + try { + $DB->updateOrInsert($table, [ + 'intensity' => $intensity, + 'data_quality' => 2 // constant GlpiPlugin\Carbon\DataTracking::DATA_QUALITY_ESTIMATED + ], [ + 'date' => "$year-01-01 00:00:00", + 'plugin_carbon_sources_id' => $source_id, + 'plugin_carbon_zones_id' => $zone_id, + ]); + } catch (\RuntimeException $e) { $file = null; // close the file - throw new \RuntimeException("Failed to insert data for year $year"); + throw new \RuntimeException("Failed to insert data for year $year; reason: " . $e->getMessage(), $e->getCode(), $e); } } if ($progress_bar) { @@ -129,17 +129,17 @@ $quebec_carbon_intensity = include(dirname(__DIR__) . '/data/carbon_intensity/quebec.php'); foreach ($quebec_carbon_intensity as $year => $intensity) { - $success = $DB->updateOrInsert($table, [ - 'intensity' => $intensity, - 'data_quality' => 2 // constant GlpiPlugin\Carbon\DataTracking::DATA_QUALITY_ESTIMATED - ], [ - 'date' => "$year-01-01 00:00:00", - 'plugin_carbon_sources_id' => $source_id, - 'plugin_carbon_zones_id' => $zone_id_quebec, - ]); - - if ($success === false) { + try { + $DB->updateOrInsert($table, [ + 'intensity' => $intensity, + 'data_quality' => 2 // constant GlpiPlugin\Carbon\DataTracking::DATA_QUALITY_ESTIMATED + ], [ + 'date' => "$year-01-01 00:00:00", + 'plugin_carbon_sources_id' => $source_id, + 'plugin_carbon_zones_id' => $zone_id_quebec, + ]); + } catch (\RuntimeException $e) { $file = null; // close the file - throw new \RuntimeException("Failed to insert data for year $year"); + throw new \RuntimeException("Failed to insert data for year $year; reason: " . $e->getMessage(), $e->getCode(), $e); } } diff --git a/install/migration/update_1.0.0_to_1.0.1.php b/install/migration/update_1.0.0_to_1.0.1.php index 975bf076..9d2ff61a 100644 --- a/install/migration/update_1.0.0_to_1.0.1.php +++ b/install/migration/update_1.0.0_to_1.0.1.php @@ -57,10 +57,16 @@ function update100to101(Migration $migration) } $dbFile = plugin_carbon_getSchemaPath($to_version); - if ($dbFile === null || !$DB->runFile($dbFile)) { + if ($dbFile === null) { $migration->addWarningMessage("Error creating tables : " . $DB->error()); $updateresult = false; } + try { + $DB->runFile($dbFile); + } catch (\RuntimeException $e) { + $migration->addWarningMessage("Error creating tables : " . $e->getMessage()); + $updateresult = false; + } // ************ Keep it at the end ************** $migration->executeMigration(); diff --git a/install/migration/update_1.0.1_to_1.1.0.php b/install/migration/update_1.0.1_to_1.1.0.php index c32d549a..6998fce6 100644 --- a/install/migration/update_1.0.1_to_1.1.0.php +++ b/install/migration/update_1.0.1_to_1.1.0.php @@ -57,10 +57,17 @@ function update101to110(Migration $migration) } $dbFile = plugin_carbon_getSchemaPath($to_version); - if ($dbFile === null || !$DB->runFile($dbFile)) { + if ($dbFile === null) { $migration->addWarningMessage("Error creating tables : " . $DB->error()); $updateresult = false; } + try { + $DB->runFile($dbFile); + } catch (\RuntimeException $e) { + $migration->addWarningMessage("Error creating tables : " . $e->getMessage()); + $updateresult = false; + } + // ************ Keep it at the end ************** $migration->executeMigration(); diff --git a/install/migration/update_1.1.1_to_1.2.0.php b/install/migration/update_1.1.1_to_1.2.0.php index 4e62e6d3..d5ca343a 100644 --- a/install/migration/update_1.1.1_to_1.2.0.php +++ b/install/migration/update_1.1.1_to_1.2.0.php @@ -57,10 +57,17 @@ function update111to120(Migration $migration) } $dbFile = plugin_carbon_getSchemaPath($to_version); - if ($dbFile === null || !$DB->runFile($dbFile)) { + if ($dbFile === null) { $migration->addWarningMessage("Error creating tables : " . $DB->error()); $updateresult = false; } + try { + $DB->runFile($dbFile); + } catch (\RuntimeException $e) { + $migration->addWarningMessage("Error creating tables : " . $e->getMessage()); + $updateresult = false; + } + // ************ Keep it at the end ************** $migration->executeMigration(); diff --git a/install/migration/update_1.1.1_to_1.2.0/09_add_impact_criterias.php b/install/migration/update_1.1.1_to_1.2.0/09_add_impact_criterias.php new file mode 100644 index 00000000..4d718d4b --- /dev/null +++ b/install/migration/update_1.1.1_to_1.2.0/09_add_impact_criterias.php @@ -0,0 +1,118 @@ +. + * + * ------------------------------------------------------------------------- + */ + +/** @var DBmysql $DB */ +/** @var Migration $migration */ + +use Glpi\Dashboard\Item as DashboardItem; + +$new_criterias = [ + 'gwppb' => '(unit g CO2 eq) Climate change - Contribution of biogenic emissions', + 'gwppf' => '(unit g CO2 eq) Climate change - Contribution of fossil fuel emissions', + 'gwpplu' => '(unit g CO2 eq) Climate change - Contribution of emissions from land use change', + 'ir' => '(unit g U235 eq) Emissions of radionizing substances', + 'lu' => '(unit none) Land use', + 'odp' => '(unit g CFC-11 eq) Depletion of the ozone layer', + 'pm' => '(unit Disease occurrence) Fine particle emissions', + 'pocp' => '(unit g NMVOC eq) Photochemical ozone formation', + 'wu' => '(unit L) Use of water resources', + 'mips' => '(unit g) Material input per unit of service', + 'adpe' => '(unit g SB eq) Use of mineral and metal resources', + 'adpf' => '(unit J) Use of fossil resources (including nuclear)', + 'ap' => '(unit mol H+ eq) Acidification', + 'ctue' => '(unit CTUe) Freshwater ecotoxicity', + // ctuh_c => '(unit CTUh) Human toxicity - non-carcinogenic effects', + 'epf' => '(unit g P eq) Eutrophication of freshwater', + 'epm' => '(unit g N eq) Eutrophication of marine waters', + 'ept' => '(unit mol N eq) Terrestrial eutrophication', +]; + +$table = 'glpi_plugin_carbon_embodiedimpacts'; +$previous_criteria = 'pe'; +foreach ($new_criterias as $criteria => $comment) { + $migration->addField( + $table, + $criteria, + 'float DEFAULT \'0\'', + [ + 'comment' => $comment, + 'after' => $previous_criteria . '_quality', + ] + ); + $migration->addField( + $table, + $criteria . '_quality', + 'int unsigned NOT NULL DEFAULT \'0\'', + [ + 'comment' => 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + 'after' => $criteria, + ] + ); + // $migration->dropField($table, $criteria); + // $migration->dropField($table, $criteria . '_quality'); + $previous_criteria = $criteria; +} + +// Uniformize existing impact : make floats signed +$old_criterias = [ + 'gwp' => '(unit g CO2 eq) Global warming potential', + 'adp' => '(unit g Sb eq) Abiotic depletion potential', + 'pe' => '(unit J) Primary energy', +]; +foreach ($old_criterias as $criteria => $comment) { + $migration->changeField($table, $criteria, $criteria, 'float DEFAULT \'0\'', [ + 'comment' => $comment, + ]); +} + +// Rename cards for the report +$dashboard_item = new DashboardItem(); +$rows = $dashboard_item->find([ + 'card_id' => 'plugin_carbon_report_embodied_gwp_impact' +]); +foreach ($rows as $row) { + $dashboard_item->update([ + 'id' => $row['id'], + 'card_id' => 'plugin_carbon_report_embodied_gwp_impact', + ]); +} + +$dashboard_item = new DashboardItem(); +$rows = $dashboard_item->find([ + 'card_id' => 'plugin_carbon_report_embodied_abiotic_depletion' +]); +foreach ($rows as $row) { + $dashboard_item->update([ + 'id' => $row['id'], + 'card_id' => 'plugin_carbon_report_embodied_adp_impact', + ]); +} diff --git a/install/migration/update_x.x.x_to_y.y.y.php b/install/migration/update_x.x.x_to_y.y.y.php index cb9599dc..2b416877 100644 --- a/install/migration/update_x.x.x_to_y.y.y.php +++ b/install/migration/update_x.x.x_to_y.y.y.php @@ -57,10 +57,17 @@ function update001to100(Migration $migration) } $dbFile = plugin_carbon_getSchemaPath($to_version); - if ($dbFile === null || !$DB->runFile($dbFile)) { + if ($dbFile === null) { $migration->addWarningMessage("Error creating tables : " . $DB->error()); $updateresult = false; } + try { + $DB->runFile($dbFile); + } catch (\RuntimeException $e) { + $migration->addWarningMessage("Error creating tables : " . $e->getMessage()); + $updateresult = false; + } + // ************ Keep it at the end ************** $migration->executeMigration(); diff --git a/install/mysql/plugin_carbon_empty.sql b/install/mysql/plugin_carbon_empty.sql index da2f1360..53f8c35b 100644 --- a/install/mysql/plugin_carbon_empty.sql +++ b/install/mysql/plugin_carbon_empty.sql @@ -132,17 +132,51 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_carbon_computerusageprofiles` ( CREATE TABLE IF NOT EXISTS `glpi_plugin_carbon_embodiedimpacts` ( `id` int unsigned NOT NULL AUTO_INCREMENT, - `itemtype` varchar(255) DEFAULT NULL, + `itemtype` varchar(255) DEFAULT NULL, `items_id` int unsigned NOT NULL DEFAULT '0', - `engine` varchar(255) DEFAULT NULL, - `engine_version` varchar(255) DEFAULT NULL, - `date_mod` timestamp NULL DEFAULT NULL, - `gwp` float unsigned DEFAULT '0' COMMENT '(unit gCO2eq) Global warming potential', + `engine` varchar(255) DEFAULT NULL, + `engine_version` varchar(255) DEFAULT NULL, + `date_mod` timestamp NULL DEFAULT NULL, + `gwp` float DEFAULT '0' COMMENT '(unit g CO2 eq) Global warming potential', `gwp_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', - `adp` float unsigned DEFAULT '0' COMMENT '(unit gSbeq) Abiotic depletion potential', + `adp` float DEFAULT '0' COMMENT '(unit g Sb eq) Abiotic depletion potential', `adp_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', - `pe` float unsigned DEFAULT '0' COMMENT '(unit J) Primary energy', + `pe` float DEFAULT '0' COMMENT '(unit J) Primary energy', `pe_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `gwppb` float DEFAULT '0' COMMENT '(unit g CO2 eq) Climate change - Contribution of biogenic emissions', + `gwppb_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `gwppf` float DEFAULT '0' COMMENT '(unit g CO2 eq) Climate change - Contribution of fossil fuel emissions', + `gwppf_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `gwpplu` float DEFAULT '0' COMMENT '(unit g CO2 eq) Climate change - Contribution of emissions from land use change', + `gwpplu_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `ir` float DEFAULT '0' COMMENT '(unit g U235 eq) Emissions of radionizing substances', + `ir_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `lu` float DEFAULT '0' COMMENT '(unit none) Land use', + `lu_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `odp` float DEFAULT '0' COMMENT '(unit g CFC-11 eq) Depletion of the ozone layer', + `odp_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `pm` float DEFAULT '0' COMMENT '(unit Disease occurrence) Fine particle emissions', + `pm_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `pocp` float DEFAULT '0' COMMENT '(unit g NMVOC eq) Photochemical ozone formation', + `pocp_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `wu` float DEFAULT '0' COMMENT '(unit L) Use of water resources', + `wu_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `mips` float DEFAULT '0' COMMENT '(unit g) Material input per unit of service', + `mips_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `adpe` float DEFAULT '0' COMMENT '(unit g SB eq) Use of mineral and metal resources', + `adpe_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `adpf` float DEFAULT '0' COMMENT '(unit J) Use of fossil resources (including nuclear)', + `adpf_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `ap` float DEFAULT '0' COMMENT '(unit mol H+ eq) Acidification', + `ap_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `ctue` float DEFAULT '0' COMMENT '(unit CTUe) Freshwater ecotoxicity', + `ctue_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `epf` float DEFAULT '0' COMMENT '(unit g P eq) Eutrophication of freshwater', + `epf_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `epm` float DEFAULT '0' COMMENT '(unit g N eq) Eutrophication of marine waters', + `epm_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `ept` float DEFAULT '0' COMMENT '(unit mol N eq) Terrestrial eutrophication', + `ept_quality` int unsigned NOT NULL DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', PRIMARY KEY (`id`), UNIQUE KEY `unicity` (`itemtype`, `items_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; @@ -161,12 +195,12 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_carbon_monitormodels` ( `gwp` int DEFAULT '0' COMMENT '(unit gCO2eq) Global warming potential', `gwp_source` mediumtext DEFAULT NULL COMMENT 'any information to describe the source, URL preferred', `gwp_quality` int DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', - `adp` int DEFAULT '0' COMMENT '(unit gSbEq) Abiotic depletion potential', - `adp_source` mediumtext DEFAULT NULL COMMENT 'any information to describe the source, URL preferred', - `adp_quality` int DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', - `pe` int DEFAULT '0' COMMENT '(unit J) Primary energy', - `pe_source` mediumtext DEFAULT NULL COMMENT 'any information to describe the source, URL preferred', - `pe_quality` int DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `adp` int DEFAULT '0' COMMENT '(unit gSbEq) Abiotic depletion potential', + `adp_source` mediumtext DEFAULT NULL COMMENT 'any information to describe the source, URL preferred', + `adp_quality` int DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', + `pe` int DEFAULT '0' COMMENT '(unit J) Primary energy', + `pe_source` mediumtext DEFAULT NULL COMMENT 'any information to describe the source, URL preferred', + `pe_quality` int DEFAULT '0' COMMENT 'DataTtacking\\AbstractTracked::DATA_QUALITY_* constants', PRIMARY KEY (`id`), UNIQUE KEY `unicity` (`monitormodels_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/Dashboard/Grid.php b/src/Dashboard/Grid.php index 3dc30e84..e005fb5c 100644 --- a/src/Dashboard/Grid.php +++ b/src/Dashboard/Grid.php @@ -35,6 +35,7 @@ use Computer; use Glpi\Dashboard\Filter; use GlpiPlugin\Carbon\Config; +use GlpiPlugin\Carbon\Impact\Type; use Session; class Grid @@ -153,6 +154,22 @@ protected static function getStandardCards(): array ]; } + foreach (Type::getImpactTypes() as $impact_type) { + $key = "plugin_carbon_embodied_{$impact_type}_impact"; + if (isset($new_cards[$key])) { + trigger_error("The card $key already exists", E_USER_WARNING); + } + $new_cards[$key] = [ + 'widgettype' => ['bigNumber'], + 'group' => $group, + 'label' => Type::getEmbodiedImpactLabel($impact_type), + 'provider' => Provider::class . '::getImpactOfEmbodiedCriteria', + 'args' => [ + 'impact_type' => $impact_type + ] + ]; + } + $new_cards += [ // Usage impact 'plugin_carbon_total_usage_power' => [ @@ -175,24 +192,24 @@ protected static function getStandardCards(): array ], // Embodied impact - 'plugin_carbon_embodied_gwp_impact' => [ - 'widgettype' => ['bigNumber'], - 'group' => $group, - 'label' => __('Embodied global warming potential', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedGlobalWarming', - ], - 'plugin_carbon_embodied_pe_impact' => [ - 'widgettype' => ['bigNumber'], - 'group' => $group, - 'label' => __('Embodied primary energy consumed', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedPrimaryEnergy', - ], - 'plugin_carbon_embodied_adp_impact' => [ - 'widgettype' => ['bigNumber'], - 'group' => $group, - 'label' => __('Embodied abiotic depletion potential', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedAbioticDepletion', - ], + // 'plugin_carbon_embodied_gwp_impact' => [ + // 'widgettype' => ['bigNumber'], + // 'group' => $group, + // 'label' => __('Embodied global warming potential', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedGlobalWarming', + // ], + // 'plugin_carbon_embodied_pe_impact' => [ + // 'widgettype' => ['bigNumber'], + // 'group' => $group, + // 'label' => __('Embodied primary energy consumed', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedPrimaryEnergy', + // ], + // 'plugin_carbon_embodied_adp_impact' => [ + // 'widgettype' => ['bigNumber'], + // 'group' => $group, + // 'label' => __('Embodied abiotic depletion potential', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedAbioticDepletion', + // ], // embodied + usage impact 'plugin_carbon_total_gwp_impact' => [ @@ -238,8 +255,8 @@ protected static function getReportCards(): array ]; } - // Usage impact $new_cards += [ + // Usage impact 'plugin_carbon_report_usage_carbon_emission_ytd' => [ 'widgettype' => ['usage_carbon_emission_ytd'], 'group' => $group, @@ -278,25 +295,40 @@ protected static function getReportCards(): array ], // Embodied impact - 'plugin_carbon_report_embodied_global_warming' => [ - 'widgettype' => ['embodied_global_warming'], - 'group' => $group, - 'label' => __('Embodied global warming potential', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedGlobalWarming', - ], - 'plugin_carbon_report_embodied_abiotic_depletion' => [ - 'widgettype' => ['embodied_abiotic_depletion'], - 'group' => $group, - 'label' => __('Embodied abiotic depletion potential', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedAbioticDepletion', - ], - 'plugin_carbon_report_embodied_pe_impact' => [ - 'widgettype' => ['embodied_primary_energy'], - 'group' => $group, - 'label' => __('Embodied primary energy consumed', 'carbon'), - 'provider' => Provider::class . '::getEmbodiedPrimaryEnergy', - ], + // 'plugin_carbon_report_embodied_global_warming' => [ + // 'widgettype' => ['embodied_global_warming'], + // 'group' => $group, + // 'label' => __('Embodied global warming potential', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedGlobalWarming', + // ], + // 'plugin_carbon_report_embodied_abiotic_depletion' => [ + // 'widgettype' => ['embodied_abiotic_depletion'], + // 'group' => $group, + // 'label' => __('Embodied abiotic depletion potential', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedAbioticDepletion', + // ], + // 'plugin_carbon_report_embodied_pe_impact' => [ + // 'widgettype' => ['embodied_primary_energy'], + // 'group' => $group, + // 'label' => __('Embodied primary energy consumed', 'carbon'), + // 'provider' => Provider::class . '::getEmbodiedPrimaryEnergy', + // ], ]; + foreach (Type::getImpactTypes() as $impact_type) { + $key = "plugin_carbon_report_embodied_{$impact_type}_impact"; + if (isset($new_cards[$key])) { + trigger_error("The card $key already exists", E_USER_WARNING); + } + $new_cards[$key] = [ + 'widgettype' => [self::getWidgetForImpact(true, $impact_type)], + 'group' => $group, + 'label' => Type::getEmbodiedImpactLabel($impact_type), + 'provider' => Provider::class . '::getImpactOfEmbodiedCriteria', + 'args' => [ + 'impact_type' => $impact_type + ] + ]; + } // Informational content $new_cards += [ @@ -314,4 +346,18 @@ protected static function getReportCards(): array return $new_cards; } + + private static function getWidgetForImpact(bool $embodied, string $type): string + { + switch (($embodied ? 'embodied' : 'usage') . ' ' . $type) { + case 'embodied gwp': + return 'embodied_global_warming'; + case 'embodied adp': + return 'embodied_abiotic_depletion'; + case 'embodied pe': + return 'embodied_primary_energy'; + } + + return ''; + } } diff --git a/src/Dashboard/Provider.php b/src/Dashboard/Provider.php index 1e8f188e..16d830c3 100644 --- a/src/Dashboard/Provider.php +++ b/src/Dashboard/Provider.php @@ -53,6 +53,7 @@ use GlpiPlugin\Carbon\UsageImpact; use Glpi\DBAL\QueryExpression; use Glpi\DBAL\QuerySubQuery; +use GlpiPlugin\Carbon\Impact\Type; use Search; use Session; use Toolbox as GlpiToolbox; @@ -950,6 +951,44 @@ public static function getEmbodiedAbioticDepletion(array $params = [], array $cr ]; } + /** + * Total embodied abiotic depletion potential in antimony equivalent + * + * @param array $params + * @param array $crit + * @return array + */ + public static function getImpactOfEmbodiedCriteria(string $impact_type, array $params = [], array $crit = []): array + { + $default_params = [ + 'label' => Type::getEmbodiedImpactLabel($impact_type), + 'icon' => 'fa-solid fa-temperature-arrow-up', + ]; + $params = array_merge($default_params, $params); + if (count($crit['itemtype'] ?? []) === 0) { + $crit['itemtype'] = PLUGIN_CARBON_TYPES; + } else { + $crit['itemtype'] = array_intersect($crit['itemtype'], PLUGIN_CARBON_TYPES); + } + + $value = self::getSum(EmbodiedImpact::getTable(), $impact_type, $params, $crit); + if ($value === null) { + $value = 'N/A'; + } else { + // $value = Toolbox::getWeight($value) . Type::getImpactUnit($impact_type); + $value = Toolbox::getHumanReadableValue( + $value, + Type::getImpactUnit($impact_type) + ); + } + + return [ + 'number' => $value, + 'label' => $params['label'], + 'icon' => $params['icon'], + ]; + } + /** * Get usage abiotic depletion potential in antimony equivalent * diff --git a/src/EmbodiedImpact.php b/src/EmbodiedImpact.php index 13271feb..968e3530 100644 --- a/src/EmbodiedImpact.php +++ b/src/EmbodiedImpact.php @@ -36,6 +36,7 @@ use DBmysql; use DBmysqlIterator; use Glpi\DBAL\QuerySubQuery; +use GlpiPlugin\Carbon\Impact\Type; /** * Embodied impact of assets @@ -83,35 +84,19 @@ public function rawSearchOptions() 'datatype' => 'itemtypename', ]; - $tab[] = [ - 'id' => '5', - 'table' => $this->getTable(), - 'field' => 'gwp', - 'name' => __('Global Warming Potential', 'carbon'), - 'massiveaction' => false, - 'datatype' => 'number', - 'unit' => 'gCO2eq', - ]; - - $tab[] = [ - 'id' => '6', - 'table' => $this->getTable(), - 'field' => 'adp', - 'name' => __('Abiotic Depletion Potential', 'carbon'), - 'massiveaction' => false, - 'datatype' => 'number', - 'unit' => 'KgSbeq', - ]; - - $tab[] = [ - 'id' => '7', - 'table' => $this->getTable(), - 'field' => 'pe', - 'name' => __('Primary energy', 'carbon'), - 'massiveaction' => false, - 'datatype' => 'number', - 'unit' => 'J', - ]; + $id = 5; + foreach (Type::getImpactTypes() as $type) { + $tab[] = [ + 'id' => $id, + 'table' => $this->getTable(), + 'field' => $type, + 'name' => Type::getEmbodiedImpactLabel($type), + 'massiveaction' => false, + 'datatype' => 'number', + 'unit' => implode(' ', Type::getImpactUnit($type)), + ]; + $id++; + } $tab[] = [ 'id' => SearchOptions::CARBON_EMISSION_CALC_DATE, diff --git a/src/Impact/Embodied/Boavizta/AbstractAsset.php b/src/Impact/Embodied/Boavizta/AbstractAsset.php index f6db53a6..e7f9685d 100644 --- a/src/Impact/Embodied/Boavizta/AbstractAsset.php +++ b/src/Impact/Embodied/Boavizta/AbstractAsset.php @@ -55,6 +55,32 @@ abstract class AbstractAsset extends AbstractEmbodiedImpact implements AssetInte /** @var Client instance of the HTTP client */ protected ?Client $client = null; + /** @var array Supported impact criterias and the multiplier unit of the value returned by Boaviztapi */ + protected array $criteria_units = [ + 'gwp' => 1000, // Kg + 'adp' => 1000, // Kg + 'pe' => 1000000, // MJ + 'gwppb' => 1000, // Kg + 'gwppf' => 1000, // Kg + 'gwpplu' => 1000, // Kg + 'ir' => 1000, // Kg + 'lu' => 1, // (no unit) + 'odp' => 1000, // Kg + 'pm' => 1, // (no unit) + 'pocp' => 1000, // Kg + 'wu' => 1000, // M^3 + 'mips' => 1000, // Kg + 'adpe' => 1000, // Kg + 'adpf' => 1000000, // MJ + 'ap' => 1, // mol + 'ctue' => 1, // CTUe + // 'ctuh_c' => 1, // CTUh request fails when this criteria is added, not a URL encoding issue + // 'ctuh_nc' => 1, // CTUh request fails when this criteria is added, not a URL encoding issue + 'epf' => 1000, // Kg + 'epm' => 1000, // Kg + 'ept' => 1, // mol + ]; + // abstract public static function getEngine(CommonDBTM $item): EngineInterface; /** @@ -98,7 +124,23 @@ protected function getVersion(): string return self::$engine_version; } - protected function query($description): array + /** + * Get the quety string specifying the impact criterias for the HTTP request + * + * @return string + */ + protected function getCriteriasQueryString(): string + { + return 'criteria=' . implode('&criteria=', array_keys($this->criteria_units)); + } + + /** + * Send a HTTP query + * + * @param array $description + * @return array + */ + protected function query(array $description): array { try { $response = $this->client->post($this->endpoint, [ @@ -115,6 +157,7 @@ protected function query($description): array /** * Read the response to find the impacts provided by Boaviztapi * + * @param array $response * @return array */ protected function parseResponse(array $response): array @@ -130,22 +173,28 @@ protected function parseResponse(array $response): array if ($impact_id === false) { continue; } - switch ($type) { - case 'gwp': - $impacts[$impact_id] = $this->parseGwp($response['impacts'][$type]); - break; - case 'adp': - $impacts[$impact_id] = $this->parseAdp($response['impacts'][$type]); - break; - case 'pe': - $impacts[$impact_id] = $this->parsePe($response['impacts'][$type]); - break; - } + $impacts[$impact_id] = $this->parseCriteria($type, $response['impacts'][$type]); } return $impacts; } + protected function parseCriteria(string $name, array $impact): ?TrackedFloat + { + if ($impact['embedded'] === 'not implemented') { + return null; + } + + $unit_multiplier = $this->criteria_units[$name]; + $value = new TrackedFloat( + $impact['embedded']['value'] * $unit_multiplier, + null, + TrackedFloat::DATA_QUALITY_ESTIMATED + ); + + return $value; + } + protected function parseGwp(array $impact): ?TrackedFloat { if ($impact['embedded'] === 'not implemented') { diff --git a/src/Impact/Embodied/Boavizta/Computer.php b/src/Impact/Embodied/Boavizta/Computer.php index f395664e..12e6e4b8 100644 --- a/src/Impact/Embodied/Boavizta/Computer.php +++ b/src/Impact/Embodied/Boavizta/Computer.php @@ -59,6 +59,7 @@ protected function doEvaluation(): ?array // adapt $this->endpoint depending on the type of computer (server, laptop, ...) $type = $this->getType($this->item); $this->endpoint = $this->getEndpoint($type); + $this->endpoint .= '?' . $this->getCriteriasQueryString(); // Ask for embodied impact only $handle_hardware = in_array($type, [ diff --git a/src/Impact/Embodied/Engine.php b/src/Impact/Embodied/Engine.php index 7a3a2dbb..a66b2b70 100644 --- a/src/Impact/Embodied/Engine.php +++ b/src/Impact/Embodied/Engine.php @@ -34,6 +34,7 @@ use CommonGLPI; use CommonDBTM; +use DBmysql; use GlpiPlugin\Carbon\AbstractModel; use GlpiPlugin\Carbon\Config; use GlpiPlugin\Carbon\DataSource\Lca\Boaviztapi\Client; @@ -131,6 +132,9 @@ protected static function configureEngine(EmbodiedImpactInterface $engine): Embo */ private static function hasModelData(CommonDBTM $item): bool { + /** @var DBmysql $DB */ + global $DB; + $itemtype = get_class($item); $glpi_model_class = $itemtype . 'Model'; $glpi_model_class_fk = getForeignKeyFieldForItemType($glpi_model_class); @@ -138,12 +142,16 @@ private static function hasModelData(CommonDBTM $item): bool * @var class-string $model_class */ $model_class = 'GlpiPlugin\\Carbon\\' . $glpi_model_class; + $model_table = getTableForItemType($model_class); $glpi_model_id = $item->fields[$glpi_model_class_fk]; $crit = [ $glpi_model_class_fk => $glpi_model_id ]; $types = Type::getImpactTypes(); foreach ($types as $key => $type) { + if (!$DB->fieldExists($model_table, $type)) { + continue; + } $crit['OR'][] = [ $type . '_quality' => ['<>', AbstractTracked::DATA_QUALITY_UNSET_VALUE] ]; diff --git a/src/Impact/Type.php b/src/Impact/Type.php index 7c3f5551..b127512d 100644 --- a/src/Impact/Type.php +++ b/src/Impact/Type.php @@ -35,16 +35,89 @@ class Type { - const IMPACT_GWP = 0; // Global warming potential - const IMPACT_ADP = 1; // Abiotic Depletion Potential - const IMPACT_PE = 2; // Primary Energy + const IMPACT_GWP = 1; // Global warming potential + const IMPACT_ADP = 2; // Abiotic Depletion Potential + const IMPACT_PE = 3; // Primary Energy + const IMPACT_GWPPB = 4; + const IMPACT_GWPPF = 5; + const IMPACT_GWPPLU = 6; + const IMPACT_IR = 7; + const IMPACT_LU = 8; + const IMPACT_ODP = 9; + const IMPACT_PM = 10; + const IMPACT_POCP = 11; + const IMPACT_WU = 12; + const IMPACT_MIPS = 13; + const IMPACT_ADPE = 14; + const IMPACT_ADPF = 15; + const IMPACT_AP = 16; + const IMPACT_CTUE = 17; + // const IMPACT_CTUHC = 18; + // const IMPACT_CTUHNC = 19; + const IMPACT_EPF = 20; + const IMPACT_EPM = 21; + const IMPACT_EPT = 22; private static array $impact_types = [ - self::IMPACT_GWP => 'gwp', - self::IMPACT_ADP => 'adp', - self::IMPACT_PE => 'pe', + self::IMPACT_GWP => 'gwp', + self::IMPACT_ADP => 'adp', + self::IMPACT_PE => 'pe', + self::IMPACT_GWPPB => 'gwppb', + self::IMPACT_GWPPF => 'gwppf', + self::IMPACT_GWPPLU => 'gwpplu', + self::IMPACT_IR => 'ir', + self::IMPACT_LU => 'lu', + self::IMPACT_ODP => 'odp', + self::IMPACT_PM => 'pm', + self::IMPACT_POCP => 'pocp', + self::IMPACT_WU => 'wu', + self::IMPACT_MIPS => 'mips', + self::IMPACT_ADPE => 'adpe', + self::IMPACT_ADPF => 'adpf', + self::IMPACT_AP => 'ap', + self::IMPACT_CTUE => 'ctue', + // self::IMPACT_CTUHC => 'ctuh_c', + // self::IMPACT_CTUHNC => 'ctuh_nc', + self::IMPACT_EPF => 'epf', + self::IMPACT_EPM => 'epm', + self::IMPACT_EPT => 'ept', ]; + /** + * Unit of impact criterias + * + * @var array + */ + private static array $impact_units = [ + 'gwp' => ['g', 'CO₂ eq'], + 'adp' => ['g', 'SB eq'], + 'pe' => ['J', ''], + 'gwppb' => ['g', 'CO₂ eq'], + 'gwppf' => ['g', 'CO₂ eq'], + 'gwpplu' => ['g', 'CO₂ eq'], + 'ir' => ['g', 'U235 eq'], + 'lu' => null, + 'odp' => ['g', 'CFC-11 eq'], + 'pm' => null, + 'pocp' => ['g', 'U235 eq'], + 'wu' => ['m³', ''], + 'mips' => ['g', ''], + 'adpe' => ['g', 'SB eq'], + 'adpf' => ['J', ''], + 'ap' => ['mol', 'H+ eq'], + 'ctue' => ['J', ''], + // 'ctuh_c' => [null, 'CTUh'], + // 'ctuh_nc' => [null, 'CTUh'], + 'epf' => ['g', 'P eq'], + 'epm' => ['g', 'N eq'], + 'ept' => ['mol', 'N eq'], + ]; + + /** + * get an array of impact types + * + * @return array + */ public static function getImpactTypes(): array { return self::$impact_types; @@ -55,9 +128,92 @@ public static function getImpactTypes(): array * * @param string $type * @return int|string|false - */ + **/ public static function getImpactId(string $type) { return array_search($type, self::$impact_types); } + + /** + * Get the unit of an impact type + * + * @param string $type impact type name + * @return array + **/ + public static function getImpactUnit(string $type): array + { + return self::$impact_units[$type] ?? ['', '']; + } + + /** + * Get the unit of an impact type + * + * @param string $type impact type name + * @return string + **/ + public static function getEmbodiedImpactLabel(string $type): string + { + $label = match ($type) { + 'gwp' => __('Embodied Global warming potential', 'carbon'), + 'adp' => __('Embodied Abiotic depletion potential', 'carbon'), + 'pe' => __('Embodied Primary energy consumed', 'carbon'), + 'gwppb' => __('Embodied Climate change - Contribution of biogenic emissions', 'carbon'), + 'gwppf' => __('Embodied Climate change - Contribution of fossil fuel emissions', 'carbon'), + 'gwpplu' => __('Embodied Climate change - Contribution of emissions from land use change', 'carbon'), + 'ir' => __('Embodied Emissions of radionizing substances', 'carbon'), + 'lu' => __('Embodied Land use', 'carbon'), + 'odp' => __('Embodied Depletion of the ozone layer', 'carbon'), + 'pm' => __('Embodied Fine particle emissions', 'carbon'), + 'pocp' => __('Embodied Photochemical ozone formation', 'carbon'), + 'wu' => __('Embodied Use of water resources', 'carbon'), + 'mips' => __('Embodied Material input per unit of service', 'carbon'), + 'adpe' => __('Embodied Use of mineral and metal resources', 'carbon'), + 'adpf' => __('Embodied Use of fossil resources (including nuclear)', 'carbon'), + 'ap' => __('Embodied Acidification', 'carbon'), + 'ctue' => __('Embodied Human Toxicity - Carcinogenic Effects', 'carbon'), + // 'ctuh_c' => __('Embodied Human Toxicity - Carcinogenic Effects', 'carbon'), + // 'ctuh_nc' => __('Embodied Human toxicity - non-carcinogenic effects', 'carbon'), + 'epf' => __('Embodied Eutrophication of freshwater', 'carbon'), + 'epm' => __('Embodied Eutrophication of marine waters', 'carbon'), + 'ept' => __('Embodied Terrestrial eutrophication', 'carbon'), + default => '', + }; + return $label; + } + + /** + * Get the unit of an impact type + * + * @param string $type impact type name + * @return string + **/ + public static function getUsageImpactLabel(string $type): string + { + $label = match ($type) { + 'gwp' => __('Usage Global warming potential', 'carbon'), + 'adp' => __('Usage Abiotic depletion potential', 'carbon'), + 'pe' => __('Usage Primary energy consumed', 'carbon'), + 'gwppb' => __('Usage Climate change - Contribution of biogenic emissions', 'carbon'), + 'gwppf' => __('Usage Climate change - Contribution of fossil fuel emissions', 'carbon'), + 'gwpplu' => __('Usage Climate change - Contribution of emissions from land use change', 'carbon'), + 'ir' => __('Usage Emissions of radionizing substances', 'carbon'), + 'lu' => __('Usage Land use', 'carbon'), + 'odp' => __('Usage Depletion of the ozone layer', 'carbon'), + 'pm' => __('Usage Fine particle emissions', 'carbon'), + 'pocp' => __('Usage Photochemical ozone formation', 'carbon'), + 'wu' => __('Usage Use of water resources', 'carbon'), + 'mips' => __('Usage Material input per unit of service', 'carbon'), + 'adpe' => __('Usage Use of mineral and metal resources', 'carbon'), + 'adpf' => __('Usage Use of fossil resources (including nuclear)', 'carbon'), + 'ap' => __('Usage Acidification', 'carbon'), + 'ctue' => __('Usage Human Toxicity - Carcinogenic Effects', 'carbon'), + // 'ctuh_c' => __('Usage Human Toxicity - Carcinogenic Effects', 'carbon'), + // 'ctuh_nc' => __('Usage Human toxicity - non-carcinogenic effects', 'carbon'), + 'epf' => __('Usage Eutrophication of freshwater', 'carbon'), + 'epm' => __('Usage Eutrophication of marine waters', 'carbon'), + 'ept' => __('Usage Terrestrial eutrophication', 'carbon'), + default => '' + }; + return $label; + } } diff --git a/src/Toolbox.php b/src/Toolbox.php index 328e34cf..67517d2d 100644 --- a/src/Toolbox.php +++ b/src/Toolbox.php @@ -224,20 +224,7 @@ public static function getWeight(float $weight): string $weight = self::dynamicRound($weight); //TRANS: %1$s is a number maybe float or string and %2$s the unit - return sprintf(__('%1$s %2$s'), $weight, $human_readable_unit); - } - - public static function dynamicRound(float $number): float - { - if ($number < 10) { - $number = round($number, 2); - } else if ($number < 100) { - $number = round($number, 1); - } else { - $number = round($number, 0); - } - - return $number; + return sprintf(__('%1$s %2$s'), $weight, $human_readable_unit); } /** @@ -272,19 +259,19 @@ public static function getPower(float $p): string $p = self::dynamicRound($p); //TRANS: %1$s is a number maybe float or string and %2$s the unit - return sprintf(__('%1$s %2$s'), $p, $human_readable_unit); + return sprintf(__('%1$s %2$s'), $p, $human_readable_unit); } /** - * Format a power passing a power in grams + * Format a energy in watt.hour * - * @param float $p Power in Watt + * @param float $p Energy in watt.hour * - * @return string formatted power + * @return string formatted energy **/ public static function getEnergy(float $p): string { - //TRANS: list of unit (W for watt) + //TRANS: list of unit (Wh for watt.hour) $units = [ __('Wh', 'carbon'), __('KWh', 'carbon'), @@ -307,7 +294,49 @@ public static function getEnergy(float $p): string $p = self::dynamicRound($p); //TRANS: %1$s is a number maybe float or string and %2$s the unit - return sprintf(__('%1$s %2$s'), $p, $human_readable_unit); + return sprintf(__('%1$s %2$s'), $p, $human_readable_unit); + } + + public static function dynamicRound(float $number): float + { + if ($number < 10) { + $number = round($number, 2); + } else if ($number < 100) { + $number = round($number, 1); + } else { + $number = round($number, 0); + } + + return $number; + } + + /** + * Convert a value and its unit into a human readable value + * + * Unit is an array i.e. ['g', 'CO2 eq'] for grams of carbondioxyde equivalent + * + * @param float $value value of the quantity to convert + * @param array $unit unit splitted into a standard unit and a qualification. + * @return string + */ + public static function getHumanReadableValue(float $value, array $unit): string + { + switch ($unit[0]) { + case 'g': + return self::getWeight($value) . $unit[1]; + case 'J': + // To be converted into watt.hour + return self::getEnergy($value / 3600) . $unit[1]; + case 'Wh': + return self::getEnergy($value) . $unit[1]; + case 'm³': + // Value is in m^3 + return sprintf(__('%1$s %2$s', 'carbon'), $value * 1000, 'L'); + case 'mol': + break; + } + + return sprintf(__('%1$s %2$s', 'carbon'), $value, implode(' ', $unit)); } /** diff --git a/tests/integration/SearchOptionTest.php b/tests/integration/SearchOptionTest.php index e8e979b9..6a45a523 100644 --- a/tests/integration/SearchOptionTest.php +++ b/tests/integration/SearchOptionTest.php @@ -81,6 +81,25 @@ class SearchOptionTest extends CommonTestCase 'gwp_quality', 'adp_quality', 'pe_quality', + 'gwppb_quality', + 'gwppf_quality', + 'gwpplu_quality', + 'ir_quality', + 'lu_quality', + 'odp_quality', + 'pm_quality', + 'pocp_quality', + 'wu_quality', + 'mips_quality', + 'adpe_quality', + 'adpf_quality', + 'ap_quality', + 'ctue_quality', + 'ctuh_c_quality', + 'ctuh_nc_quality', + 'epf_quality', + 'epm_quality', + 'ept_quality', ], Location::class => [], Zone::class => [