ai_upgrade_assistant-0.2.0-alpha2/src/Service/UpdateHistoryService.php
src/Service/UpdateHistoryService.php
<?php
namespace Drupal\ai_upgrade_assistant\Service;
use Drupal\Core\Database\Connection;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Datetime\DrupalDateTime;
/**
* Service for tracking update history and outcomes.
*
* Maintains a detailed history of all updates, including:
* - Success/failure status
* - Duration
* - Generated patches
* - Error logs
* - AI analysis results
*/
class UpdateHistoryService {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* Table names.
*/
const HISTORY_TABLE = 'ai_upgrade_assistant_update_history';
const PATCHES_TABLE = 'ai_upgrade_assistant_update_patches';
const ERRORS_TABLE = 'ai_upgrade_assistant_update_errors';
/**
* Constructs a new UpdateHistoryService.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory service.
*/
public function __construct(
Connection $database,
StateInterface $state,
LoggerChannelFactoryInterface $logger_factory
) {
$this->database = $database;
$this->state = $state;
$this->loggerFactory = $logger_factory;
}
/**
* Records the start of an update.
*
* @param string $module_name
* The module being updated.
* @param string $from_version
* Starting version.
* @param string $to_version
* Target version.
* @param array $metadata
* Additional update metadata.
*
* @return int
* The update ID.
*/
public function recordUpdateStart($module_name, $from_version, $to_version, array $metadata = []) {
$record = [
'module_name' => $module_name,
'from_version' => $from_version,
'to_version' => $to_version,
'status' => 'in_progress',
'start_time' => time(),
'metadata' => serialize($metadata),
];
return $this->database->insert(self::HISTORY_TABLE)
->fields($record)
->execute();
}
/**
* Records the completion of an update.
*
* @param int $update_id
* The update ID.
* @param string $status
* The final status ('success' or 'failure').
* @param array $results
* Update results and metrics.
*/
public function recordUpdateCompletion($update_id, $status, array $results = []) {
$this->database->update(self::HISTORY_TABLE)
->fields([
'status' => $status,
'end_time' => time(),
'results' => serialize($results),
])
->condition('id', $update_id)
->execute();
// Update success rate metrics
$this->updateSuccessMetrics($status);
}
/**
* Records generated patches for an update.
*
* @param int $update_id
* The update ID.
* @param array $patches
* Array of patch information.
*/
public function recordPatches($update_id, array $patches) {
foreach ($patches as $patch) {
$record = [
'update_id' => $update_id,
'file_path' => $patch['file'],
'patch_content' => $patch['content'],
'ai_confidence' => $patch['confidence'] ?? NULL,
'status' => $patch['status'],
'created' => time(),
];
$this->database->insert(self::PATCHES_TABLE)
->fields($record)
->execute();
}
}
/**
* Records errors encountered during an update.
*
* @param int $update_id
* The update ID.
* @param array $errors
* Array of error information.
*/
public function recordErrors($update_id, array $errors) {
foreach ($errors as $error) {
$record = [
'update_id' => $update_id,
'error_type' => $error['type'],
'message' => $error['message'],
'stack_trace' => $error['trace'] ?? '',
'timestamp' => time(),
];
$this->database->insert(self::ERRORS_TABLE)
->fields($record)
->execute();
}
}
/**
* Gets update history for a module.
*
* @param string $module_name
* The module name.
* @param int $limit
* Number of records to return.
*
* @return array
* Update history records.
*/
public function getModuleHistory($module_name, $limit = 10) {
return $this->database->select(self::HISTORY_TABLE, 'h')
->fields('h')
->condition('module_name', $module_name)
->orderBy('start_time', 'DESC')
->range(0, $limit)
->execute()
->fetchAll();
}
/**
* Gets detailed information about a specific update.
*
* @param int $update_id
* The update ID.
*
* @return array
* Detailed update information.
*/
public function getUpdateDetails($update_id) {
$update = $this->database->select(self::HISTORY_TABLE, 'h')
->fields('h')
->condition('id', $update_id)
->execute()
->fetchAssoc();
if (!$update) {
return NULL;
}
// Get patches
$patches = $this->database->select(self::PATCHES_TABLE, 'p')
->fields('p')
->condition('update_id', $update_id)
->execute()
->fetchAll();
// Get errors
$errors = $this->database->select(self::ERRORS_TABLE, 'e')
->fields('e')
->condition('update_id', $update_id)
->execute()
->fetchAll();
return [
'update' => $update,
'patches' => $patches,
'errors' => $errors,
];
}
/**
* Gets success metrics for updates.
*
* @param string $period
* Time period ('day', 'week', 'month', 'all').
*
* @return array
* Success metrics.
*/
public function getSuccessMetrics($period = 'all') {
$query = $this->database->select(self::HISTORY_TABLE, 'h')
->fields('h', ['status']);
if ($period !== 'all') {
$timestamp = strtotime("-1 $period");
$query->condition('start_time', $timestamp, '>');
}
$results = $query->execute()->fetchAll();
$total = count($results);
$successful = count(array_filter($results, function ($r) {
return $r->status === 'success';
}));
return [
'total' => $total,
'successful' => $successful,
'success_rate' => $total ? ($successful / $total) * 100 : 0,
'period' => $period,
];
}
/**
* Updates success rate metrics in state.
*
* @param string $status
* The status of the latest update.
*/
protected function updateSuccessMetrics($status) {
$metrics = $this->state->get('ai_upgrade_assistant.success_metrics', [
'total' => 0,
'successful' => 0,
]);
$metrics['total']++;
if ($status === 'success') {
$metrics['successful']++;
}
$this->state->set('ai_upgrade_assistant.success_metrics', $metrics);
}
}
