ai_upgrade_assistant-0.2.0-alpha2/src/Controller/UpgradeController.php

src/Controller/UpgradeController.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\ai_upgrade_assistant\Service\ProjectAnalyzer;
use Drupal\ai_upgrade_assistant\Service\PatchSearcher;
use Drupal\ai_upgrade_assistant\Service\BatchAnalyzer;
use Drupal\ai_upgrade_assistant\Service\HuggingFaceService;
use Drupal\ai_upgrade_assistant\Service\AchievementService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

/**
 * Controller for handling upgrade operations.
 */
class UpgradeController extends ControllerBase {

  /**
   * The state service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The module extension list.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleList;

  /**
   * The project analyzer service.
   *
   * @var \Drupal\ai_upgrade_assistant\Service\ProjectAnalyzer
   */
  protected $projectAnalyzer;

  /**
   * The patch searcher service.
   *
   * @var \Drupal\ai_upgrade_assistant\Service\PatchSearcher
   */
  protected $patchSearcher;

  /**
   * The batch analyzer service.
   *
   * @var \Drupal\ai_upgrade_assistant\Service\BatchAnalyzer
   */
  protected $batchAnalyzer;

  /**
   * The HuggingFace service.
   *
   * @var \Drupal\ai_upgrade_assistant\Service\HuggingFaceService
   */
  protected $huggingFace;

  /**
   * The achievement service.
   *
   * @var \Drupal\ai_upgrade_assistant\Service\AchievementService
   */
  protected $achievementService;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs a new UpgradeController object.
   *
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service.
   * @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\ProjectAnalyzer $project_analyzer
   *   The project analyzer service.
   * @param \Drupal\ai_upgrade_assistant\Service\PatchSearcher $patch_searcher
   *   The patch searcher service.
   * @param \Drupal\ai_upgrade_assistant\Service\BatchAnalyzer $batch_analyzer
   *   The batch analyzer service.
   * @param \Drupal\ai_upgrade_assistant\Service\HuggingFaceService $huggingFace
   *   The HuggingFace service.
   * @param \Drupal\ai_upgrade_assistant\Service\AchievementService $achievement_service
   *   The achievement service.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(
    StateInterface $state,
    ModuleHandlerInterface $module_handler,
    ModuleExtensionList $module_list,
    ProjectAnalyzer $project_analyzer,
    PatchSearcher $patch_searcher,
    BatchAnalyzer $batch_analyzer,
    HuggingFaceService $huggingFace,
    AchievementService $achievement_service,
    RequestStack $request_stack
  ) {
    $this->state = $state;
    $this->moduleHandler = $module_handler;
    $this->moduleList = $module_list;
    $this->projectAnalyzer = $project_analyzer;
    $this->patchSearcher = $patch_searcher;
    $this->batchAnalyzer = $batch_analyzer;
    $this->huggingFace = $huggingFace;
    $this->achievementService = $achievement_service;
    $this->requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('state'),
      $container->get('module_handler'),
      $container->get('extension.list.module'),
      $container->get('ai_upgrade_assistant.project_analyzer'),
      $container->get('ai_upgrade_assistant.patch_searcher'),
      $container->get('ai_upgrade_assistant.batch_analyzer'),
      $container->get('ai_upgrade_assistant.huggingface'),
      $container->get('ai_upgrade_assistant.achievement'),
      $container->get('request_stack')
    );
  }

  /**
   * Check environment requirements.
   */
  protected function checkEnvironment() {
    $checks = [];
    
    // Check PHP version
    $php_version = phpversion();
    $required_php = '8.1';
    $checks[] = [
      'title' => $this->t('PHP Version'),
      'value' => $php_version,
      'status' => version_compare($php_version, $required_php, '>='),
    ];

    // Check Drupal core version
    $drupal_version = \Drupal::VERSION;
    $checks[] = [
      'title' => $this->t('Drupal Core Version'),
      'value' => $drupal_version,
      'status' => version_compare($drupal_version, '10.0.0', '<'),
    ];

    // Check HuggingFace API key
    $api_key = $this->state->get('ai_upgrade_assistant.huggingface_api_key');
    $checks[] = [
      'title' => $this->t('HuggingFace API Key'),
      'value' => $api_key ? $this->t('Configured') : $this->t('Not configured'),
      'status' => !empty($api_key),
    ];

    // Check write permissions
    $module_path = $this->moduleHandler->getModule('ai_upgrade_assistant')->getPath();
    $is_writable = is_writable($module_path);
    $checks[] = [
      'title' => $this->t('Module Directory Permissions'),
      'value' => $is_writable ? $this->t('Writable') : $this->t('Not writable'),
      'status' => $is_writable,
    ];

    return $checks;
  }

  /**
   * Get modules grouped by status.
   *
   * @return array
   *   An array of modules grouped by status.
   */
  protected function getModulesByStatus() {
    $modules = [
      'compatible' => [],
      'needs_update' => [],
      'unknown' => [],
    ];

    $installed_modules = $this->moduleHandler->getModuleList();
    
    foreach ($installed_modules as $name => $extension) {
      // Get module info using the proper method
      $module_info = $this->moduleList->getExtensionInfo($name);
      
      if ($this->isModuleCompatible($name)) {
        $modules['compatible'][] = [
          'name' => [
            '#markup' => $module_info['name'],
          ],
          'machine_name' => [
            '#markup' => $name,
          ],
          'version' => [
            '#markup' => $module_info['version'] ?? 'Unknown',
          ],
          'status' => [
            '#markup' => 'compatible',
          ],
        ];
      }
      else {
        $modules['needs_update'][] = [
          'name' => [
            '#markup' => $module_info['name'],
          ],
          'machine_name' => [
            '#markup' => $name,
          ],
          'version' => [
            '#markup' => $module_info['version'] ?? 'Unknown',
          ],
          'status' => [
            '#markup' => 'needs_update',
          ],
        ];
      }
    }

    return $modules;
  }

  /**
   * Calculate overall progress.
   *
   * @return array
   *   Progress information.
   */
  protected function calculateProgress() {
    $modules = $this->getModulesByStatus();
    $total_modules = 0;
    $compatible_modules = 0;

    foreach ($modules as $status => $status_modules) {
      $total_modules += count($status_modules);
      if ($status === 'compatible') {
        $compatible_modules += count($status_modules);
      }
    }

    $percentage = $total_modules > 0 
      ? round(($compatible_modules / $total_modules) * 100) 
      : 0;

    return [
      'total' => $total_modules,
      'compatible' => $compatible_modules,
      'percentage' => $percentage,
    ];
  }

  /**
   * Automatically fixes all modules that can be automatically upgraded.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect response object.
   */
  public function autoFixAll() {
    $modules = $this->getModulesByStatus();
    $batch = [
      'title' => $this->t('Upgrading modules...'),
      'operations' => [],
      'finished' => [$this, 'batchFinished'],
      'progress_message' => $this->t('Processed @current out of @total modules.'),
      'error_message' => $this->t('An error occurred during the upgrade process.'),
    ];

    foreach ($modules['needs_update'] as $module) {
      if ($module['has_fix']) {
        $batch['operations'][] = [
          [$this, 'batchProcessModule'],
          [$module],
        ];
      }
    }

    batch_set($batch);
    return $this->redirect('ai_upgrade_assistant.status');
  }

  /**
   * Auto-fix a specific module.
   *
   * @param string $module_name
   *   The name of the module to auto-fix.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
   *   JSON response with the status of the operation or a redirect response.
   */
  public function autoFixModule($module_name) {
    try {
      if (empty($module_name)) {
        throw new \InvalidArgumentException('Module name is required.');
      }

      if (!$this->moduleHandler->moduleExists($module_name)) {
        return new JsonResponse([
          'status' => 'error',
          'message' => $this->t('Module @module does not exist.', ['@module' => $module_name]),
        ], 404);
      }

      // Get module path
      $module_path = $this->moduleHandler->getModule($module_name)->getPath();
      
      // Create batch operation
      $batch = [
        'title' => $this->t('Auto-fixing @module', ['@module' => $module_name]),
        'operations' => [
          [
            [$this->batchAnalyzer, 'analyzeModule'],
            [$module_name, $module_path],
          ],
          [
            [$this->projectAnalyzer, 'applyFixes'],
            [$module_name],
          ],
        ],
        'finished' => [$this->batchAnalyzer, 'finishBatch'],
        'file' => drupal_get_path('module', 'ai_upgrade_assistant') . '/src/Service/BatchAnalyzer.php',
        'progressive' => TRUE,
      ];

      batch_set($batch);

      // For AJAX requests, return JSON
      if ($this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
        return new JsonResponse([
          'status' => 'success',
          'message' => $this->t('Started auto-fix process for @module.', ['@module' => $module_name]),
        ]);
      }

      // For non-AJAX requests, redirect to status page
      return $this->redirect('ai_upgrade_assistant.status');
    }
    catch (\Exception $e) {
      $this->getLogger('ai_upgrade_assistant')->error(
        'Error auto-fixing module @module: @error',
        [
          '@module' => $module_name ?? 'unknown',
          '@error' => $e->getMessage(),
        ]
      );

      if ($this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
        return new JsonResponse([
          'status' => 'error',
          'message' => $this->t('Error auto-fixing module: @error', ['@error' => $e->getMessage()]),
        ], 500);
      }

      $this->messenger()->addError($this->t('Error auto-fixing module: @error', [
        '@error' => $e->getMessage(),
      ]));
      return $this->redirect('ai_upgrade_assistant.status');
    }
  }

  /**
   * Process a single module in the batch operation.
   *
   * @param array $module
   *   The module to process.
   * @param array &$context
   *   The batch context array.
   */
  public function batchProcessModule($module, &$context) {
    if (!isset($context['sandbox']['progress'])) {
      $context['sandbox']['progress'] = 0;
      $context['sandbox']['current_module'] = 0;
      $context['sandbox']['max'] = 1;
    }

    try {
      // Analyze module
      $analysis = $this->projectAnalyzer->analyzeModule($module['name']);
      
      // Search for patches
      $patches = $this->patchSearcher->findPatches($module['name'], $analysis);
      
      // Process module with batch analyzer
      $this->batchAnalyzer->processModule($module['name'], $analysis, $patches);

      $context['message'] = $this->t('Upgraded @name module.', ['@name' => $module['name']]);
      $context['results'][] = $module['name'];
    }
    catch (\Exception $e) {
      $context['results']['errors'][] = $this->t('Error upgrading @name: @error', [
        '@name' => $module['name'],
        '@error' => $e->getMessage(),
      ]);
    }

    $context['sandbox']['progress']++;
    $context['sandbox']['current_module']++;
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }

  /**
   * Finish batch processing.
   *
   * @param bool $success
   *   Whether the batch completed successfully.
   * @param array $results
   *   The batch results array.
   * @param array $operations
   *   The batch operations array.
   */
  public function batchFinished($success, $results, $operations) {
    if ($success) {
      if (!empty($results['errors'])) {
        foreach ($results['errors'] as $error) {
          $this->messenger()->addError($error);
        }
      }
      else {
        $count = count($results);
        $this->messenger()->addStatus($this->formatPlural(
          $count,
          'Successfully upgraded 1 module.',
          'Successfully upgraded @count modules.'
        ));
      }
    }
    else {
      $this->messenger()->addError($this->t('An error occurred while upgrading the modules.'));
    }
    
    $this->completeUpgrade($success);
  }

  /**
   * Displays the upgrade status page.
   *
   * @return array
   *   A render array for the status page.
   */
  public function status() {
    // Get environment checks
    $environment = $this->checkEnvironment();

    // Get modules by status
    $modules_by_status = $this->getModulesByStatus();

    // Calculate progress
    $progress = $this->calculateProgress();

    // Format the data for rendering
    $build = [
      '#theme' => 'ai_upgrade_assistant_status',
      '#environment' => [
        'items' => array_map(function($check) {
          return [
            'status' => [
              '#markup' => $check['status'] ? 'success' : 'error',
            ],
            'message' => [
              '#markup' => $this->t('@title: @value', [
                '@title' => $check['title'],
                '@value' => $check['value'],
              ]),
            ],
          ];
        }, $environment),
      ],
      '#progress' => [
        'data' => [
          '#markup' => $progress['percentage'],
        ],
      ],
      '#modules' => [
        'groups' => [],
      ],
      '#attached' => [
        'library' => [
          'ai_upgrade_assistant/status_page',
        ],
      ],
    ];

    // Group modules by status
    foreach ($modules_by_status as $status => $modules) {
      $build['#modules']['groups'][$status] = $modules;
    }

    return $build;
  }

  /**
   * Checks the current upgrade status.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response containing the current upgrade status.
   */
  public function checkStatus() {
    $response = [
      'status' => 'in_progress',
      'message' => '',
      'progress' => 0,
      'errors' => [],
      'terminal_output' => [],
    ];

    try {
      // Get stored state
      $state = $this->state->get('ai_upgrade_assistant.upgrade_status', []);
      if (!empty($state)) {
        $response = array_merge($response, $state);
      }

      // Calculate current progress
      $progress = $this->calculateProgress();
      $response['progress'] = $progress['percentage'];

      // Get modules by status
      $modules = $this->getModulesByStatus();
      $response['modules'] = [
        'compatible' => count($modules['compatible']),
        'needs_update' => count($modules['needs_update']),
      ];

      // Check if process is complete
      if ($progress['percentage'] === 100) {
        $response['status'] = 'complete';
        $response['message'] = $this->t('Upgrade process completed successfully.');
      }

      // Get terminal output
      $terminal_output = $this->state->get('ai_upgrade_assistant.terminal_output', []);
      if (!empty($terminal_output)) {
        $response['terminal_output'] = $terminal_output;
      }
    }
    catch (\Exception $e) {
      $response['status'] = 'error';
      $response['message'] = $this->t('Error checking upgrade status: @error', ['@error' => $e->getMessage()]);
      $response['errors'][] = $e->getMessage();
    }

    return new JsonResponse($response);
  }

  /**
   * Initialize the terminal output with default messages.
   */
  protected function initializeTerminalOutput() {
    $this->state->set('ai_upgrade_assistant.terminal_output', [
      [
        'message' => $this->t('Initializing AI Upgrade Assistant...'),
        'type' => 'info',
        'timestamp' => time(),
      ],
    ]);
  }

  /**
   * Add a message to the terminal output.
   *
   * @param string $message
   *   The message to add.
   * @param string $type
   *   The message type (info, success, warning, error).
   *
   * @return array
   *   The updated terminal output array.
   */
  protected function addTerminalMessage($message, $type = 'info') {
    $terminal_output = $this->state->get('ai_upgrade_assistant.terminal_output', []);
    
    $terminal_output[] = [
      'message' => $message,
      'type' => $type,
      'timestamp' => time(),
    ];
    
    $this->state->set('ai_upgrade_assistant.terminal_output', $terminal_output);
    
    return $terminal_output;
  }

  /**
   * Get the installed Composer version.
   *
   * @return string
   *   The Composer version string.
   */
  protected function getComposerVersion() {
    try {
      $process = new Process(['composer', '--version']);
      $process->run();
      
      if (!$process->isSuccessful()) {
        throw new ProcessFailedException($process);
      }
      
      $output = $process->getOutput();
      preg_match('/Composer version ([0-9.]+)/', $output, $matches);
      
      return $matches[1] ?? '0.0.0';
    }
    catch (\Exception $e) {
      return '0.0.0';
    }
  }

  /**
   * Convert PHP memory limit string to bytes.
   *
   * @param string $memory_limit
   *   Memory limit string (e.g., '128M', '1G').
   *
   * @return int
   *   Memory limit in bytes.
   */
  protected function convertToBytes($memory_limit) {
    $value = (int) $memory_limit;
    
    switch (strtolower(substr($memory_limit, -1))) {
      case 'g':
        $value *= 1024;
      case 'm':
        $value *= 1024;
      case 'k':
        $value *= 1024;
    }
    
    return $value;
  }

  /**
   * Check if a module is compatible with Drupal 11.
   *
   * @param string $name
   *   The module name.
   *
   * @return bool
   *   TRUE if the module is compatible, FALSE otherwise.
   */
  protected function isModuleCompatible($name) {
    // For now, just return FALSE to indicate all modules need fixing
    return FALSE;
  }

  /**
   * Check if a module has a known fix available.
   *
   * @param string $name
   *   The module name.
   *
   * @return bool
   *   TRUE if a fix is available, FALSE otherwise.
   */
  protected function hasKnownFix($name) {
    // For now, return TRUE for testing
    return TRUE;
  }

  /**
   * Handles the completion of an upgrade process.
   *
   * @param bool $success
   *   Whether the upgrade was successful.
   * @param int $complexity
   *   The complexity of the upgrade.
   */
  protected function completeUpgrade($success, $complexity = 3) {
    $account = $this->currentUser();
    
    // Award points based on success and complexity
    $this->achievementService->awardUpgradePoints(
      $account->id(),
      $success,
      $complexity
    );
    
    // If successful, check for pattern contribution
    if ($success) {
      $pattern_quality = min(5, ceil($complexity * 1.5));
      $this->achievementService->awardPatternPoints(
        $account->id(),
        $pattern_quality
      );
    }
  }

  /**
   * Gets the current rate limit status.
   *
   * @return array
   *   Render array for the rate limit status page.
   */
  public function getRateLimitStatus() {
    $rate_limits = $this->huggingFace->getRateLimits();
    
    return [
      '#theme' => 'rate_limit_status',
      '#rate_limits' => [
        'current_usage' => [
          '#markup' => $rate_limits['current_usage'] ?? 0,
        ],
        'limit' => [
          '#markup' => $rate_limits['limit'] ?? 'Unknown',
        ],
        'reset_time' => [
          '#markup' => $rate_limits['reset_time'] 
            ? date('Y-m-d H:i:s', $rate_limits['reset_time']) 
            : $this->t('Unknown'),
        ],
      ],
      '#cache' => [
        'max-age' => 60, // Cache for 1 minute
      ],
    ];
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc