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
5 changes: 3 additions & 2 deletions src/DataSource/Lca/Boaviztapi/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use GlpiPlugin\Carbon\Config as CarbonConfig;
use GlpiPlugin\Carbon\DataSource\Lca\AbstractClient;
use GlpiPlugin\Carbon\DataSource\RestApiClientInterface;
use GlpiPlugin\Carbon\DataTracking\AbstractTracked;
use GlpiPlugin\Carbon\DataTracking\TrackedFloat;
use GlpiPlugin\Carbon\Impact\Type;
use GlpiPlugin\Carbon\Source;
Expand Down Expand Up @@ -252,7 +253,7 @@ public static function getZones()
*
* @param array $response
* @param string $scope (must be either embedded or use)
* @return array
* @return array<int, null|AbstractTracked>
*/
public function parseResponse(array $response, string $scope): array
{
Expand All @@ -273,7 +274,7 @@ public function parseResponse(array $response, string $scope): array
return $impacts;
}

protected function parseCriteria(string $name, $impact): ?TrackedFloat
protected function parseCriteria(string $name, string|array $impact): ?TrackedFloat
{
if ($impact === 'not implemented') {
return null;
Expand Down
9 changes: 7 additions & 2 deletions src/Impact/Embodied/AbstractEmbodiedImpact.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ public function __construct(CommonDBTM $item)
}
}

abstract protected function getVersion(): string;
public function getEnginename(): string
{
return $this->engine;
}

abstract public function getVersion(): string;

#[Override]
public function setLimit(int $limit)
Expand Down Expand Up @@ -221,7 +226,7 @@ public static function getEvaluableQuery(string $itemtype, array $crit = [], boo
/**
* Do the environmental impact evaluation of an asset
*
* @return ?array
* @return ?array<int, null|AbstractTracked>
*/
abstract protected function doEvaluation(): ?array;

Expand Down
2 changes: 1 addition & 1 deletion src/Impact/Embodied/Boavizta/AbstractAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function setClient(Client $client)
}

#[Override]
protected function getVersion(): string
public function getVersion(): string
{
if (self::$engine_version !== 'unknown') {
return self::$engine_version;
Expand Down
25 changes: 13 additions & 12 deletions src/Impact/Embodied/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static function getAvailableBackends(): array
}

/**
* Get an instance of the engine to calculate imapcts for the given itemtype
* Get an instance of the engine to calculate impacts for the given itemtype
*
* Returns null if no engine found
*
Expand All @@ -68,10 +68,6 @@ public static function getEngineFromItemtype(CommonDBTM $item): ?EmbodiedImpactI
{
$itemtype = get_class($item);

if (self::hasModelData($item)) {
return self::getInternalEngineFromItemtype($item);
}

$embodied_impact_namespace = Config::getEmbodiedImpactEngine();
/** @var class-string<AbstractEmbodiedImpact> $embodied_impact_class */
$embodied_impact_class = $embodied_impact_namespace . '\\' . $itemtype;
Expand All @@ -80,31 +76,36 @@ public static function getEngineFromItemtype(CommonDBTM $item): ?EmbodiedImpactI
return self::getInternalEngineFromItemtype($item);
}

/** @var AbstractEmbodiedImpact $embodied_impact */
$embodied_impact = new $embodied_impact_class($item);
/** @var AbstractEmbodiedImpact $external_embodied_impact_engine */
$external_embodied_impact_engine = new $embodied_impact_class($item);
try {
return self::configureEngine($embodied_impact);
$external_embodied_impact_engine = self::configureEngine($external_embodied_impact_engine);
} catch (RuntimeException $e) {
// If the engine cannot be configured, it is not usable
return null;
$external_embodied_impact_engine = null;
}
if (self::hasModelData($item)) {
return self::getInternalEngineFromItemtype($item, $external_embodied_impact_engine);
}

return $external_embodied_impact_engine;
}

/**
* Get an instance of the internal engine to calcilate impacts for the given itemtype
* This is a fallback engine
*
* @param CommonDBTM $item item to analyze
* @param ?EmbodiedImpactInterface $external_embodied_impact_engine
* @return ?EmbodiedImpactInterface
*/
public static function getInternalEngineFromItemtype(CommonDBTM $item): ?EmbodiedImpactInterface
public static function getInternalEngineFromItemtype(CommonDBTM $item, ?EmbodiedImpactInterface $external_embodied_impact_engine = null): ?EmbodiedImpactInterface
{
$itemtype = get_class($item);
$embodied_impact_class = 'GlpiPlugin\\Carbon\\Impact\\Embodied\Internal' . '\\' . $itemtype;
if (!class_exists($embodied_impact_class) || !is_subclass_of($embodied_impact_class, AbstractEmbodiedImpact::class)) {
return null;
}
$embodied_impact = new $embodied_impact_class($item);
$embodied_impact = new $embodied_impact_class($item, $external_embodied_impact_engine);
return $embodied_impact;
}

Expand Down
71 changes: 48 additions & 23 deletions src/Impact/Embodied/Internal/AbstractAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
namespace GlpiPlugin\Carbon\Impact\Embodied\Internal;

use CommonDBTM;
use DBmysql;
use GlpiPlugin\Carbon\DataTracking\TrackedFloat;
use GlpiPlugin\Carbon\Impact\Embodied\AbstractEmbodiedImpact;
use GlpiPlugin\Carbon\Impact\Type;
Expand All @@ -48,42 +49,66 @@ abstract class AbstractAsset extends AbstractEmbodiedImpact
/** @var string $engine_version Version of the calculation engine */
protected static string $engine_version = '1';

protected ?AbstractEmbodiedImpact $external_embodied_impact_engine = null;

public function __construct(CommonDBTM $item, ?AbstractEmbodiedImpact $external_embodied_impact_engine)
{
parent::__construct($item);
$this->external_embodied_impact_engine = $external_embodied_impact_engine;
}

#[Override]
protected function getVersion(): string
public function getVersion(): string
{
return self::$engine_version;
}

#[Override]
protected function doEvaluation(): array
{
/** @var class-string<CommonDBTM> */
/** @var DBmysql $DB */
global $DB;

$impacts = [];

$glpi_model_itemtype = static::$itemtype . 'Model';
$glpi_model_table = getTableForItemType($glpi_model_itemtype);
$glpi_model_fk = getForeignKeyFieldForItemType($glpi_model_itemtype);
if ($glpi_model_itemtype::isNewID($this->item->fields[$glpi_model_fk])) {
return [];
}
$model_itemtype = 'GlpiPlugin\\Carbon\\' . $glpi_model_itemtype;
$model = getItemForItemtype($model_itemtype);
if ($model !== false) {
$model_table = getTableForItemtype($model_itemtype);
$model->getFromDBByRequest([
'INNER JOIN' => [
$glpi_model_table => [
'ON' => [
$glpi_model_table => 'id',
$model_table => $glpi_model_fk,
],
],
],
'WHERE' => [
$glpi_model_fk => $this->item->fields[$glpi_model_fk],
],
]);

/** @var CommonDBTM $model */
$model = getItemForItemtype($glpi_model_itemtype);
$model->getFromDBByCrit([
$glpi_model_fk => $this->item->fields[$glpi_model_fk],
]);
if ($model->isNewItem()) {
return [];
}
$types = Type::getImpactTypes();

$impacts = [];
$types = Type::getImpactTypes();
foreach ($types as $type) {
if (!isset($model->fields[$type]) || empty($model->fields[$type])) {
continue;
foreach ($types as $type) {
if (!isset($model->fields[$type]) || empty($model->fields[$type])) {
continue;
}
$impacts[Type::getImpactId($type)] = new TrackedFloat(
$model->fields[$type],
null,
$model->fields["{$type}_quality"]
);
}
$impacts[Type::getImpactId($type)] = new TrackedFloat(
$model->fields[$type],
null,
$model->fields["{$type}_quality"]
);
}
if ($this->external_embodied_impact_engine !== null) {
$external_impacts = $this->external_embodied_impact_engine->doEvaluation();
$this->engine .= ' + ' . $this->external_embodied_impact_engine->getEngineName() . ' ' . $this->external_embodied_impact_engine->getVersion();
$impacts = array_replace($external_impacts, $impacts);
}

return $impacts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,22 @@
use PHPUnit\Framework\Attributes\CoversClass;

#[CoversClass(AbstractEmbodiedImpact::class)]
class AbstractEmbodiedImpactTest extends DbTestCase
class AbstractCommonEmbodiedImpactTest extends DbTestCase
{
protected static string $itemtype = '';
protected static string $itemtype_type = '';
protected static string $itemtype_model = '';

public function testGetItemsToEvaluate()
public static function setUpBeforeClass(): void
{
if (static::$itemtype === '' || static::$itemtype_type === '' || static::$itemtype_model === '') {
// Ensure that the inherited test class is properly implemented for this test
$this->fail('Itemtype properties not set in ' . static::class);
self::fail('Itemtype properties not set in ' . static::class);
}
}

public function test_GetItemsToEvaluate_evaluates_asset_when_no_embodied_impact_exists()
{
// Test the asset is evaluable when no embodied impact is in the DB
$glpi_asset_type = $this->createItem(static::$itemtype_type);
$asset_type = $this->createItem('GlpiPlugin\\Carbon\\' . static::$itemtype_type, [
Expand All @@ -64,7 +67,10 @@ public function testGetItemsToEvaluate()
$asset->getTableField('id') => $asset->getID(),
]);
$this->assertEquals(1, $iterator->count());
}

public function test_GetItemsToEvaluate_does_not_evaluates_asset_when_embodied_impact_exists()
{
// Test the asset is no longer evaluable when there is embodied impact in the DB
$glpi_asset_type = $this->createItem(static::$itemtype_type);
$asset_type = $this->createItem('GlpiPlugin\\Carbon\\' . static::$itemtype_type, [
Expand All @@ -82,8 +88,11 @@ public function testGetItemsToEvaluate()
$asset::getTableField('id') => $asset->getID(),
]);
$this->assertEquals(0, $iterator->count());
}

// Test the asset is evaluable when there is embodied impact in the DB but recamculate is set
public function test_GetItemsToEvaluate_evaluates_asset_when_embodied_impact_exists_and_marked_for_recalculation()
{
// Test the asset is evaluable when there is embodied impact in the DB but recalculate is set
$glpi_asset_type = $this->createItem(static::$itemtype_type);
$asset_type = $this->createItem('GlpiPlugin\\Carbon\\' . static::$itemtype_type, [
getForeignKeyFieldForItemType(static::$itemtype_type) => $glpi_asset_type->getID(),
Expand Down
45 changes: 45 additions & 0 deletions tests/src/Impact/Embodied/Boavizta/AbstractEmbodiedImpactTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* -------------------------------------------------------------------------
* Carbon plugin for GLPI
*
* @copyright Copyright (C) 2024-2025 Teclib' and contributors.
* @license https://www.gnu.org/licenses/gpl-3.0.txt GPLv3+
* @link https://github.com/pluginsGLPI/carbon
*
* -------------------------------------------------------------------------
*
* LICENSE
*
* This file is part of Carbon plugin for GLPI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/

namespace GlpiPlugin\Carbon\Tests\Impact\Embodied\Boavizta;

use GlpiPlugin\Carbon\Impact\Embodied\AbstractEmbodiedImpact;
use GlpiPlugin\Carbon\Tests\Impact\Embodied\AbstractCommonEmbodiedImpactTest;
use PHPUnit\Framework\Attributes\CoversClass;

#[CoversClass(AbstractEmbodiedImpact::class)]
class AbstractEmbodiedImpactTest extends AbstractCommonEmbodiedImpactTest
{
protected static string $itemtype = '';
protected static string $itemtype_type = '';
protected static string $itemtype_model = '';
}
83 changes: 83 additions & 0 deletions tests/src/Impact/Embodied/Internal/AbstractEmbodiedImpactTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/**
* -------------------------------------------------------------------------
* Carbon plugin for GLPI
*
* @copyright Copyright (C) 2024-2025 Teclib' and contributors.
* @license https://www.gnu.org/licenses/gpl-3.0.txt GPLv3+
* @link https://github.com/pluginsGLPI/carbon
*
* -------------------------------------------------------------------------
*
* LICENSE
*
* This file is part of Carbon plugin for GLPI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/

namespace GlpiPlugin\Carbon\Tests\Impact\Embodied\Internal;

use GlpiPlugin\Carbon\DataTracking\AbstractTracked;
use GlpiPlugin\Carbon\DataTracking\TrackedFloat;
use GlpiPlugin\Carbon\EmbodiedImpact;
use GlpiPlugin\Carbon\Impact\Embodied\AbstractEmbodiedImpact;
use GlpiPlugin\Carbon\Impact\Embodied\Boavizta\AbstractAsset;
use GlpiPlugin\Carbon\Impact\Embodied\Internal\Computer;
use GlpiPlugin\Carbon\Tests\Impact\Embodied\AbstractCommonEmbodiedImpactTest;
use PHPUnit\Framework\Attributes\CoversClass;

#[CoversClass(AbstractEmbodiedImpact::class)]
class AbstractEmbodiedImpactTest extends AbstractCommonEmbodiedImpactTest
{
protected static string $itemtype = '';
protected static string $itemtype_type = '';
protected static string $itemtype_model = '';

public function test_doEvaluation_uses_user_input_impacts_when_user_input_impacts_are_set()
{
$glpi_asset_type = $this->createItem(static::$itemtype_type);
$glpi_asset_model = $this->createItem(static::$itemtype_model);
$asset_type = $this->createItem('GlpiPlugin\\Carbon\\' . static::$itemtype_type, [
getForeignKeyFieldForItemType(static::$itemtype_type) => $glpi_asset_type->getID(),
]);
$asset_model = $this->createItem('GlpiPlugin\\Carbon\\' . static::$itemtype_model, [
getForeignKeyFieldForItemtype(static::$itemtype_model) => $glpi_asset_model->getID(),
'gwp' => 1024,
'gwp_quality' => AbstractTracked::DATA_QUALITY_MANUAL,
]);
$asset = $this->createItem(static::$itemtype, [
getForeignKeyFieldForItemType(static::$itemtype_type) => $glpi_asset_type->getID(),
getForeignKeyFieldForItemType(static::$itemtype_model) => $glpi_asset_model->getID(),
]);

$external_engine = $this->createStub(AbstractAsset::class);
$external_engine->method('doEvaluation')->willReturn([
// 0 is the ID of GWP, see impact types
0 => new TrackedFloat(2048, null, AbstractTracked::DATA_QUALITY_ESTIMATED),
]);
$instance = new Computer($asset, $external_engine);
$instance->evaluateItem();

$embodied_impact = $this->getItem(EmbodiedImpact::class, [
'itemtype' => get_class($asset),
'items_id' => $asset->getID(),
]);

$this->assertEquals(1024, $embodied_impact->fields['gwp']);
}
}
Loading