oldnews 0.8.0__py3-none-any.whl → 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
oldnews/__main__.py CHANGED
@@ -9,7 +9,7 @@ from operator import attrgetter
9
9
  ##############################################################################
10
10
  # Local imports.
11
11
  from . import __doc__, __version__
12
- from .data import initialise_database, reset_data
12
+ from .data import reset_data
13
13
  from .data.locations import config_dir, data_dir
14
14
  from .oldnews import OldNews
15
15
 
@@ -155,7 +155,6 @@ def main() -> None:
155
155
  case "themes":
156
156
  show_themes()
157
157
  case _:
158
- initialise_database()
159
158
  OldNews(args).run()
160
159
 
161
160
 
oldnews/data/__init__.py CHANGED
@@ -9,7 +9,6 @@ from .config import (
9
9
  save_configuration,
10
10
  update_configuration,
11
11
  )
12
- from .db import initialise_database
13
12
  from .dump import data_dump
14
13
  from .last_grab import last_grabbed_data_at, remember_we_last_grabbed_at
15
14
  from .local_articles import (
@@ -24,6 +23,7 @@ from .local_articles import (
24
23
  rename_folder_for_articles,
25
24
  save_local_articles,
26
25
  )
26
+ from .local_data import initialise_local_data, shutdown_local_data
27
27
  from .local_folders import get_local_folders, save_local_folders
28
28
  from .local_subscriptions import get_local_subscriptions, save_local_subscriptions
29
29
  from .local_unread import LocalUnread, get_local_unread, total_unread
@@ -34,9 +34,8 @@ from .reset import reset_data
34
34
  ##############################################################################
35
35
  # Exports.
36
36
  __all__ = [
37
- "Configuration",
38
- "Log",
39
37
  "clean_old_read_articles",
38
+ "Configuration",
40
39
  "data_dump",
41
40
  "get_auth_token",
42
41
  "get_local_articles",
@@ -45,12 +44,13 @@ __all__ = [
45
44
  "get_local_unread",
46
45
  "get_navigation_state",
47
46
  "get_unread_article_ids",
48
- "initialise_database",
47
+ "initialise_local_data",
49
48
  "last_grabbed_data_at",
50
49
  "load_configuration",
51
- "locally_mark_read",
52
50
  "locally_mark_article_ids_read",
51
+ "locally_mark_read",
53
52
  "LocalUnread",
53
+ "Log",
54
54
  "move_subscription_articles",
55
55
  "remember_we_last_grabbed_at",
56
56
  "remove_folder_from_articles",
@@ -63,6 +63,7 @@ __all__ = [
63
63
  "save_local_subscriptions",
64
64
  "save_navigation_state",
65
65
  "set_auth_token",
66
+ "shutdown_local_data",
66
67
  "total_unread",
67
68
  "update_configuration",
68
69
  ]
oldnews/data/config.py CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  ##############################################################################
4
4
  # Python imports.
5
+ from collections.abc import Iterator
5
6
  from contextlib import contextmanager
6
7
  from dataclasses import asdict, dataclass, field
7
- from functools import lru_cache
8
+ from functools import cache
8
9
  from json import dumps, loads
9
10
  from pathlib import Path
10
- from typing import Iterator
11
11
 
12
12
  ##############################################################################
13
13
  # Local imports.
@@ -37,6 +37,9 @@ class Configuration:
37
37
  startup_refresh_holdoff_period: float = 600
38
38
  """The number of seconds to wait before hitting TheOldReader again on startup."""
39
39
 
40
+ article_download_batch_size: int = 50
41
+ """The batch size to use when downloading articles."""
42
+
40
43
 
41
44
  ##############################################################################
42
45
  def configuration_file() -> Path:
@@ -66,7 +69,7 @@ def save_configuration(configuration: Configuration) -> Configuration:
66
69
 
67
70
 
68
71
  ##############################################################################
69
- @lru_cache(maxsize=None)
72
+ @cache
70
73
  def load_configuration() -> Configuration:
71
74
  """Load the configuration.
72
75
 
oldnews/data/last_grab.py CHANGED
@@ -2,39 +2,31 @@
2
2
 
3
3
  ##############################################################################
4
4
  # Python imports.
5
- from datetime import datetime, timezone
5
+ from datetime import UTC, datetime
6
6
 
7
7
  ##############################################################################
8
- # TypeDAL imports.
9
- from typedal import TypedTable
8
+ # Tortoise imports.
9
+ from tortoise.transactions import in_transaction
10
10
 
11
11
  ##############################################################################
12
12
  # Local imports.
13
- from .tools import commit
13
+ from .models import LastGrabbed
14
14
 
15
15
 
16
16
  ##############################################################################
17
- class LastGrabbed(TypedTable):
18
- """Table that holds details of when data was last grabbed."""
19
-
20
- at_time: datetime
21
- """The time at which data was last grabbed."""
22
-
23
-
24
- ##############################################################################
25
- def last_grabbed_data_at() -> datetime | None:
17
+ async def last_grabbed_data_at() -> datetime | None:
26
18
  """The time at which data was last grabbed.
27
19
 
28
20
  Returns:
29
21
  The time at which we last grabbed data, or `None` if we never have.
30
22
  """
31
- if (row := LastGrabbed.select(LastGrabbed.ALL).first()) is not None:
32
- return row.at_time
23
+ if last_grabbed := await LastGrabbed.first():
24
+ return last_grabbed.at_time
33
25
  return None
34
26
 
35
27
 
36
28
  ##############################################################################
37
- def remember_we_last_grabbed_at(grab_time: datetime | None = None) -> None:
29
+ async def remember_we_last_grabbed_at(grab_time: datetime | None = None) -> None:
38
30
  """Remember the time we last grabbed data.
39
31
 
40
32
  Args:
@@ -43,9 +35,9 @@ def remember_we_last_grabbed_at(grab_time: datetime | None = None) -> None:
43
35
  Note:
44
36
  If `grab_time` isn't supplied then it is recorded as now.
45
37
  """
46
- LastGrabbed.truncate()
47
- LastGrabbed.insert(at_time=grab_time or datetime.now(timezone.utc))
48
- commit(LastGrabbed)
38
+ async with in_transaction():
39
+ await LastGrabbed.all().delete()
40
+ await LastGrabbed.create(at_time=grab_time or datetime.now(UTC))
49
41
 
50
42
 
51
43
  ### last_grab.py ends here
@@ -2,9 +2,10 @@
2
2
 
3
3
  ##############################################################################
4
4
  # Python imports.
5
- from datetime import datetime, timedelta
5
+ from collections.abc import Iterable
6
+ from datetime import UTC, datetime, timedelta
6
7
  from html import unescape
7
- from typing import Iterable, Iterator, cast
8
+ from typing import cast
8
9
 
9
10
  ##############################################################################
10
11
  # OldAS imports.
@@ -12,102 +13,17 @@ from oldas import Article, Articles, Folder, Folders, State, Subscription
12
13
  from oldas.articles import Alternate, Alternates, Direction, Origin, Summary
13
14
 
14
15
  ##############################################################################
15
- # TypeDAL imports.
16
- from typedal import TypedField, TypedTable, relationship
16
+ # Tortoise imports.
17
+ from tortoise.transactions import in_transaction
17
18
 
18
19
  ##############################################################################
19
20
  # Local imports.
20
21
  from .log import Log
21
- from .tools import commit
22
+ from .models import LocalArticle, LocalArticleAlternate, LocalArticleCategory
22
23
 
23
24
 
24
25
  ##############################################################################
25
- class LocalArticle(TypedTable):
26
- """A local copy of an article."""
27
-
28
- article_id: TypedField[str]
29
- """The ID of the article."""
30
- title: str
31
- """The title of the article."""
32
- published: TypedField[datetime] = TypedField(datetime)
33
- """The time when the article was published."""
34
- updated: TypedField[datetime] = TypedField(datetime)
35
- """The time when the article was updated."""
36
- author: str
37
- """The author of the article."""
38
- summary_direction: str
39
- """The direction for the text in the summary."""
40
- summary_content: TypedField[str] = TypedField(str, type="text")
41
- """The content of the summary."""
42
- origin_stream_id: str
43
- """The stream ID for the article's origin."""
44
- origin_title: str
45
- """The title of the origin of the article."""
46
- origin_html_url: str
47
- """The URL of the HTML of the origin of the article."""
48
- categories = relationship(
49
- list["LocalArticleCategory"],
50
- condition=lambda article, category: cast(LocalArticle, article).id
51
- == cast(LocalArticleCategory, category).article,
52
- join="left",
53
- )
54
- """The categories associated with this article."""
55
- alternate = relationship(
56
- list["LocalArticleAlternate"],
57
- condition=lambda article, alternate: cast(LocalArticle, article).id
58
- == cast(LocalArticleAlternate, alternate).article,
59
- join="left",
60
- )
61
- """The alternates for the article."""
62
-
63
- def add_category(self, category: str | State) -> None:
64
- """Add a given category to the local article.
65
-
66
- Args:
67
- category: The category to add.
68
- """
69
- if not str(category) in self.categories:
70
- LocalArticleCategory.insert(article=self.id, category=str(category))
71
- commit(LocalArticleCategory)
72
-
73
- def remove_category(self, category: str | State) -> None:
74
- """Remove a given category from the local article.
75
-
76
- Args:
77
- category: The category to add.
78
- """
79
- if str(category) in self.categories:
80
- LocalArticleCategory.where(
81
- (LocalArticleCategory.article == self.id)
82
- & (LocalArticleCategory.category == str(category))
83
- ).delete()
84
- commit(LocalArticleCategory)
85
-
86
-
87
- ##############################################################################
88
- class LocalArticleCategory(TypedTable):
89
- """A local copy of the categories associated with an article."""
90
-
91
- article: TypedField[LocalArticle]
92
- """The article that this category belongs to."""
93
- category: str
94
- """The category."""
95
-
96
-
97
- ##############################################################################
98
- class LocalArticleAlternate(TypedTable):
99
- """A local copy of the alternate URLs associated with an article."""
100
-
101
- article: TypedField[LocalArticle]
102
- """The article that this alternate belongs to."""
103
- href: str
104
- """The URL of the alternate."""
105
- mime_type: str
106
- """The MIME type of the alternate."""
107
-
108
-
109
- ##############################################################################
110
- def save_local_articles(articles: Articles) -> Articles:
26
+ async def save_local_articles(articles: Articles) -> Articles:
111
27
  """Locally save the given articles.
112
28
 
113
29
  Args:
@@ -117,108 +33,55 @@ def save_local_articles(articles: Articles) -> Articles:
117
33
  The articles.
118
34
  """
119
35
  for article in articles:
120
- local_article = LocalArticle.update_or_insert(
121
- LocalArticle.article_id == article.id,
122
- article_id=article.id,
123
- title=article.title,
124
- published=article.published,
125
- updated=article.updated,
126
- author=article.author,
127
- summary_direction=article.summary.direction,
128
- summary_content=article.summary.content,
129
- origin_stream_id=article.origin.stream_id,
130
- origin_title=article.origin.title,
131
- origin_html_url=article.origin.html_url,
132
- )
133
- LocalArticleCategory.where(article=local_article.id).delete()
134
- LocalArticleCategory.bulk_insert(
135
- [
136
- {"article": local_article.id, "category": str(category)}
36
+ async with in_transaction():
37
+ local_article, _ = await LocalArticle.update_or_create(
38
+ article_id=article.id,
39
+ defaults={
40
+ "title": article.title,
41
+ "published": article.published,
42
+ "updated": article.updated,
43
+ "author": article.author,
44
+ "summary_direction": article.summary.direction,
45
+ "summary_content": article.summary.content,
46
+ "origin_stream_id": article.origin.stream_id,
47
+ "origin_title": article.origin.title,
48
+ "origin_html_url": article.origin.html_url,
49
+ },
50
+ )
51
+ await LocalArticleCategory.filter(article=local_article).delete()
52
+ await LocalArticleCategory.bulk_create(
53
+ LocalArticleCategory(article=local_article, category=str(category))
137
54
  for category in article.categories
138
- ]
139
- )
140
- LocalArticleAlternate.where(article=local_article.id).delete()
141
- LocalArticleAlternate.bulk_insert(
142
- [
143
- {
144
- "article": local_article.id,
145
- "href": alternate.href,
146
- "mime_type": alternate.mime_type,
147
- }
55
+ )
56
+ await LocalArticleAlternate.filter(article=local_article).delete()
57
+ await LocalArticleAlternate.bulk_create(
58
+ LocalArticleAlternate(
59
+ article=local_article,
60
+ href=alternate.href,
61
+ mime_type=alternate.mime_type,
62
+ )
148
63
  for alternate in article.alternate
149
- ]
150
- )
151
- commit(LocalArticle)
64
+ )
152
65
  return articles
153
66
 
154
67
 
155
68
  ##############################################################################
156
- def get_local_read_article_ids() -> set[int]:
69
+ async def get_local_read_article_ids() -> set[str]:
157
70
  """Get the set of local articles that have been read.
158
71
 
159
72
  Returns:
160
73
  A `set` of IDs of articles that have been read.
161
74
  """
162
75
  return {
163
- category.article.id
164
- for category in LocalArticleCategory.where(
165
- LocalArticleCategory.category == State.READ
166
- ).collect()
76
+ category.article.article_id
77
+ for category in await LocalArticleCategory.filter(
78
+ category=str(State.READ)
79
+ ).prefetch_related("article")
167
80
  }
168
81
 
169
82
 
170
83
  ##############################################################################
171
- def _for_subscription(
172
- subscription: Subscription, unread_only: bool
173
- ) -> Iterator[LocalArticle]:
174
- """Get all unread articles for a given subscription.
175
-
176
- Args:
177
- subscription: The subscription to get the articles for.
178
- unread_only: Only load up the unread articles?
179
-
180
- Yields:
181
- The articles.
182
- """
183
- read = get_local_read_article_ids() if unread_only else set()
184
- for article in (
185
- LocalArticle.where(~LocalArticle.id.belongs(read))
186
- .where(origin_stream_id=subscription.id)
187
- .join()
188
- .orderby(~LocalArticle.published)
189
- ):
190
- yield article
191
-
192
-
193
- ##############################################################################
194
- def _for_folder(folder: Folder, unread_only: bool) -> Iterator[LocalArticle]:
195
- """Get all unread articles for a given folder.
196
-
197
- Args:
198
- folder: The folder to get the articles for.
199
- unread_only: Only load up the unread articles?
200
-
201
- Yields:
202
- The unread articles.
203
- """
204
- in_folder = {
205
- category.article.id
206
- for category in LocalArticleCategory.where(
207
- LocalArticleCategory.category == folder.id
208
- ).collect()
209
- }
210
- read = get_local_read_article_ids() if unread_only else set()
211
- for article in (
212
- LocalArticle.where(LocalArticle.id.belongs(in_folder - read))
213
- .select()
214
- .join()
215
- .orderby(~LocalArticle.published)
216
- ):
217
- yield article
218
-
219
-
220
- ##############################################################################
221
- def get_local_articles(
84
+ async def get_local_articles(
222
85
  related_to: Folder | Subscription, unread_only: bool
223
86
  ) -> Articles:
224
87
  """Get all available unread articles.
@@ -229,12 +92,18 @@ def get_local_articles(
229
92
 
230
93
  Returns: The unread articles.
231
94
  """
232
- articles: list[Article] = []
233
- for article in (
234
- _for_folder(related_to, unread_only)
95
+ local_articles = (
96
+ LocalArticle.filter(categories__category=related_to.id)
235
97
  if isinstance(related_to, Folder)
236
- else _for_subscription(related_to, unread_only)
237
- ):
98
+ else LocalArticle.filter(origin_stream_id=related_to.id)
99
+ )
100
+ if unread_only and (read := (await get_local_read_article_ids())):
101
+ local_articles = local_articles.filter(article_id__not_in=read)
102
+
103
+ articles: list[Article] = []
104
+ for article in await local_articles.prefetch_related(
105
+ "categories", "alternates"
106
+ ).order_by("-published"):
238
107
  articles.append(
239
108
  Article(
240
109
  id=article.article_id,
@@ -243,11 +112,12 @@ def get_local_articles(
243
112
  updated=article.updated,
244
113
  author=article.author,
245
114
  categories=Article.clean_categories(
246
- category.category for category in article.categories
115
+ category.category
116
+ for category in article.categories # type: ignore
247
117
  ),
248
118
  alternate=Alternates(
249
119
  Alternate(href=alternate.href, mime_type=alternate.mime_type)
250
- for alternate in article.alternate
120
+ for alternate in article.alternates # type: ignore
251
121
  ),
252
122
  origin=Origin(
253
123
  stream_id=article.origin_stream_id,
@@ -264,95 +134,93 @@ def get_local_articles(
264
134
 
265
135
 
266
136
  ##############################################################################
267
- def locally_mark_read(article: Article) -> None:
137
+ async def locally_mark_read(article: Article) -> None:
268
138
  """Mark the given article as read.
269
139
 
270
140
  Args:
271
141
  article: The article to locally mark as read.
272
142
  """
273
- if local_article := LocalArticle.where(
274
- LocalArticle.article_id == article.id
275
- ).first():
276
- local_article.add_category(State.READ)
143
+ if local_article := await LocalArticle.filter(article_id=article.id).get_or_none():
144
+ await local_article.add_category(str(State.READ))
277
145
 
278
146
 
279
147
  ##############################################################################
280
- def locally_mark_article_ids_read(articles: Iterable[str]) -> None:
148
+ async def locally_mark_article_ids_read(articles: Iterable[str]) -> None:
281
149
  """Locally mark a collection of article IDs as being read.
282
150
 
283
151
  Args:
284
152
  articles: The article IDs to mark as read.
285
153
  """
286
- for local_article in LocalArticle.where(LocalArticle.article_id.belongs(articles)):
287
- local_article.add_category(State.READ)
154
+ if article_ids := set(articles):
155
+ Log().debug(f"Number of articles to mark as read: {len(article_ids)}")
156
+ await LocalArticleCategory.bulk_create(
157
+ [
158
+ LocalArticleCategory(article=article, category=str(State.READ))
159
+ for article in await LocalArticle.filter(article_id__in=article_ids)
160
+ ],
161
+ ignore_conflicts=True,
162
+ )
288
163
 
289
164
 
290
165
  ##############################################################################
291
- def unread_count_in(
292
- category: Folder | Subscription, read: set[int] | None = None
166
+ async def unread_count_in(
167
+ category: Folder | Subscription, read: set[str] | None = None
293
168
  ) -> int:
294
169
  """Get the count of unread articles in a given category.
295
170
 
296
171
  Args:
297
- category: The category to get the unread count for.
172
+ category: The category (Folder or Subscription) to get the unread count for.
298
173
  read: The set of IDs of read articles.
299
174
 
300
175
  Returns:
301
176
  The count of unread articles in that category.
302
-
303
- Notes:
304
- Note that `read` is optional and will be worked out of not passed,
305
- but if this function is being called in a tight loop it's more
306
- efficient to provide this externally.
307
177
  """
308
- read = get_local_read_article_ids() if read is None else read
309
- if isinstance(category, Folder):
310
- in_folder = {
311
- category.article.id
312
- for category in LocalArticleCategory.where(
313
- LocalArticleCategory.category == category.id
314
- ).collect()
315
- }
316
- return LocalArticle.where(LocalArticle.id.belongs(in_folder - read)).count()
317
- return (
318
- LocalArticle.where(~LocalArticle.id.belongs(read))
319
- .where(origin_stream_id=category.id)
320
- .count()
178
+ query = (
179
+ LocalArticle.filter(categories__category=category.id)
180
+ if isinstance(category, Folder)
181
+ else LocalArticle.filter(origin_stream_id=category.id)
321
182
  )
183
+ if read := read if read is not None else await get_local_read_article_ids():
184
+ query = query.filter(article_id__not_in=read)
185
+ return await query.count()
322
186
 
323
187
 
324
188
  ##############################################################################
325
- def get_unread_article_ids() -> list[str]:
189
+ async def get_unread_article_ids() -> list[str]:
326
190
  """Get a list of all the unread article IDs.
327
191
 
328
192
  Returns:
329
193
  The list of IDs of unread articles.
330
194
  """
331
- read = get_local_read_article_ids()
195
+ read = await get_local_read_article_ids()
332
196
  return [
333
197
  article.article_id
334
- for article in LocalArticle.where(~LocalArticle.id.belongs(read)).select()
198
+ for article in await LocalArticle.filter(article_id__not_in=read)
335
199
  ]
336
200
 
337
201
 
338
202
  ##############################################################################
339
- def clean_old_read_articles(cutoff: timedelta) -> int:
340
- """Clean up articles that are older than the given cutoff time."""
341
- read = get_local_read_article_ids()
342
- retire_time = datetime.now() - cutoff
203
+ async def clean_old_read_articles(cutoff: timedelta) -> int:
204
+ """Clean up articles that are older than the given cutoff time.
205
+
206
+ Args:
207
+ cutoff: The cutoff period after which articles will be removed.
208
+
209
+ Returns:
210
+ The number of removed articles.
211
+ """
212
+ read = await get_local_read_article_ids()
213
+ retire_time = datetime.now(UTC) - cutoff
343
214
  Log().debug(f"Cleaning up read articles published before {retire_time}")
344
- cleaned = len(
345
- LocalArticle.where(
346
- (LocalArticle.published < retire_time) & LocalArticle.id.belongs(read)
347
- ).delete()
348
- )
215
+ cleaned = await LocalArticle.filter(
216
+ published__lt=retire_time, article_id__in=read
217
+ ).delete()
349
218
  Log().debug(f"Cleaned: {cleaned}")
350
- commit(LocalArticle)
351
219
  return cleaned
352
220
 
353
221
 
354
222
  ##############################################################################
355
- def rename_folder_for_articles(rename_from: str | Folder, rename_to: str) -> None:
223
+ async def rename_folder_for_articles(rename_from: str | Folder, rename_to: str) -> None:
356
224
  """Rename a folder for all articles that are in that folder.
357
225
 
358
226
  Args:
@@ -362,14 +230,11 @@ def rename_folder_for_articles(rename_from: str | Folder, rename_to: str) -> Non
362
230
  rename_from = Folders.full_id(rename_from)
363
231
  rename_to = Folders.full_id(rename_to)
364
232
  Log().debug(f"Renaming folder for local articles from {rename_from} to {rename_to}")
365
- LocalArticleCategory.where(LocalArticleCategory.category == rename_from).update(
366
- category=rename_to
367
- )
368
- commit(LocalArticleCategory)
233
+ await LocalArticleCategory.filter(category=rename_from).update(category=rename_to)
369
234
 
370
235
 
371
236
  ##############################################################################
372
- def remove_folder_from_articles(folder: str | Folder) -> None:
237
+ async def remove_folder_from_articles(folder: str | Folder) -> None:
373
238
  """Remove a folder from being associated with all articles.
374
239
 
375
240
  Args:
@@ -377,12 +242,11 @@ def remove_folder_from_articles(folder: str | Folder) -> None:
377
242
  """
378
243
  folder = Folders.full_id(folder)
379
244
  Log().debug(f"Removing folder {folder} from all local articles")
380
- LocalArticleCategory.where(LocalArticleCategory.category == folder).delete()
381
- commit(LocalArticleCategory)
245
+ await LocalArticleCategory.filter(category=folder).delete()
382
246
 
383
247
 
384
248
  ##############################################################################
385
- def move_subscription_articles(
249
+ async def move_subscription_articles(
386
250
  subscription: Subscription,
387
251
  from_folder: str | Folder | None,
388
252
  to_folder: str | Folder | None,
@@ -401,20 +265,17 @@ def move_subscription_articles(
401
265
  Log().debug(
402
266
  f"Moving all articles of {subscription.title} ({subscription.id}) from folder {from_folder} to {to_folder}"
403
267
  )
404
- for article in LocalArticle.where(origin_stream_id=subscription.id).join().select():
268
+ for article in await LocalArticle.filter(
269
+ origin_stream_id=subscription.id
270
+ ).prefetch_related("categories"):
405
271
  if from_folder:
406
- LocalArticleCategory.where(
407
- (LocalArticleCategory.article == article.id)
408
- & (LocalArticleCategory.category == from_folder)
409
- ).delete()
410
- commit(LocalArticleCategory)
272
+ await article.remove_category(from_folder)
411
273
  if to_folder:
412
- LocalArticleCategory.insert(article=article.id, category=to_folder)
413
- commit(LocalArticleCategory)
274
+ await article.add_category(to_folder)
414
275
 
415
276
 
416
277
  ##############################################################################
417
- def remove_subscription_articles(subscription: str | Subscription) -> None:
278
+ async def remove_subscription_articles(subscription: str | Subscription) -> None:
418
279
  """Remove all the articles associated with the given subscription.
419
280
 
420
281
  Args:
@@ -423,8 +284,8 @@ def remove_subscription_articles(subscription: str | Subscription) -> None:
423
284
  if isinstance(subscription, Subscription):
424
285
  subscription = subscription.id
425
286
  Log().debug(f"Removing all local articles for subscription {subscription}")
426
- LocalArticle.where(origin_stream_id=subscription).delete()
427
- commit(LocalArticle)
287
+ deleted = await LocalArticle.filter(origin_stream_id=subscription).delete()
288
+ Log().debug(f"Articles removed that belonged to {subscription}: {deleted}")
428
289
 
429
290
 
430
291
  ### local_articles.py ends here