ai_upgrade_assistant-0.2.0-alpha2/src/Service/CommunityLearningService.php
src/Service/CommunityLearningService.php
<?php
namespace Drupal\ai_upgrade_assistant\Service;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Queue\QueueFactory;
use GuzzleHttp\ClientInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* Service for community learning and pattern sharing.
*/
class CommunityLearningService {
use DependencySerializationTrait;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The queue factory.
*
* @var \Drupal\Core\Queue\QueueFactory
*/
protected $queueFactory;
/**
* The HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* Constructs a new CommunityLearningService object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend.
* @param \Drupal\Core\Queue\QueueFactory $queue_factory
* The queue factory.
* @param \GuzzleHttp\ClientInterface $http_client
* The HTTP client.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
Connection $database,
EntityTypeManagerInterface $entity_type_manager,
AccountProxyInterface $current_user,
StateInterface $state,
LoggerChannelFactoryInterface $logger_factory,
CacheBackendInterface $cache,
QueueFactory $queue_factory,
ClientInterface $http_client
) {
$this->configFactory = $config_factory;
$this->database = $database;
$this->entityTypeManager = $entity_type_manager;
$this->currentUser = $current_user;
$this->state = $state;
$this->loggerFactory = $logger_factory;
$this->cache = $cache;
$this->queueFactory = $queue_factory;
$this->httpClient = $http_client;
}
/**
* Creates a forum topic.
*
* @param string $title
* The topic title.
* @param string $body
* The topic body.
* @param array $tags
* Optional tags for the topic.
*
* @return int|bool
* The topic ID if successful, FALSE otherwise.
*/
public function createForumTopic($title, $body, array $tags = []) {
try {
$fields = [
'title' => $title,
'body' => $body,
'uid' => $this->currentUser->id(),
'created' => time(),
'changed' => time(),
];
if (!empty($tags)) {
$fields['tags'] = Json::encode($tags);
}
$id = $this->database->insert('ai_upgrade_assistant_forum_topics')
->fields($fields)
->execute();
$this->loggerFactory->get('ai_upgrade_assistant')->info(
'Forum topic created: @title',
['@title' => $title]
);
return $id;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error creating forum topic: @error',
['@error' => $e->getMessage()]
);
return FALSE;
}
}
/**
* Gets forum topics with optional filtering.
*
* @param array $filters
* Filters to apply to the query.
* @param int $limit
* Number of topics to return.
* @param int $offset
* Offset for pagination.
*
* @return array
* Array of forum topics.
*/
public function getForumTopics(array $filters = [], $limit = 10, $offset = 0) {
try {
$query = $this->database->select('ai_upgrade_assistant_forum_topics', 't')
->fields('t')
->orderBy('created', 'DESC')
->range($offset, $limit);
if (!empty($filters['uid'])) {
$query->condition('uid', $filters['uid']);
}
if (!empty($filters['tag'])) {
$query->condition('tags', '%' . $filters['tag'] . '%', 'LIKE');
}
$result = $query->execute();
$topics = [];
foreach ($result as $record) {
$topics[] = (array) $record;
}
return $topics;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error fetching forum topics: @error',
['@error' => $e->getMessage()]
);
return [];
}
}
/**
* Gets shared upgrade patterns for a module.
*
* @param array $context
* The context array containing module information.
*
* @return array
* Array of shared patterns.
*/
public function getSharedPatterns(array $context) {
$cid = 'ai_upgrade_assistant:shared_patterns:' . md5(serialize($context));
$cached = $this->cache->get($cid);
if ($cached) {
return $cached->data;
}
try {
$query = $this->database->select('ai_upgrade_assistant_patterns', 'p')
->fields('p');
if (!empty($context['task_type'])) {
$query->condition('p.task_type', $context['task_type']);
}
if (!empty($context['project'])) {
$query->condition('p.module_name', $context['project']);
}
$patterns = $query->execute()->fetchAll();
$this->cache->set($cid, $patterns, time() + 3600);
return $patterns;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error fetching shared patterns: @error',
['@error' => $e->getMessage()]
);
return [];
}
}
/**
* Gets security advisories for a module.
*
* @param string $module
* The module name.
*
* @return array
* Array of security advisories.
*/
public function getSecurityAdvisories($module) {
$cid = 'ai_upgrade_assistant:security_advisories:' . $module;
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
try {
// Fetch security advisories from Drupal.org
$url = sprintf('https://www.drupal.org/api/sa?project=%s', $module);
$response = $this->httpClient->get($url);
$data = Json::decode($response->getBody());
$advisories = [];
if (!empty($data['list'])) {
foreach ($data['list'] as $advisory) {
$advisories[] = [
'title' => $advisory['title'],
'link' => $advisory['url'],
'severity' => $advisory['field_security_advisory_severity'] ?? 'Unknown',
'date' => $advisory['created'],
'status' => $advisory['field_sa_status'] ?? 'Unknown',
];
}
}
// Cache for 1 hour
$this->cache->set($cid, $advisories, time() + 3600);
return $advisories;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error fetching security advisories for @module: @error',
[
'@module' => $module,
'@error' => $e->getMessage(),
]
);
return [];
}
}
/**
* Gets statistics about the community patterns.
*
* @return array
* Array of statistics.
*/
public function getDatasetStats() {
$cid = 'ai_upgrade_assistant:community_stats';
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
try {
$stats = [
'total_patterns' => $this->database->select('ai_upgrade_assistant_patterns', 'p')
->countQuery()
->execute()
->fetchField(),
'total_topics' => $this->database->select('ai_upgrade_assistant_forum_topics', 't')
->countQuery()
->execute()
->fetchField(),
'unique_contributors' => $this->database->select('ai_upgrade_assistant_patterns', 'p')
->fields('p', ['uid'])
->distinct()
->countQuery()
->execute()
->fetchField(),
'last_update' => $this->database->select('ai_upgrade_assistant_patterns', 'p')
->fields('p', ['created'])
->orderBy('created', 'DESC')
->range(0, 1)
->execute()
->fetchField(),
];
$this->cache->set($cid, $stats, time() + 3600);
return $stats;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error fetching community statistics: @error',
['@error' => $e->getMessage()]
);
return [];
}
}
/**
* Shares a pattern with the community.
*
* @param string $title
* Pattern title.
* @param string $description
* Pattern description.
* @param array $pattern_data
* The pattern data to share.
* @param array $tags
* Optional tags for the pattern.
*
* @return bool
* TRUE if successful, FALSE otherwise.
*/
public function sharePattern($title, $description, array $pattern_data, array $tags = []) {
try {
$fields = [
'type' => $pattern_data['type'] ?? 'general',
'module_name' => $pattern_data['module_name'],
'pattern_data' => Json::encode($pattern_data),
'created' => time(),
];
$this->database->insert('ai_upgrade_assistant_patterns')
->fields($fields)
->execute();
$this->loggerFactory->get('ai_upgrade_assistant')->info(
'Pattern shared: @title',
['@title' => $title]
);
// Invalidate relevant caches
$this->cache->invalidateTags(['ai_upgrade_assistant:patterns']);
return TRUE;
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error sharing pattern: @error',
['@error' => $e->getMessage()]
);
return FALSE;
}
}
/**
* Finds similar patterns for a file.
*
* @param string $file
* The file path.
*
* @return array
* Array of similar patterns.
*/
public function findSimilarPatterns($file) {
try {
// Extract module name from file path
if (preg_match('#modules/(?:contrib|custom)/([^/]+)/#', $file, $matches)) {
$module_name = $matches[1];
}
else {
$this->loggerFactory->get('ai_upgrade_assistant')->notice(
'Could not extract module name from file path: @file',
['@file' => $file]
);
return [];
}
// Skip empty module names
if (empty($module_name)) {
$this->loggerFactory->get('ai_upgrade_assistant')->notice(
'Empty module name extracted from file path: @file',
['@file' => $file]
);
return [];
}
// Get all patterns and filter in PHP
$query = $this->database->select('ai_upgrade_assistant_patterns', 'p')
->fields('p');
$patterns = $query->execute()->fetchAll();
// Filter patterns based on module name and task type
return array_filter($patterns, function ($pattern) use ($module_name) {
// If we have a module_name column and it matches, use that
if (isset($pattern->module_name) && $pattern->module_name === $module_name) {
return TRUE;
}
// Otherwise check the pattern data
try {
$data = unserialize($pattern->pattern_data);
if (!empty($data['module']) && $data['module'] === $module_name) {
return TRUE;
}
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->warning(
'Error unserializing pattern data: @error',
['@error' => $e->getMessage()]
);
}
return FALSE;
});
}
catch (\Exception $e) {
$this->loggerFactory->get('ai_upgrade_assistant')->error(
'Error fetching shared patterns: @error',
['@error' => $e->getMessage()]
);
return [];
}
}
}
