Skip to content

Commit e926d3e

Browse files
committed
Docs
1 parent 780a8f6 commit e926d3e

File tree

8 files changed

+111
-43
lines changed

8 files changed

+111
-43
lines changed

docs/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Welcome to django-feed-reader's documentation!
1111
:caption: Contents:
1212

1313
modules/overview.rst
14-
modules/about.rst
1514
modules/commands.rst
1615
modules/models.rst
1716
modules/utils.rst

docs/modules/about.rst

Lines changed: 0 additions & 5 deletions
This file was deleted.

docs/modules/commands.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
Commands
22
========
3+
4+
Commands that django-feed-reader adds to Django
5+
6+
.. automodule:: feeds.management.commands.refreshfeeds
7+
:members:
8+
:undoc-members:

docs/modules/overview.rst

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,25 @@ A feed is represented by a ``Source`` object which has (among other things) a ``
3636

3737
To start reading a feed, simply create a new ``Source`` with the desired ``feed_url``
3838

39-
``Sources`` have ``Posts`` a which contain the content.
39+
``Source`` objects have ``Post`` children which contain the content.
4040

41-
``Posts`` may have ``Enclosures`` s which is what podcasts use to send their audio. The app does not download enclosures, if you want to do that you will need to it in your project using the url provided.
41+
A ``Post`` may have ``Enclosure`` (or more) which is what podcasts use to send their audio.
42+
The app does not download enclosures, if you want to do that you will need to do that in your project
43+
using the url provided.
4244

4345

4446
Refreshing feeds
4547
----------------
4648

47-
To conserve resources with large feed lists, the module will adjust how often it polls feeds based on how often they are updated. The fastest it will poll a feed is every hour. The slowest it will poll is every 24 hours.
49+
To conserve resources with large feed lists, the module will adjust how often it polls feeds
50+
based on how often they are updated. The fastest it will poll a feed is every hour. The
51+
slowest it will poll is every 24 hours.
4852

49-
Sources that don't get updated are polled progressively more slowly until the 24 hour limit is reached. When a feed changes, its polling frequency increases.
53+
Sources that don't get updated are polled progressively more slowly until the 24 hour limit is
54+
reached. When a feed changes, its polling frequency increases.
5055

51-
You will need to decided how and when to run the poller. When the poller runs, it checks all feeds that are currently due. The ideal frequency to run it is every 5 - 10 minutes.
56+
You will need to decided how and when to run the poller. When the poller runs, it checks all
57+
feeds that are currently due. The ideal frequency to run it is every 5 - 10 minutes.
5258

5359
Polling with cron
5460
-----------------
@@ -83,15 +89,40 @@ There are two ways to track the read/unread state of feeds depending on your nee
8389
Single User Installations
8490
^^^^^^^^^^^^^^^^^^^^^^^^^
8591

86-
If your usage is just for a single user, then there are helper methods on a ``Source``
92+
If your usage is just for a single user, then there are helper methods on a Source
8793
to track your read state.
8894

8995
All posts come in unread. You can get the current number of unread posts from
9096
``Source.unread_count``.
9197

92-
To get all the unread posts from a feed in chronological order
98+
To get a ResultSet of all the unread posts from a feed call ``Source.get_unread_posts``
9399

100+
To mark all posts on a fed as read call ``Source.mark_read``
94101

102+
To get all of the posts in a feed regardless of read status, a page at a time call
103+
``Source.get_paginated_posts`` which returns a tuple of (Posts, Paginator)
104+
105+
Multi-User Installations
106+
^^^^^^^^^^^^^^^^^^^^^^^^
107+
To allow multiple users to follow the same feed with individual read/unread status,
108+
create a new ``Subscription`` for that Source and User.
109+
110+
Subscription has the same helper methods for retrieving posts and marking read as
111+
Source.
112+
113+
You can also arrange feeds into a folder-like hierarchy using Subscriptions.
114+
Every Subscription has an optional ``parent``. Subscriptions with a ``None`` parent
115+
are considered at the root level. By convention, Subscriptions that are acting as parent
116+
folders should have a ``None`` ``source``
117+
118+
Subscriptions have a ``name`` field which by convention should be a display name if it is
119+
a folder or the name of the Source it is tracking. However this can be set to any
120+
value if you want to give a personally-meaningful name to a feed who's name is cryptic.
121+
122+
There are two helper methods in the ``utils`` module to help manage subscriptions as folders.
123+
``get_subscription_list_for_user`` will return all Subscriptions for a User where the
124+
parent is None. ``get_unread_subscription_list_for_user`` will do the same but only returns
125+
Subscriptions that are unread or that have unread children if they are a folder.
95126

96127
Dealing with Cloudflare
97128
-----------------------

feeds/management/commands/refreshfeeds.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
class Command(BaseCommand):
88
"""
99
This command refreshes the RSS feeds
10+
11+
Usage is ``python manage.py refreshfeeds``
12+
1013
"""
1114

12-
help = 'Rrefreshes the RSS feeds'
15+
help = 'Refreshes the RSS feeds, 30 at a time'
1316

1417
def handle(self, *args, **options):
1518

feeds/models.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ def __str__(self):
8686
return self.display_name
8787

8888
@property
89-
def subscriber_count(self):
89+
def subscriber_count(self) -> int:
9090
"""**int** he number of subscribers this feed has"""
9191
return self.num_subs
9292

9393
@property
94-
def unread_count(self):
94+
def unread_count(self) -> int:
9595
"""**int** In a single user system how many unread articles are there?
9696
9797
If you need more than one user, or want to arrange feeds
@@ -100,7 +100,7 @@ def unread_count(self):
100100
return self.max_index - self.last_read
101101

102102
@property
103-
def best_link(self):
103+
def best_link(self) -> str:
104104
"""**str** The best user facing link to this feed.
105105
106106
Will be the **site_url** if it's present, otherwise **feed_url**
@@ -111,7 +111,7 @@ def best_link(self):
111111
return self.site_url
112112

113113
@property
114-
def display_name(self):
114+
def display_name(self) -> str:
115115
"""**str** The best user-facing name for this feed.
116116
117117
Will be the the feed's **name** as described in the feed if there is one.
@@ -123,7 +123,7 @@ def display_name(self):
123123
return self.name
124124

125125
@property
126-
def garden_style(self):
126+
def garden_style(self) -> str:
127127
"""Visual representation of how health the feed is Green -> Red
128128
129129
Internal to FeedThing and Recast and should probably be moved
@@ -150,7 +150,7 @@ def garden_style(self):
150150
return css
151151

152152
@property
153-
def health_box(self):
153+
def health_box(self) -> str:
154154
"""Visual representation of how health the feed is Green -> Red
155155
156156
Internal to FeedThing and Recast and should probably be moved
@@ -177,19 +177,19 @@ def health_box(self):
177177

178178
return css
179179

180-
def get_unread_posts(self, oldest_first=False):
181-
"""**ResultSet[Post]** In a single user system get all unread posts
180+
def get_unread_posts(self, newest_first: bool = True) -> list:
181+
"""**List[Post]** In a single user system get all unread posts
182182
183183
If you need more than one user, or want to arrange feeds
184184
into folders, use a Subscription
185185
"""
186186

187-
if oldest_first:
188-
return self.posts.filter(index__gt=self.last_read)
187+
if newest_first:
188+
return list(self.posts.filter(index__gt=self.last_read).order_by("-created"))
189189
else:
190-
return self.post.filter(index__gt=self.last_read).order_by("-created")
190+
return list(self.post.filter(index__gt=self.last_read).order_by("created"))
191191

192-
def get_paginated_posts(self, page: int, oldest_first: bool = False, posts_per_page: int = 20):
192+
def get_paginated_posts(self, page: int, newest_first: bool = True, posts_per_page: int = 20):
193193
"""Get a posts from the feed a page at a time
194194
195195
:param page: The page to fetch.
@@ -206,8 +206,10 @@ def get_paginated_posts(self, page: int, oldest_first: bool = False, posts_per_p
206206
"""
207207

208208
post_list = Post.objects.filter(source=self)
209-
if oldest_first:
209+
if newest_first:
210210
post_list = post_list.order_by("-created")
211+
else:
212+
post_list = post_list.order_by("created")
211213

212214
paginator = Paginator(post_list, posts_per_page)
213215

@@ -400,10 +402,12 @@ def __str__(self):
400402

401403
is_river = models.BooleanField(default=False)
402404
"""**bool** Indicates if the feed/folder should be displayed in a "River of News" style"""
405+
403406
name = models.CharField(max_length=255)
407+
"""**str** The display name of the subscription - typically should be set to the name of the source where present"""
404408

405409
@property
406-
def unread_count(self):
410+
def unread_count(self) -> int:
407411
"""**int** The number of undread posts in teh subscription
408412
409413
If the subscription is acting as a folder, this will total
@@ -438,7 +442,6 @@ def get_unread_posts(self, oldest_first=True):
438442
return posts
439443

440444
def _gather_sources(self, source_list: dict):
441-
442445
if self.source:
443446
source_list[self.source.id] = self
444447

feeds/tests.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ def test_single_user(self, mock):
8888

8989
self.assertEqual(src.unread_count, 1)
9090

91-
self.assertEqual(src.unread_posts.count(), 1)
91+
self.assertEqual(len(src.get_unread_posts()), 1)
9292

9393
src.mark_read()
9494

9595
src.refresh_from_db()
9696

9797
self.assertEqual(src.unread_count, 0)
9898

99-
self.assertEqual(src.unread_posts.count(), 0)
99+
self.assertEqual(len(src.get_unread_posts()), 0)
100100

101101
def test_subscriber_count(self, mock):
102102

@@ -252,10 +252,10 @@ def test_basic_subscription_read(self, mock):
252252
if s.source is None:
253253
self.assertEqual(s.unread_count, 5)
254254

255-
self.assertEqual(len(s.unread_posts), 5)
255+
self.assertEqual(len(s.get_unread_posts()), 5)
256256

257257
i = 5
258-
for p in s.unread_posts:
258+
for p in s.get_unread_posts():
259259
i -= 1
260260
self.assertEqual(p.title, f"post{i}")
261261

@@ -321,9 +321,9 @@ def test_nested_subscription_read(self, mock):
321321
for s in sub_list:
322322
if s.source is None:
323323
self.assertEqual(s.unread_count, 18)
324-
self.assertEqual(len(s.unread_posts), 18)
324+
self.assertEqual(len(s.get_unread_posts()), 18)
325325
last = None
326-
for p in s.unread_posts:
326+
for p in s.get_unread_posts():
327327
if last:
328328
self.assertGreater(p.created, last.created)
329329
last = p
@@ -348,10 +348,10 @@ def test_get_unread(self, mock):
348348
read_feed(src, output=NullOutput())
349349
src.refresh_from_db()
350350

351-
self.assertEqual(src.unread_posts.count(), 1)
351+
self.assertEqual(len(src.get_unread_posts()), 1)
352352
src.mark_read()
353353
src.refresh_from_db()
354-
self.assertEqual(src.unread_posts.count(), 0)
354+
self.assertEqual(len(src.get_unread_posts()), 0)
355355

356356
def test_get_unread_count_for_single_folder(self, mock):
357357

readme.rst

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,25 @@ A feed is represented by a ``Source`` object which has (among other things) a ``
3636

3737
To start reading a feed, simply create a new ``Source`` with the desired ``feed_url``
3838

39-
``Sources`` have ``Posts`` a which contain the content.
39+
``Source`` objects have ``Post`` children which contain the content.
4040

41-
``Posts`` may have ``Enclosures`` s which is what podcasts use to send their audio. The app does not download enclosures, if you want to do that you will need to it in your project using the url provided.
41+
A ``Post`` may have ``Enclosure`` (or more) which is what podcasts use to send their audio.
42+
The app does not download enclosures, if you want to do that you will need to do that in your project
43+
using the url provided.
4244

4345

4446
Refreshing feeds
4547
----------------
4648

47-
To conserve resources with large feed lists, the module will adjust how often it polls feeds based on how often they are updated. The fastest it will poll a feed is every hour. The slowest it will poll is every 24 hours.
49+
To conserve resources with large feed lists, the module will adjust how often it polls feeds
50+
based on how often they are updated. The fastest it will poll a feed is every hour. The
51+
slowest it will poll is every 24 hours.
4852

49-
Sources that don't get updated are polled progressively more slowly until the 24 hour limit is reached. When a feed changes, its polling frequency increases.
53+
Sources that don't get updated are polled progressively more slowly until the 24 hour limit is
54+
reached. When a feed changes, its polling frequency increases.
5055

51-
You will need to decided how and when to run the poller. When the poller runs, it checks all feeds that are currently due. The ideal frequency to run it is every 5 - 10 minutes.
56+
You will need to decided how and when to run the poller. When the poller runs, it checks all
57+
feeds that are currently due. The ideal frequency to run it is every 5 - 10 minutes.
5258

5359
Polling with cron
5460
-----------------
@@ -83,17 +89,40 @@ There are two ways to track the read/unread state of feeds depending on your nee
8389
Single User Installations
8490
^^^^^^^^^^^^^^^^^^^^^^^^^
8591

86-
If your usage is just for a single user, then there are helper methods on a ``Source``
92+
If your usage is just for a single user, then there are helper methods on a Source
8793
to track your read state.
8894

8995
All posts come in unread. You can get the current number of unread posts from
9096
``Source.unread_count``.
9197

98+
To get a ResultSet of all the unread posts from a feed call ``Source.get_unread_posts``
9299

100+
To mark all posts on a fed as read call ``Source.mark_read``
93101

102+
To get all of the posts in a feed regardless of read status, a page at a time call
103+
``Source.get_paginated_posts`` which returns a tuple of (Posts, Paginator)
94104

105+
Multi-User Installations
106+
^^^^^^^^^^^^^^^^^^^^^^^^
107+
To allow multiple users to follow the same feed with individual read/unread status,
108+
create a new ``Subscription`` for that Source and User.
95109

110+
Subscription has the same helper methods for retrieving posts and marking read as
111+
Source.
96112

113+
You can also arrange feeds into a folder-like hierarchy using Subscriptions.
114+
Every Subscription has an optional ``parent``. Subscriptions with a ``None`` parent
115+
are considered at the root level. By convention, Subscriptions that are acting as parent
116+
folders should have a ``None`` ``source``
117+
118+
Subscriptions have a ``name`` field which by convention should be a display name if it is
119+
a folder or the name of the Source it is tracking. However this can be set to any
120+
value if you want to give a personally-meaningful name to a feed who's name is cryptic.
121+
122+
There are two helper methods in the ``utils`` module to help manage subscriptions as folders.
123+
``get_subscription_list_for_user`` will return all Subscriptions for a User where the
124+
parent is None. ``get_unread_subscription_list_for_user`` will do the same but only returns
125+
Subscriptions that are unread or that have unread children if they are a folder.
97126

98127
Dealing with Cloudflare
99128
-----------------------
@@ -105,3 +134,5 @@ It's a huge pain and affects lots of self-hosted RSS readers. Seriously, Google
105134
``django-feed-reader`` will do it's utmost to get these feeds anyway through the judicious use of public proxy servers, but is haphazard and you cannot rely on the scheduling of such feeds.
106135

107136
Feeds blocked by Cloudflare will have the ``is_cloudflare`` flag set on their ``Source`` and will update on a best-efforts basis.
137+
138+
For more details see the `full documentation <https//django-feed-reader.readthedocs.io>`_.

0 commit comments

Comments
 (0)