ai_upgrade_assistant-0.2.0-alpha2/src/Controller/ReportController.php
src/Controller/ReportController.php
<?php
namespace Drupal\ai_upgrade_assistant\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Url;
use Drupal\ai_upgrade_assistant\Service\HuggingFaceService;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Controller for the AI Upgrade Assistant reports.
*/
class ReportController extends ControllerBase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The module extension list.
*
* @var \Drupal\Core\Extension\ModuleExtensionList
*/
protected $moduleList;
/**
* The HuggingFace service.
*
* @var \Drupal\ai_upgrade_assistant\Service\HuggingFaceService
*/
protected $huggingFace;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs a new ReportController.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ModuleExtensionList $module_list
* The module extension list.
* @param \Drupal\ai_upgrade_assistant\Service\HuggingFaceService $huggingFace
* The HuggingFace service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(
ModuleHandlerInterface $module_handler,
ModuleExtensionList $module_list,
HuggingFaceService $huggingFace,
StateInterface $state
) {
$this->moduleHandler = $module_handler;
$this->moduleList = $module_list;
$this->huggingFace = $huggingFace;
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('module_handler'),
$container->get('extension.list.module'),
$container->get('ai_upgrade_assistant.huggingface'),
$container->get('state')
);
}
/**
* Displays the overview page.
*/
public function overview() {
// Get results from state
$projects = $this->state->get('upgrade_status.project_summary', []);
$rows = [];
foreach ($projects as $project) {
if (empty($project['paths'])) {
continue;
}
$status = $this->t('Not analyzed');
if (!empty($project['data'])) {
$errors = $project['data']['totals']['errors'] ?? 0;
$warnings = $project['data']['totals']['warnings'] ?? 0;
$status = $this->t('@errors errors, @warnings warnings', [
'@errors' => $errors,
'@warnings' => $warnings,
]);
}
$ai_status = '';
$ai_results = $this->state->get('ai_upgrade_assistant.analysis_results.' . $project['name'], []);
if (!empty($ai_results)) {
$ai_status = $this->t('AI analysis available');
}
$rows[] = [
$project['name'],
$project['type'],
$status,
$ai_status,
[
'data' => [
'#type' => 'operations',
'#links' => [
'analyze' => [
'title' => $this->t('Get AI recommendations'),
'url' => Url::fromRoute('ai_upgrade_assistant.analyze', ['module' => $project['name']]),
],
],
],
],
];
}
$build['table'] = [
'#type' => 'table',
'#header' => [
$this->t('Name'),
$this->t('Type'),
$this->t('Status'),
$this->t('AI Analysis'),
$this->t('Operations'),
],
'#rows' => $rows,
'#empty' => $this->t('No modules found.'),
];
return $build;
}
/**
* Analyzes a specific module with AI assistance.
*
* @param string $module
* The module to analyze.
*/
public function analyze($module) {
$batch = [
'operations' => [
[
[$this, 'processAiAnalysis'],
[$module],
],
],
'finished' => [$this, 'finishAiAnalysis'],
'title' => $this->t('Analyzing @module with AI assistance', ['@module' => $module]),
'progress_message' => $this->t('Analyzing files...'),
'error_message' => $this->t('An error occurred during analysis.'),
];
batch_set($batch);
return batch_process(Url::fromRoute('ai_upgrade_assistant.report'));
}
/**
* Batch operation to process AI analysis.
*/
public function processAiAnalysis($module, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['current_id'] = 0;
$context['results'] = [];
// Get Upgrade Status results from state
$project = $this->state->get('upgrade_status.scan_results.' . $module, NULL);
if (!$project || empty($project['data'])) {
throw new \Exception('No Upgrade Status data available. Please run Upgrade Status scan first.');
}
$context['sandbox']['files'] = [];
foreach ($project['data']['files'] ?? [] as $file => $issues) {
if (!empty($issues)) {
$context['sandbox']['files'][] = [
'file' => $file,
'issues' => $issues,
];
}
}
$context['sandbox']['max'] = count($context['sandbox']['files']);
}
// Process one file at a time
if (!empty($context['sandbox']['files'])) {
$file_data = array_shift($context['sandbox']['files']);
$file = $file_data['file'];
$issues = $file_data['issues'];
try {
// Get file contents
$module_path = $this->moduleHandler->getModule($module)->getPath();
$file_path = $module_path . '/' . $file;
if (file_exists($file_path)) {
$code = file_get_contents($file_path);
// Get AI recommendations
$ai_analysis = $this->huggingFace->analyzeCode($code, [
'module' => $module,
'file' => $file,
'issues' => $issues,
'drupal_version' => \Drupal::VERSION,
]);
$context['results']['files'][$file] = [
'issues' => $issues,
'ai_recommendations' => $ai_analysis,
];
}
}
catch (\Exception $e) {
$context['results']['errors'][] = $e->getMessage();
}
$context['sandbox']['progress']++;
$context['message'] = $this->t('Analyzing file @file...', ['@file' => $file]);
}
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Batch finish callback.
*/
public function finishAiAnalysis($success, $results, $operations) {
if ($success) {
if (!empty($results['errors'])) {
foreach ($results['errors'] as $error) {
$this->messenger()->addError($error);
}
}
if (!empty($results['files'])) {
$this->messenger()->addStatus($this->t('AI analysis completed. Found recommendations for @count files.', [
'@count' => count($results['files']),
]));
}
// Store results
$this->state->set('ai_upgrade_assistant.analysis_results.' . $operations[0][1][0], $results);
}
else {
$this->messenger()->addError($this->t('An error occurred during analysis.'));
}
}
/**
* Generates a comprehensive upgrade report.
*
* @return array
* A render array for the report page.
*/
public function generateReport() {
try {
$modules = $this->moduleList->getList();
$report_data = [];
// Get scan results from state
$scan_results = $this->state->get('upgrade_status.project_summary', []);
foreach ($modules as $name => $extension) {
// Skip core modules
if (strpos($name, 'core') === 0) {
continue;
}
// Get scan results for this module
$module_scan_results = $this->state->get("upgrade_status.scan_results.$name", []);
if (!$module_scan_results) {
continue;
}
// Get deprecations from scan results
$deprecations = $module_scan_results['data']['files'] ?? [];
// Get AI analysis
$ai_analysis = $this->state->get("ai_upgrade_assistant.analysis_results.$name", []);
// Combine the results
$report_data[$name] = [
'name' => $name,
'type' => $extension->getType(),
'version' => $extension->info['version'] ?? 'Unknown',
'deprecations' => $deprecations,
'ai_analysis' => $ai_analysis,
'scan_results' => $module_scan_results,
'summary' => $scan_results[$name] ?? [],
];
}
// Build the page
return [
'#theme' => 'analysis_report',
'#report_data' => $report_data,
'#attached' => [
'library' => ['ai_upgrade_assistant/analysis_report'],
],
];
}
catch (\Exception $e) {
$this->logger('ai_upgrade_assistant')->error('Error generating report: @error', [
'@error' => $e->getMessage(),
]);
return [
'#type' => 'container',
'#attributes' => ['class' => ['error-message']],
'message' => [
'#markup' => '<p>' . $this->t('An error occurred while generating the report.') . '</p>',
],
];
}
}
/**
* Gets a formatted module status.
*
* @param int $errors
* Number of errors.
* @param int $warnings
* Number of warnings.
*
* @return array
* A render array for the status.
*/
protected function getModuleStatus($errors, $warnings) {
if ($errors === 0 && $warnings === 0) {
return [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $this->t('Compatible'),
'#attributes' => ['class' => ['status-compatible']],
];
}
elseif ($errors === 0) {
return [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $this->t('Needs Update'),
'#attributes' => ['class' => ['status-needs-update']],
];
}
else {
return [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $this->t('Incompatible'),
'#attributes' => ['class' => ['status-incompatible']],
];
}
}
}
