refac: split on other files
This commit is contained in:
parent
8109f03309
commit
942d8d7782
5 changed files with 155 additions and 125 deletions
|
@ -1,129 +1,18 @@
|
||||||
import mimetypes
|
|
||||||
from lxml.etree import Element, CDATA, tostring
|
|
||||||
from typing import Optional, List, TypeVar, Dict, Any, Union
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Optional, List, Dict
|
||||||
|
|
||||||
import pytz
|
from lxml.etree import Element, CDATA, tostring
|
||||||
|
|
||||||
|
from .utils import create_element, ElementT
|
||||||
|
from .item import Item
|
||||||
|
from .enclosure import Enclosure
|
||||||
|
|
||||||
__all__ = ('GenRSS', 'Item', 'Enclosure')
|
__all__ = ('GenRSS', 'Item', 'Enclosure')
|
||||||
|
|
||||||
ElementT = TypeVar('ElementT')
|
|
||||||
RSS_DEFAULT_GENERATOR = f'Generated by genrss for python'
|
RSS_DEFAULT_GENERATOR = f'Generated by genrss for python'
|
||||||
|
|
||||||
|
|
||||||
def create_element(name: str, text: Any = None, children: List[ElementT] = None,
|
|
||||||
**kwargs) -> ElementT:
|
|
||||||
"""Creates xml node with text or children elements.
|
|
||||||
|
|
||||||
:param name: Tag name of node with namespace
|
|
||||||
:param text: Text of node
|
|
||||||
:param children: Appends elements as child nodes
|
|
||||||
"""
|
|
||||||
el = Element(name, **kwargs)
|
|
||||||
if text:
|
|
||||||
if isinstance(text, datetime):
|
|
||||||
text = text.replace(tzinfo=pytz.timezone('GMT')). \
|
|
||||||
strftime("%a, %d %b %Y %H:%M:%S %Z")
|
|
||||||
el.text = text
|
|
||||||
elif isinstance(children, (list, tuple)):
|
|
||||||
for child in children:
|
|
||||||
el.append(child)
|
|
||||||
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:
|
class GenRSS:
|
||||||
"""Generates RSS feed of channel.
|
"""Generates RSS feed of channel.
|
||||||
|
|
||||||
|
@ -240,5 +129,5 @@ class GenRSS:
|
||||||
root = self.to_element()
|
root = self.to_element()
|
||||||
|
|
||||||
return tostring(root, pretty_print=pretty, xml_declaration=True,
|
return tostring(root, pretty_print=pretty, xml_declaration=True,
|
||||||
encoding='UTF-8').\
|
encoding='UTF-8'). \
|
||||||
decode('utf-8')
|
decode('utf-8')
|
||||||
|
|
32
genrss/enclosure.py
Normal file
32
genrss/enclosure.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import mimetypes
|
||||||
|
from typing import Dict, Union
|
||||||
|
|
||||||
|
from .utils import ElementT, create_element
|
||||||
|
|
||||||
|
__all__ = ('Enclosure',)
|
||||||
|
|
||||||
|
|
||||||
|
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'))
|
||||||
|
|
||||||
|
|
78
genrss/item.py
Normal file
78
genrss/item.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from lxml.etree import CDATA
|
||||||
|
|
||||||
|
from .utils import ElementT, create_element
|
||||||
|
from .enclosure import Enclosure
|
||||||
|
|
||||||
|
__all__ = ('Item',)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
30
genrss/utils.py
Normal file
30
genrss/utils.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import TypeVar, Any, List
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
from lxml.etree import Element
|
||||||
|
|
||||||
|
__all__ = ('ElementT', 'create_element',)
|
||||||
|
|
||||||
|
|
||||||
|
ElementT = TypeVar('ElementT')
|
||||||
|
|
||||||
|
|
||||||
|
def create_element(name: str, text: Any = None, children: List[ElementT] = None,
|
||||||
|
**kwargs) -> ElementT:
|
||||||
|
"""Creates xml node with text or children elements.
|
||||||
|
|
||||||
|
:param name: Tag name of node with namespace
|
||||||
|
:param text: Text of node
|
||||||
|
:param children: Appends elements as child nodes
|
||||||
|
"""
|
||||||
|
el = Element(name, **kwargs)
|
||||||
|
if text:
|
||||||
|
if isinstance(text, datetime):
|
||||||
|
text = text.replace(tzinfo=pytz.timezone('GMT')). \
|
||||||
|
strftime("%a, %d %b %Y %H:%M:%S %Z")
|
||||||
|
el.text = text
|
||||||
|
elif isinstance(children, (list, tuple)):
|
||||||
|
for child in children:
|
||||||
|
el.append(child)
|
||||||
|
return el
|
|
@ -92,15 +92,16 @@ def test_item_enclosure(feed):
|
||||||
create_item(feed, enclosure=enclosure)
|
create_item(feed, enclosure=enclosure)
|
||||||
xml = feed.xml()
|
xml = feed.xml()
|
||||||
assert xml
|
assert xml
|
||||||
assert '<enclosure length="{}" type="{}" url="{}"/>' \
|
assert '<enclosure url="{url}" length="{length}" type="{type}"/>' \
|
||||||
'</item>'.format(0, 'image/jpeg', enclosure.url) in xml
|
'</item>'.format(length=0, type='image/jpeg',
|
||||||
|
url=enclosure.url) in xml
|
||||||
|
|
||||||
|
|
||||||
def test_item_enclosure_from_dict(feed, enclosure_dict):
|
def test_item_enclosure_from_dict(feed, enclosure_dict):
|
||||||
create_item(feed, enclosure=enclosure_dict)
|
create_item(feed, enclosure=enclosure_dict)
|
||||||
xml = feed.xml()
|
xml = feed.xml()
|
||||||
assert xml
|
assert xml
|
||||||
assert '<enclosure length="{}" type="{}" url="{}"/>' \
|
assert '<enclosure url="{url}" length="{length}" type="{type}"/>' \
|
||||||
'</item>'.format(enclosure_dict.get('size', 0),
|
'</item>'.format(length=enclosure_dict.get('size', 0),
|
||||||
enclosure_dict.get('type', 'image/jpeg'),
|
type=enclosure_dict.get('type', 'image/jpeg'),
|
||||||
enclosure_dict.get('url')) in xml
|
url=enclosure_dict.get('url')) in xml
|
||||||
|
|
Loading…
Reference in a new issue