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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# AI Sentiments Analysis

AI-powered multi-dimensional text analysis measuring trust, objectivity, audience
targeting, and reading levels.
AI-powered multi-dimensional text analysis measuring trust, objectivity,
audience targeting, and reading levels.

## Features

- **Four Analysis Dimensions**: Trust & Credibility, Objectivity & Bias, Audience
Vibe Check, CEFR Reading Level
- **Four Analysis Dimensions**: Trust & Credibility, Objectivity & Bias,
Audience Vibe Check, CEFR Reading Level
- **Flexible Configuration**: Add, remove, reorder sentiments dimensions via UI
- **Content Type Control**: Enable/disable specific analysis types per content
type
Expand Down
3 changes: 2 additions & 1 deletion analyze_ai_sentiments.install
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ function analyze_ai_sentiments_uninstall() {
}

/**
* Update plugin ID from ai_sentiments_analyzer to analyze_ai_sentiments_analyzer.
* Update plugin ID from ai_sentiments_analyzer to
* analyze_ai_sentiments_analyzer.
*/
function analyze_ai_sentiments_update_8001() {
$config_factory = \Drupal::configFactory();
Expand Down
5 changes: 0 additions & 5 deletions analyze_ai_sentiments.links.action.yml

This file was deleted.

35 changes: 35 additions & 0 deletions analyze_ai_sentiments.module
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\views\ViewExecutable;

/**
* Implements hook_entity_update().
Expand Down Expand Up @@ -44,3 +47,35 @@ function _analyze_ai_sentiments_is_supported_entity(EntityInterface $entity): bo

return isset($status[$entity_type_id][$bundle]['ai_sentiments_analyzer']);
}

/**
* Implements hook_views_pre_view().
*/
function analyze_ai_sentiments_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
// Add settings link to the sentiments analysis view header.
if ($view->id() === 'ai_sentiments_analysis_results' && $display_id === 'page_1') {
// Build the settings link if user has permission.
$current_user = \Drupal::currentUser();
if ($current_user->hasPermission('administer analyze settings')) {
$settings_url = Url::fromRoute('analyze_ai_sentiments.settings');
if ($settings_url->access()) {
$link = Link::fromTextAndUrl(t('Configure settings'), $settings_url);
$link = $link->toRenderable();
$link['#attributes']['class'][] = 'button';
$link['#attributes']['class'][] = 'button--small';
$link['#attributes']['class'][] = 'button--primary';

// Add content before the view using attachment_before as render array.
$view->attachment_before = [
'#type' => 'container',
'#attributes' => ['class' => ['form-actions', 'views-configure-actions']],
'#weight' => -10,
'configure_link' => $link,
'#attached' => [
'library' => ['system/base'],
],
];
}
}
}
}
2 changes: 1 addition & 1 deletion analyze_ai_sentiments.routing.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
analyze_ai_sentiments.settings:
path: '/admin/config/analyze/sentiments'
defaults:
_form: '\Drupal\analyze_ai_sentiments\Form\SentimentsettingsForm'
_form: '\Drupal\analyze_ai_sentiments\Form\SentimentsSettingsForm'
_title: 'Sentiments Analysis Settings'
requirements:
_permission: 'administer site configuration'
Expand Down
2 changes: 1 addition & 1 deletion analyze_ai_sentiments.services.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
analyze_ai_sentiments.storage:
class: Drupal\analyze_ai_sentiments\Service\SentimentstorageService
class: Drupal\analyze_ai_sentiments\Service\SentimentsStorageService
arguments: ['@database', '@config.factory', '@entity_type.manager', '@renderer', '@datetime.time']

analyze_ai_sentiments.batch_service:
Expand Down
5 changes: 3 additions & 2 deletions config/install/views.view.ai_sentiments_analysis_results.yml
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,9 @@ display:
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: none
options: { }
type: perm
options:
perm: 'view analyze results'
cache:
type: tag
options: { }
Expand Down
33 changes: 27 additions & 6 deletions src/Form/SentimentsSettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\analyze_ai_sentiments\Service\SentimentstorageService;
use Drupal\analyze_ai_sentiments\Service\SentimentsStorageService;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Configure sentiments analysis settings.
*/
class SentimentsettingsForm extends ConfigFormBase {
class SentimentsSettingsForm extends ConfigFormBase {
/**
* The sentiments storage service.
*/
protected SentimentstorageService $sentimentstorage;
protected SentimentsStorageService $sentimentstorage;

/**
* Constructs a SentimentsettingsForm object.
* Constructs a SentimentsSettingsForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
* The typed config manager.
* @param \Drupal\analyze_ai_sentiments\Service\SentimentstorageService $sentiments_storage
* @param \Drupal\analyze_ai_sentiments\Service\SentimentsStorageService $sentiments_storage
* The sentiments storage service.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
TypedConfigManagerInterface $typed_config_manager,
SentimentstorageService $sentiments_storage,
SentimentsStorageService $sentiments_storage,
) {
parent::__construct($config_factory, $typed_config_manager);
$this->sentimentstorage = $sentiments_storage;
Expand Down Expand Up @@ -129,6 +129,27 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
],
];

// Add link to reports page if user has permission.
$current_user = \Drupal::currentUser();
if ($current_user->hasPermission('access site reports')) {
$reports_url = Url::fromRoute('view.ai_sentiments_analysis_results.page_1');
if ($reports_url->access()) {
$form['actions_top'] = [
'#type' => 'container',
'#attributes' => ['class' => ['form-actions']],
'#weight' => -10,
'report_link' => [
'#type' => 'link',
'#title' => $this->t('View reports'),
'#url' => $reports_url,
'#attributes' => [
'class' => ['button', 'button--small', 'button--primary'],
],
],
];
}
}

$form['table'] = [
'#type' => 'container',
'#attributes' => ['class' => ['sentiments-table-container']],
Expand Down
20 changes: 7 additions & 13 deletions src/Plugin/Analyze/AISentimentsAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\ai\Service\PromptJsonDecoder\PromptJsonDecoderInterface;
use Drupal\Core\Link;
use Drupal\analyze_ai_sentiments\Service\SentimentstorageService;
use Drupal\analyze_ai_sentiments\Service\SentimentsStorageService;

/**
* A sentiments analyzer that uses AI to analyze content sentiments.
Expand Down Expand Up @@ -58,9 +58,9 @@ final class AISentimentsAnalyzer extends AnalyzePluginBase {
/**
* The sentiments storage service.
*
* @var \Drupal\analyze_ai_sentiments\Service\SentimentstorageService
* @var \Drupal\analyze_ai_sentiments\Service\SentimentsStorageService
*/
protected SentimentstorageService $storage;
protected SentimentsStorageService $storage;

/**
* Creates the plugin.
Expand Down Expand Up @@ -89,7 +89,7 @@ final class AISentimentsAnalyzer extends AnalyzePluginBase {
* The messenger service.
* @param \Drupal\ai\Service\PromptJsonDecoder\PromptJsonDecoderInterface $promptJsonDecoder
* The prompt JSON decoder service.
* @param \Drupal\analyze_ai_sentiments\Service\SentimentstorageService $storage
* @param \Drupal\analyze_ai_sentiments\Service\SentimentsStorageService $storage
* The sentiments storage service.
*/
public function __construct(
Expand All @@ -105,7 +105,7 @@ public function __construct(
protected LanguageManagerInterface $languageManager,
MessengerInterface $messenger,
PromptJsonDecoderInterface $promptJsonDecoder,
SentimentstorageService $storage,
SentimentsStorageService $storage,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $helper, $currentUser);
$this->aiProvider = $aiProvider;
Expand Down Expand Up @@ -156,7 +156,7 @@ protected function getConfiguredSentiments(): array {
if (empty($sentiments)) {
// Load defaults from the settings form.
$form = \Drupal::classResolver()
->getInstanceFromDefinition('\Drupal\analyze_ai_sentiments\Form\SentimentsettingsForm');
->getInstanceFromDefinition('\Drupal\analyze_ai_sentiments\Form\SentimentsSettingsForm');
return $form->getDefaultSentiments();
}

Expand Down Expand Up @@ -379,13 +379,6 @@ public function renderFullReport(EntityInterface $entity): array {
],
];

$build['title'] = [
'#type' => 'html_tag',
'#tag' => 'h2',
// @phpstan-ignore-next-line
'#value' => $this->t('Sentiments Analysis'),
];

foreach ($enabled_sentiments as $id => $sentiments) {
if (isset($scores[$id])) {
// Convert -1 to +1 range to 0 to 1 for gauge.
Expand Down Expand Up @@ -604,6 +597,7 @@ public function saveSettings(string $entity_type_id, ?string $bundle, array $set
* {@inheritdoc}
*
* @return array<string, mixed>
* Default settings array.
*/
public function getDefaultSettings(): array {
$sentiments = $this->getConfiguredSentiments();
Expand Down
11 changes: 7 additions & 4 deletions src/Service/SentimentsBatchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

namespace Drupal\analyze_ai_sentiments\Service;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\analyze\AnalyzePluginManager;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;

Expand All @@ -20,10 +21,10 @@ final class SentimentsBatchService {

public function __construct(
private readonly EntityTypeManagerInterface $entityTypeManager,
private readonly SentimentstorageService $storage,
private readonly SentimentsStorageService $storage,
private readonly ConfigFactoryInterface $configFactory,
private readonly EntityTypeBundleInfoInterface $bundleInfo,
private readonly DefaultPluginManager $analyzePluginManager,
private readonly AnalyzePluginManager $analyzePluginManager,
) {
}

Expand Down Expand Up @@ -137,10 +138,12 @@ public function processBatch(array $entities, bool $force_refresh, int $total_en
}
}
}
catch (\Exception $e) {
catch (PluginException $e) {
$context['results']['errors'][] = $this->t('Batch processing error: @message', [
'@message' => $e->getMessage(),
])->render();
// If the analyzer cannot be created, we cannot proceed.
return;
}

$context['message'] = $this->t('Processed @current of @max entities...', [
Expand Down
2 changes: 1 addition & 1 deletion src/Service/SentimentsStorageService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* Service for storing and retrieving sentiments analysis results.
*/
final class SentimentstorageService {
final class SentimentsStorageService {
use DependencySerializationTrait;

public function __construct(
Expand Down
Loading