feat: add item and enclosures models
This commit is contained in:
parent
2fbbdcc7b8
commit
b173becf70
3 changed files with 119 additions and 62 deletions
|
@ -7,3 +7,9 @@ API
|
|||
|
||||
.. autoclass:: GenRSS
|
||||
:members:
|
||||
|
||||
.. autoclass:: Item
|
||||
:members:
|
||||
|
||||
.. autoclass:: Enclosure
|
||||
:members:
|
|
@ -24,7 +24,7 @@ Install GenRSS
|
|||
|
||||
Use the following command to install GenRSS:
|
||||
|
||||
``pip install genrss``
|
||||
``pip install -U genrss``
|
||||
|
||||
GenRSS is now installed. Check out :ref:`quickstart`
|
||||
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
import mimetypes
|
||||
from lxml.etree import Element, CDATA, tostring
|
||||
from typing import Optional, List, TypeVar, Dict, Any
|
||||
from typing import Optional, List, TypeVar, Dict, Any, Union
|
||||
from datetime import datetime
|
||||
from collections import namedtuple
|
||||
|
||||
import pytz
|
||||
|
||||
__all__ = ('GenRSS', 'Enclosure',)
|
||||
|
||||
ElementT = TypeVar('ElementT')
|
||||
|
||||
Enclosure = namedtuple('Enclosure', ('url', 'size', 'type'))
|
||||
Enclosure.__new__.__defaults__ = (None, None, None)
|
||||
Enclosure.__doc__ = 'Creates information for enclosure tag.'
|
||||
|
||||
RSS_DEFAULT_GENERATOR = f'Generated by genrss for python'
|
||||
|
||||
|
||||
|
@ -37,6 +31,101 @@ def create_element(name: str, text: Any = None, children: List[ElementT] = None,
|
|||
return el
|
||||
|
||||
|
||||
class Enclosure:
|
||||
"""Data for enclosure tag for rss.
|
||||
|
||||
:param url: Absolute url to file
|
||||
:param size: File size
|
||||
:param type: File mime type
|
||||
"""
|
||||
|
||||
def __init__(self, url: str, size=None, type=None):
|
||||
self.url = url
|
||||
self.size = size or 0
|
||||
self.type = type or mimetypes.guess_type(self.url)[0]
|
||||
|
||||
def to_element(self) -> ElementT:
|
||||
"""Returns item element for xml."""
|
||||
return create_element('enclosure', url=self.url, length=str(self.size),
|
||||
type=self.type)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Union[str, int]]):
|
||||
"""Makes enclosure data from dict."""
|
||||
return Enclosure(data.get('url'), data.get('size'), data.get('type'))
|
||||
|
||||
|
||||
class Item:
|
||||
"""Data for item tag for rss.
|
||||
|
||||
:param title: Title of this particular item
|
||||
:param description: Content for the item. Can contain html but
|
||||
link and image urls must be absolute path including hostname
|
||||
:param url: Url to the item. This could be a blog entry
|
||||
:param guid: A unique string feed readers use to know if an item
|
||||
is new or has already been seen. If you use a guid never change
|
||||
it. If you don't provide a guid then your item urls must be unique
|
||||
:param author: If included it is the name of the item's creator.
|
||||
If not provided the item author will be the same as the feed
|
||||
author. This is typical except on multi-author blogs
|
||||
:param categories: If provided, each array item will be added as a
|
||||
category element
|
||||
:param enclosure: An enclosure object
|
||||
:param pub_date: The date and time of when the item was created.
|
||||
Feed readers use this to determine the sort order. Some readers
|
||||
will also use it to determine if the content should be presented
|
||||
as unread
|
||||
"""
|
||||
|
||||
def __init__(self, title: str, **kwargs):
|
||||
self.title: str = title
|
||||
self.description: str = kwargs.pop('description', '')
|
||||
self.url: Optional[str] = kwargs.pop('url', None)
|
||||
self.guid: Optional[str] = kwargs.pop('guid', None)
|
||||
self.author: Optional[str] = kwargs.pop('author', None)
|
||||
self.categories: List[str] = kwargs.pop('categories', [])
|
||||
self.enclosure: Optional[Enclosure] = kwargs.pop('enclosure', None)
|
||||
self.pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
|
||||
|
||||
if isinstance(self.enclosure, dict):
|
||||
self.enclosure = Enclosure.from_dict(self.enclosure)
|
||||
|
||||
def to_element(self) -> ElementT:
|
||||
"""Returns item element for xml."""
|
||||
item = create_element('item', children=[
|
||||
create_element('title', CDATA(self.title)),
|
||||
create_element('description', CDATA(self.description)),
|
||||
])
|
||||
|
||||
if self.url:
|
||||
item.append(create_element('link', self.url))
|
||||
|
||||
item.append(create_element(
|
||||
'guid',
|
||||
attrib={
|
||||
'isPermaLink': str(bool(not self.guid and self.url)).lower()
|
||||
},
|
||||
text=(self.guid or self.url or CDATA(self.title))
|
||||
))
|
||||
|
||||
if self.author:
|
||||
item.append(create_element(
|
||||
'{http://purl.org/dc/elements/1.1/}creator',
|
||||
CDATA(self.author)
|
||||
))
|
||||
|
||||
for category in self.categories:
|
||||
item.append(create_element('category', CDATA(category)))
|
||||
|
||||
if self.enclosure:
|
||||
item.append(self.enclosure.to_element())
|
||||
|
||||
if self.pub_date:
|
||||
item.append(create_element('pubDate', self.pub_date))
|
||||
|
||||
return item
|
||||
|
||||
|
||||
class GenRSS:
|
||||
"""Generates RSS feed of channel.
|
||||
|
||||
|
@ -69,11 +158,12 @@ class GenRSS:
|
|||
self.docs_url: Optional[str] = kwargs.pop('docs_url', None)
|
||||
self.categories: List[str] = kwargs.pop('categories', [])
|
||||
|
||||
self.items: List[Element] = []
|
||||
self.items: List[Item] = kwargs.pop('items', [])
|
||||
self.generator: str = kwargs.pop('generator', RSS_DEFAULT_GENERATOR)
|
||||
self.root_version: str = '2.0'
|
||||
self.root_nsmap: Dict[str, str] = {
|
||||
'atom': 'http://www.w3.org/2005/Atom'
|
||||
'atom': 'http://www.w3.org/2005/Atom',
|
||||
'dc': 'http://purl.org/dc/elements/1.1/'
|
||||
}
|
||||
|
||||
def item(self, title: str, **kwargs):
|
||||
|
@ -100,58 +190,10 @@ class GenRSS:
|
|||
will also use it to determine if the content should be presented
|
||||
as unread
|
||||
"""
|
||||
description: str = kwargs.pop('description', '')
|
||||
url: Optional[str] = kwargs.pop('url', None)
|
||||
guid: Optional[str] = kwargs.pop('guid', None)
|
||||
author: Optional[str] = kwargs.pop('author', None)
|
||||
categories: List[str] = kwargs.pop('categories', [])
|
||||
enclosure: Optional[Enclosure] = kwargs.pop('enclosure', None)
|
||||
pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
|
||||
self.items.append(Item(title, **kwargs))
|
||||
|
||||
item = create_element('item', children=[
|
||||
create_element('title', CDATA(title)),
|
||||
create_element('description', CDATA(description)),
|
||||
])
|
||||
|
||||
if url:
|
||||
item.append(create_element('link', url))
|
||||
|
||||
item.append(create_element(
|
||||
'guid',
|
||||
attrib={'isPermaLink': str(bool(not guid and url)).lower()},
|
||||
text=(guid or url or CDATA(title))
|
||||
))
|
||||
|
||||
if author or self.author:
|
||||
if 'dc' not in self.root_nsmap:
|
||||
self.root_nsmap['dc'] = 'http://purl.org/dc/elements/1.1/'
|
||||
|
||||
item.append(create_element(
|
||||
'{http://purl.org/dc/elements/1.1/}creator',
|
||||
CDATA(author or self.author)
|
||||
))
|
||||
|
||||
for category in categories:
|
||||
item.append(create_element('category', CDATA(category)))
|
||||
|
||||
if enclosure:
|
||||
item.append(create_element(
|
||||
'enclosure',
|
||||
url=enclosure.url,
|
||||
length=str(enclosure.size or 0),
|
||||
type=enclosure.type or mimetypes.guess_type(enclosure.url)[0]
|
||||
))
|
||||
|
||||
if pub_date:
|
||||
item.append(create_element('pubDate', pub_date))
|
||||
|
||||
self.items.append(item)
|
||||
|
||||
def xml(self, pretty: bool = False) -> str:
|
||||
"""Returns the XML as a string.
|
||||
|
||||
:param pretty: Pretty print xml
|
||||
"""
|
||||
def to_element(self) -> ElementT:
|
||||
"""Returns root element for xml."""
|
||||
root = Element('rss', nsmap=self.root_nsmap, version=self.root_version)
|
||||
channel = create_element('channel', children=[
|
||||
create_element('title', CDATA(self.title)),
|
||||
|
@ -186,9 +228,18 @@ class GenRSS:
|
|||
channel.append(create_element('docs', self.docs_url))
|
||||
|
||||
for item in self.items:
|
||||
channel.append(item)
|
||||
if isinstance(item, Item):
|
||||
channel.append(item.to_element())
|
||||
|
||||
root.append(channel)
|
||||
return root
|
||||
|
||||
def xml(self, pretty: bool = False) -> str:
|
||||
"""Returns the XML as a string.
|
||||
|
||||
:param pretty: Pretty print xml
|
||||
"""
|
||||
root = self.to_element()
|
||||
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>\n' \
|
||||
+ ('\n' if pretty else '') \
|
||||
|
|
Loading…
Reference in a new issue