v1.8.0: titel-dedup over feeds (zelfde artikel, andere url)
This commit is contained in:
parent
d7ba8a06de
commit
6264e73441
7 changed files with 77 additions and 5 deletions
|
|
@ -15,6 +15,8 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from string import Template
|
||||
|
||||
|
|
@ -85,6 +87,19 @@ date_fmt = "%Y-%m-%d %H:%M:%S"
|
|||
date_fmt_microseconds = "%Y-%m-%d %H:%M:%S.%f"
|
||||
|
||||
|
||||
def normalize_title(title: str) -> str:
|
||||
"""Genormaliseerde titel-sleutel voor cross-feed duplicate-detectie.
|
||||
|
||||
Stript HTML, lowercaset, vouwt whitespace samen en verwijdert rand-interpunctie,
|
||||
zodat hetzelfde artikel dat door één uitgever onder een andere URL/feed wordt
|
||||
gepubliceerd (vrijwel) identieke titels matcht. Bewust exacte matching:
|
||||
terugkerende koppen ("treinstoring", "staking") met écht andere titels
|
||||
mogen niet als duplicaat worden weggefilterd."""
|
||||
text = html.unescape(re.sub(r"<[^>]+>", "", title or ""))
|
||||
text = re.sub(r"\s+", " ", text)
|
||||
return text.strip(" \t\n.,;:!?…-–—").lower()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Entry:
|
||||
feed_id: int
|
||||
|
|
@ -151,6 +166,21 @@ class DBManager:
|
|||
for row in existing:
|
||||
entries.pop(row["id"], None)
|
||||
|
||||
async def get_recent_posted_titles(self, since: datetime) -> set[str]:
|
||||
"""Genormaliseerde titels van entries die sinds `since` daadwerkelijk zijn
|
||||
gebroadcast (posted=true). Dit is de bron voor cross-feed titel-dedup."""
|
||||
rows = await self.db.fetch(
|
||||
"SELECT title FROM entry WHERE posted AND date >= $1", since
|
||||
)
|
||||
return {normalize_title(row["title"]) for row in rows if row["title"]}
|
||||
|
||||
async def mark_entry_posted(self, feed_id: int, entry_id: str) -> None:
|
||||
await self.db.execute(
|
||||
"UPDATE entry SET posted = true WHERE feed_id = $1 AND id = $2",
|
||||
feed_id,
|
||||
entry_id,
|
||||
)
|
||||
|
||||
async def add_entries(self, entries: list[Entry], override_feed_id: int | None = None) -> None:
|
||||
if not entries:
|
||||
return
|
||||
|
|
|
|||
Loading…
Reference in a new issue