Skip to content

Commit fdbb5b3

Browse files
committed
refactor: rework queueing, use enums for most consistent messages, fix issues and logic and start adding basic tests.
Refs: RW-1109
1 parent 20d838a commit fdbb5b3

24 files changed

+773
-247
lines changed

ocha_content_classification.install

+13-1
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,23 @@ function ocha_content_classification_schema() {
3030
'not null' => TRUE,
3131
'description' => 'The ID of the classified entity.',
3232
],
33+
'entity_revision_id' => [
34+
'type' => 'int',
35+
'unsigned' => TRUE,
36+
'not null' => TRUE,
37+
'description' => 'The revision ID of the classified entity.',
38+
],
39+
'user_id' => [
40+
'type' => 'int',
41+
'unsigned' => TRUE,
42+
'not null' => TRUE,
43+
'description' => 'The ID of the user who initiated the creation of this record.',
44+
],
3345
'status' => [
3446
'type' => 'varchar_ascii',
3547
'length' => 32,
3648
'not null' => TRUE,
37-
'description' => 'The status of the classification (queued, processed or failed).',
49+
'description' => 'The status of the classification (queued, completed or failed).',
3850
],
3951
'attempts' => [
4052
'type' => 'int',

src/Access/ClassificationRequeueAccessCheck.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function __construct(
2828
) {}
2929

3030
/**
31-
* Checks access to the relationship field on the given route.
31+
* Check access to the relationship field on the given route.
3232
*
3333
* @param \Symfony\Component\Routing\Route $route
3434
* The route to check against.

src/Attribute/OchaContentClassifier.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class OchaContentClassifier extends Plugin {
2424

2525
/**
26-
* Constructs a classifier attribute.
26+
* Constructor.
2727
*
2828
* @param string $id
2929
* The plugin ID.

src/Entity/ClassificationWorkflow.php

+105-29
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77
use Drupal\Core\Config\Entity\ConfigEntityBase;
88
use Drupal\Core\Database\Connection;
99
use Drupal\Core\Entity\ContentEntityInterface;
10-
use Drupal\ocha_content_classification\Exception\AlreadyProcessedException;
10+
use Drupal\Core\Session\AccountProxyInterface;
11+
use Drupal\ocha_content_classification\Enum\ClassificationMessage;
12+
use Drupal\ocha_content_classification\Enum\ClassificationStatus;
13+
use Drupal\ocha_content_classification\Exception\AttemptsLimitReachedException;
14+
use Drupal\ocha_content_classification\Exception\ClassificationCompletedException;
1115
use Drupal\ocha_content_classification\Exception\ClassificationFailedException;
16+
use Drupal\ocha_content_classification\Exception\FieldAlreadySpecifiedException;
1217
use Drupal\ocha_content_classification\Exception\UnsupportedEntityException;
18+
use Drupal\ocha_content_classification\Exception\WorkflowNotEnabledException;
1319
use Drupal\ocha_content_classification\Helper\EntityHelper;
1420
use Drupal\ocha_content_classification\Plugin\ClassifierPluginInterface;
1521
use Drupal\ocha_content_classification\Plugin\ClassifierPluginManagerInterface;
@@ -126,6 +132,13 @@ class ClassificationWorkflow extends ConfigEntityBase implements ClassificationW
126132
*/
127133
protected Connection $database;
128134

135+
/**
136+
* The current user.
137+
*
138+
* @var \Drupal\Core\Session\AccountProxyInterface
139+
*/
140+
protected AccountProxyInterface $currentUser;
141+
129142
/**
130143
* {@inheritdoc}
131144
*/
@@ -348,6 +361,12 @@ public function classifyEntity(ContentEntityInterface $entity): bool {
348361
* {@inheritdoc}
349362
*/
350363
public function validateEntity(ContentEntityInterface $entity, bool $check_status = TRUE): bool {
364+
if (!$this->enabled()) {
365+
throw new WorkflowNotEnabledException(strtr('Workflow @id not enabled.', [
366+
'@id' => $this->id(),
367+
]));
368+
}
369+
351370
if ($entity->getEntityTypeId() !== $this->getTargetEntityTypeId() || $entity->bundle() !== $this->getTargetBundle()) {
352371
throw new UnsupportedEntityException('Entity type or bundle not supported.');
353372
}
@@ -357,25 +376,26 @@ public function validateEntity(ContentEntityInterface $entity, bool $check_statu
357376
$status = $existing_record['status'] ?? '';
358377
$attempts = $existing_record['attempts'] ?? 0;
359378

360-
// Skip if the entity is already processed.
361-
if ($check_status && $status === 'processed') {
362-
throw new AlreadyProcessedException(strtr('@bundle_label @id already processed.', [
379+
// Skip if the classification is marked as completed.
380+
if ($check_status && $status === ClassificationStatus::COMPLETED) {
381+
throw new ClassificationCompletedException(strtr('Classification already completed for @bundle_label @id.', [
363382
'@bundle_label' => $bundle_label,
364383
'@id' => $entity->id(),
365384
]));
366385
}
367386

368387
// Skip if the classification is marked as failure.
369-
if ($check_status && $status === 'failed') {
370-
throw new ClassificationFailedException(strtr('Classification failed for @bundle_label @id.', [
388+
if ($check_status && $status === ClassificationStatus::FAILED) {
389+
throw new ClassificationFailedException(strtr('Classification previously failed for @bundle_label @id.', [
371390
'@bundle_label' => $bundle_label,
372391
'@id' => $entity->id(),
373392
]));
374393
}
375394

376395
// Skip if we reached the maximum number of classification attempts.
377396
if ($check_status && $attempts >= $this->getAttemptsLimit()) {
378-
throw new ClassificationFailedException(strtr('Maximum classification attempts reached for @bundle_label @id.', [
397+
throw new AttemptsLimitReachedException(strtr('Limit of @limit classification attempts reached for @bundle_label @id.', [
398+
'@limit' => $this->getAttemptsLimit(),
379399
'@bundle_label' => $bundle_label,
380400
'@id' => $entity->id(),
381401
]));
@@ -394,8 +414,11 @@ public function validateEntity(ContentEntityInterface $entity, bool $check_statu
394414
}
395415

396416
// The field is not empty, we consider the classification done.
417+
// @todo review adding some settings to the workflow to control the
418+
// behavior when a classifiable is already specified but other
419+
// classifiable fields are not.
397420
if (!$entity->get($field_name)->isEmpty()) {
398-
throw new AlreadyProcessedException(strtr('@field_label already specified for @bundle_label @id.', [
421+
throw new FieldAlreadySpecifiedException(strtr('@field_label already specified for @bundle_label @id.', [
399422
'@field_label' => $entity->get($field_name)->getFieldDefinition()->getLabel(),
400423
'@bundle_label' => $bundle_label,
401424
'@id' => $entity->id(),
@@ -413,35 +436,47 @@ public function validateEntity(ContentEntityInterface $entity, bool $check_statu
413436
*/
414437
public function updateClassificationProgress(
415438
ContentEntityInterface $entity,
416-
string $message,
417-
string $status,
439+
ClassificationMessage $message,
440+
ClassificationStatus $status,
418441
bool $new = FALSE,
419-
): string {
442+
): void {
420443
// Extract necessary information from the entity.
421444
$entity_type_id = $entity->getEntityTypeId();
422445
$entity_bundle = $entity->bundle();
423446
$entity_id = $entity->id();
447+
$entity_revision_id = $entity->getRevisionId();
448+
449+
// When creating or resetting a record, if the status is not queued, then
450+
// we consider there was one attempt already.
451+
$new_record_attempts = ($new && $status === ClassificationStatus::QUEUED) ? 0 : 1;
452+
453+
// Get the current timestamp.
454+
$time = time();
455+
456+
// Get the ID of the classifier plugin.
457+
$classifier = $this->getClassifierPluginId();
424458

425459
// Retrieve the existing progress record if any.
426460
$existing_record = $this->getClassificationProgress($entity);
427461

428462
if (!empty($existing_record)) {
429-
// Update existing record: increment attempts and update status/message.
463+
// Update existing record. If new was specified, reset the user ID,
464+
// creation time and attempts.
430465
$this->getDatabase()->update('ocha_content_classification_progress')
431466
->fields([
432-
'status' => $status,
433-
'attempts' => $new ? 1 : $existing_record['attempts'] + 1,
434-
'created' => $new ? time() : $existing_record['created'],
435-
'changed' => time(),
436-
'message' => $message,
437-
'classifier' => $this->getClassifierPluginId(),
467+
'entity_revision_id' => $entity_revision_id,
468+
'user_id' => $new ? $this->getCurrentUser()->id() : $existing_record['user_id'],
469+
'status' => $status->value,
470+
'attempts' => $new ? $new_record_attempts : $existing_record['attempts'] + 1,
471+
'created' => $new ? $time : $existing_record['created'],
472+
'changed' => $time,
473+
'message' => $message->value,
474+
'classifier' => $classifier,
438475
])
439476
->condition('entity_type_id', $entity_type_id)
440477
->condition('entity_bundle', $entity_bundle)
441478
->condition('entity_id', $entity_id)
442479
->execute();
443-
444-
return $existing_record['status'];
445480
}
446481
else {
447482
// Insert a new record.
@@ -450,30 +485,58 @@ public function updateClassificationProgress(
450485
'entity_type_id' => $entity_type_id,
451486
'entity_bundle' => $entity_bundle,
452487
'entity_id' => $entity_id,
453-
'status' => $status,
454-
'attempts' => $status === 'queued' ? 0 : 1,
455-
'created' => time(),
456-
'changed' => time(),
457-
'message' => $message,
458-
'classifier' => $this->getClassifierPluginId(),
488+
'entity_revision_id' => $entity_revision_id,
489+
'user_id' => $this->getCurrentUser()->id(),
490+
'status' => $status->value,
491+
'attempts' => $new_record_attempts,
492+
'created' => $time,
493+
'changed' => $time,
494+
'message' => $message->value,
495+
'classifier' => $classifier,
459496
])
460497
->execute();
461-
462-
return '';
463498
}
464499
}
465500

466501
/**
467502
* {@inheritdoc}
468503
*/
469504
public function getClassificationProgress(ContentEntityInterface $entity): ?array {
470-
return $this->getDatabase()->select('ocha_content_classification_progress', 'ocp')
505+
$record = $this->getDatabase()->select('ocha_content_classification_progress', 'ocp')
471506
->fields('ocp')
472507
->condition('entity_type_id', $entity->getEntityTypeId())
473508
->condition('entity_bundle', $entity->bundle())
474509
->condition('entity_id', $entity->id())
475510
->execute()
476511
?->fetchAssoc() ?: NULL;
512+
513+
// Ensure the record properties have the proper types.
514+
if (isset($record['entity_id'])) {
515+
$record['entity_id'] = (int) $record['entity_id'];
516+
}
517+
if (isset($record['revision_id'])) {
518+
$record['revision_id'] = (int) $record['revision_id'];
519+
}
520+
if (isset($record['user_id'])) {
521+
$record['user_id'] = (int) $record['user_id'];
522+
}
523+
if (isset($record['status'])) {
524+
$record['status'] = ClassificationStatus::tryFrom($record['status']) ?? '';
525+
}
526+
if (isset($record['attempts'])) {
527+
$record['attempts'] = (int) $record['attempts'];
528+
}
529+
if (isset($record['created'])) {
530+
$record['created'] = (int) $record['created'];
531+
}
532+
if (isset($record['changed'])) {
533+
$record['changed'] = (int) $record['changed'];
534+
}
535+
if (isset($record['message'])) {
536+
$record['message'] = ClassificationMessage::tryFrom($record['message']) ?? '';
537+
}
538+
539+
return $record;
477540
}
478541

479542
/**
@@ -513,4 +576,17 @@ protected function getDatabase(): Connection {
513576
return $this->database;
514577
}
515578

579+
/**
580+
* Get the current user.
581+
*
582+
* @return \Drupal\Core\Session\AccountProxyInterface
583+
* The current user.
584+
*/
585+
protected function getCurrentUser(): AccountProxyInterface {
586+
if (!isset($this->currentUser)) {
587+
$this->currentUser = \Drupal::currentUser();
588+
}
589+
return $this->currentUser;
590+
}
591+
516592
}

0 commit comments

Comments
 (0)