diff options
| author | 2024-01-03 11:23:06 +0100 | |
|---|---|---|
| committer | 2024-01-03 11:23:06 +0100 | |
| commit | 70e71b8364c1317af04f92fd86df4541fa269e0c (patch) | |
| tree | 193f4db62a85a218e227ee79ce7554a32eade390 /app/Models | |
| parent | 1e5f5078ed029640f69bdcc5ba51dd4dc69574ca (diff) | |
Auto-label (#5954)
Add labels automatically to incoming articles
fix https://github.com/FreshRSS/FreshRSS/issues/2380
fix https://github.com/FreshRSS/FreshRSS/issues/2420
fix https://github.com/FreshRSS/FreshRSS/issues/3279
fix https://github.com/FreshRSS/FreshRSS/discussions/4947
fix https://github.com/FreshRSS/FreshRSS/issues/5728
fix https://github.com/FreshRSS/FreshRSS/issues/5599
Diffstat (limited to 'app/Models')
| -rw-r--r-- | app/Models/Entry.php | 13 | ||||
| -rw-r--r-- | app/Models/EntryDAO.php | 19 | ||||
| -rw-r--r-- | app/Models/FilterActionsTrait.php | 11 | ||||
| -rw-r--r-- | app/Models/Tag.php | 2 | ||||
| -rw-r--r-- | app/Models/TagDAO.php | 29 | ||||
| -rw-r--r-- | app/Models/View.php | 1 |
6 files changed, 61 insertions, 14 deletions
diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 62ba91db3..16f01483a 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -50,7 +50,7 @@ class FreshRSS_Entry extends Minz_Model { } /** @param array{'id'?:string,'id_feed'?:int,'guid'?:string,'title'?:string,'author'?:string,'content'?:string,'link'?:string,'date'?:int|string,'lastSeen'?:int, - * 'hash'?:string,'is_read'?:bool|int,'is_favorite'?:bool|int,'tags'?:string|array<string>,'attributes'?:string,'thumbnail'?:string,'timestamp'?:string} $dao */ + * 'hash'?:string,'is_read'?:bool|int,'is_favorite'?:bool|int,'tags'?:string|array<string>,'attributes'?:?string,'thumbnail'?:string,'timestamp'?:string} $dao */ public static function fromArray(array $dao): FreshRSS_Entry { FreshRSS_DatabaseDAO::pdoInt($dao, ['id_feed', 'date', 'lastSeen', 'is_read', 'is_favorite']); @@ -98,6 +98,17 @@ class FreshRSS_Entry extends Minz_Model { return $entry; } + /** + * @param Traversable<array{'id'?:string,'id_feed'?:int,'guid'?:string,'title'?:string,'author'?:string,'content'?:string,'link'?:string,'date'?:int|string,'lastSeen'?:int, + * 'hash'?:string,'is_read'?:bool|int,'is_favorite'?:bool|int,'tags'?:string|array<string>,'attributes'?:?string,'thumbnail'?:string,'timestamp'?:string}> $daos + * @return Traversable<FreshRSS_Entry> + */ + public static function fromTraversable(Traversable $daos): Traversable { + foreach ($daos as $dao) { + yield FreshRSS_Entry::fromArray($dao); + } + } + public function id(): string { return $this->id; } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 232db8521..b809c7475 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -95,7 +95,7 @@ SQL; private $addEntryPrepared = false; /** @param array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int,'hash':string, - * 'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $valuesTmp */ + * 'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes'?:null|string|array<string,mixed>} $valuesTmp */ public function addEntry(array $valuesTmp, bool $useTmpTable = true): bool { if ($this->addEntryPrepared == null) { $sql = static::sqlIgnoreConflict( @@ -693,19 +693,22 @@ SQL; } /** @return Traversable<array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int, - * 'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>}> */ - public function selectAll(): Traversable { + * 'hash':string,'is_read':bool,'is_favorite':bool,'id_feed':int,'tags':string,'attributes':?string}> */ + public function selectAll(?int $limit = null): Traversable { $content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content'; $hash = static::sqlHexEncode('hash'); $sql = <<<SQL SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes FROM `_entry` SQL; + if (is_int($limit) && $limit >= 0) { + $sql .= ' ORDER BY id DESC LIMIT ' . $limit; + } $stm = $this->pdo->query($sql); if ($stm != false) { while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { /** @var array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int, - * 'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $row */ + * 'hash':string,'is_read':bool,'is_favorite':bool,'id_feed':int,'tags':string,'attributes':?string} $row */ yield $row; } } else { @@ -727,7 +730,7 @@ FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid SQL; $res = $this->fetchAssoc($sql, [':id_feed' => $id_feed, ':guid' => $guid]); /** @var array<array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string}> $res */ + * 'is_read':int,'is_favorite':int,'tags':string,'attributes':?string}> $res */ return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null; } @@ -740,7 +743,7 @@ FROM `_entry` WHERE id=:id SQL; $res = $this->fetchAssoc($sql, [':id' => $id]); /** @var array<array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string}> $res */ + * 'is_read':int,'is_favorite':int,'tags':string,'attributes':?string}> $res */ return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null; } @@ -1167,7 +1170,7 @@ SQL; while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { if (is_array($row)) { /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ + * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:?string} $row */ yield FreshRSS_Entry::fromArray($row); } } @@ -1212,7 +1215,7 @@ SQL; while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { if (is_array($row)) { /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ + * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes':?string} $row */ yield FreshRSS_Entry::fromArray($row); } } diff --git a/app/Models/FilterActionsTrait.php b/app/Models/FilterActionsTrait.php index aefb549af..a19c0a361 100644 --- a/app/Models/FilterActionsTrait.php +++ b/app/Models/FilterActionsTrait.php @@ -50,8 +50,7 @@ trait FreshRSS_FilterActionsTrait { $filterActions = $this->filterActions(); for ($i = count($filterActions) - 1; $i >= 0; $i--) { $filterAction = $filterActions[$i]; - if ($filterAction != null && $filterAction->booleanSearch() != null && - $filterAction->actions() != null && in_array($action, $filterAction->actions(), true)) { + if (in_array($action, $filterAction->actions(), true)) { $filters[] = $filterAction->booleanSearch(); } } @@ -120,7 +119,11 @@ trait FreshRSS_FilterActionsTrait { $this->_filterActions($filterActions); } - public function applyFilterActions(FreshRSS_Entry $entry): void { + /** + * @param bool $applyLabel Parameter by reference, which will be set to true if the callers needs to apply a label to the article entry. + */ + public function applyFilterActions(FreshRSS_Entry $entry, ?bool &$applyLabel = null): void { + $applyLabel = false; foreach ($this->filterActions() as $filterAction) { if ($entry->matches($filterAction->booleanSearch())) { foreach ($filterAction->actions() as $action) { @@ -135,7 +138,7 @@ trait FreshRSS_FilterActionsTrait { $entry->_isFavorite(true); break; case 'label': - //TODO: Implement more actions + $applyLabel = true; break; } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index cedc4647d..0ed7c462c 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -2,7 +2,7 @@ declare(strict_types=1); class FreshRSS_Tag extends Minz_Model { - use FreshRSS_AttributesTrait; + use FreshRSS_AttributesTrait, FreshRSS_FilterActionsTrait; private int $id = 0; private string $name; diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php index fba27dc7b..391bde36d 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -301,6 +301,35 @@ SQL; } /** + * @param array<array{id_tag:int,id_entry:string}> $addLabels Labels to insert as batch + * @return int|false Number of new entries or false in case of error + */ + public function tagEntries(array $addLabels) { + $hasValues = false; + $sql = 'INSERT ' . $this->sqlIgnore() . ' INTO `_entrytag`(id_tag, id_entry) VALUES '; + foreach ($addLabels as $addLabel) { + $id_tag = (int)($addLabel['id_tag'] ?? 0); + $id_entry = $addLabel['id_entry'] ?? ''; + if ($id_tag > 0 && ctype_digit($id_entry)) { + $sql .= "({$id_tag},{$id_entry}),"; + $hasValues = true; + } + } + $sql = rtrim($sql, ','); + if (!$hasValues) { + return false; + } + + $affected = $this->pdo->exec($sql); + if ($affected !== false) { + return $affected; + } + $info = $this->pdo->errorInfo(); + Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); + return false; + } + + /** * @return array<int,array{'id':int,'name':string,'id_entry':string,'checked':bool}>|false */ public function getTagsForEntry(string $id_entry) { diff --git a/app/Models/View.php b/app/Models/View.php index 3c188dd99..4dd0be36a 100644 --- a/app/Models/View.php +++ b/app/Models/View.php @@ -13,6 +13,7 @@ class FreshRSS_View extends Minz_View { /** @var array<FreshRSS_Category> */ public array $categories; public ?FreshRSS_Category $category; + public ?FreshRSS_Tag $tag; public string $current_user; /** @var iterable<FreshRSS_Entry> */ public $entries; |
