feat: add image element
This commit is contained in:
parent
942d8d7782
commit
517c74269d
7 changed files with 144 additions and 20 deletions
|
@ -6,8 +6,9 @@ from lxml.etree import Element, CDATA, tostring
|
|||
from .utils import create_element, ElementT
|
||||
from .item import Item
|
||||
from .enclosure import Enclosure
|
||||
from .image import Image
|
||||
|
||||
__all__ = ('GenRSS', 'Item', 'Enclosure')
|
||||
__all__ = ('GenRSS', 'Item', 'Enclosure', 'Image')
|
||||
|
||||
|
||||
RSS_DEFAULT_GENERATOR = f'Generated by genrss for python'
|
||||
|
@ -21,12 +22,15 @@ class GenRSS:
|
|||
:param feed_url: Absolute url to the rss feed
|
||||
:param description: A short description of feed
|
||||
:param image_url: Image absolute url for channel
|
||||
:param image: Image element for channel (it replaces image_url)
|
||||
:param author: Author of channel
|
||||
:param pub_date: Datetime in utc when last item was published
|
||||
:param copyright: Copyright information for this feed
|
||||
:param language: The language of the content of this feed.
|
||||
:param editor: Who manages content in this feed
|
||||
:param webmaster: Who manages feed availability and technical support
|
||||
:param docs_url: Url to rss documentation
|
||||
:param categories: List of category names
|
||||
:param generator: Feed generator
|
||||
"""
|
||||
|
||||
|
@ -36,6 +40,7 @@ class GenRSS:
|
|||
self.feed_url: str = feed_url
|
||||
self.description: str = kwargs.pop('description', self.title)
|
||||
self.image_url: Optional[str] = kwargs.pop('image_url', None)
|
||||
self.image: Optional[Image] = kwargs.pop('image', None)
|
||||
self.author: Optional[str] = kwargs.pop('author', None)
|
||||
self.pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
|
||||
self.copyright: Optional[str] = kwargs.pop('copyright', None)
|
||||
|
@ -44,7 +49,6 @@ class GenRSS:
|
|||
self.webmaster: Optional[str] = kwargs.pop('webmaster', None)
|
||||
self.docs_url: Optional[str] = kwargs.pop('docs_url', None)
|
||||
self.categories: List[str] = kwargs.pop('categories', [])
|
||||
|
||||
self.items: List[Item] = kwargs.pop('items', [])
|
||||
self.generator: str = kwargs.pop('generator', RSS_DEFAULT_GENERATOR)
|
||||
self.root_version: str = '2.0'
|
||||
|
@ -93,12 +97,12 @@ class GenRSS:
|
|||
create_element('lastBuildDate', datetime.utcnow())
|
||||
])
|
||||
|
||||
if self.image_url:
|
||||
channel.append(create_element('image', children=[
|
||||
create_element('url', self.image_url),
|
||||
create_element('title', CDATA(self.title)),
|
||||
create_element('link', self.site_url)
|
||||
]))
|
||||
channel_image = self.image
|
||||
if not channel_image and self.image_url:
|
||||
channel_image = Image(self.image_url, self.site_url, self.title)
|
||||
if isinstance(image, Image):
|
||||
channel.append(channel_image.to_element())
|
||||
|
||||
for category in self.categories:
|
||||
channel.append(create_element('category', CDATA(category)))
|
||||
if self.pub_date:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import mimetypes
|
||||
from typing import Dict, Union
|
||||
from typing import Dict, Union, TypeVar, Dict
|
||||
|
||||
from .utils import ElementT, create_element
|
||||
|
||||
__all__ = ('Enclosure',)
|
||||
__all__ = ('Enclosure', 'EnclosureOrDictT')
|
||||
|
||||
|
||||
class Enclosure:
|
||||
|
@ -24,9 +24,10 @@ class Enclosure:
|
|||
return create_element('enclosure', url=self.url, length=str(self.size),
|
||||
type=self.type)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Union[str, int]]):
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Union[str, int]]):
|
||||
"""Makes enclosure data from dict."""
|
||||
return Enclosure(data.get('url'), data.get('size'), data.get('type'))
|
||||
return cls(**data)
|
||||
|
||||
|
||||
EnclosureOrDictT = Union[Enclosure, Dict]
|
||||
|
|
51
genrss/image.py
Normal file
51
genrss/image.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from typing import Dict, Union
|
||||
from lxml.etree import CDATA
|
||||
|
||||
from .utils import ElementT, create_element
|
||||
|
||||
__all__ = ('Image',)
|
||||
|
||||
|
||||
class Image:
|
||||
"""The element allows an image to be displayed when aggregators
|
||||
present a feed.
|
||||
|
||||
:param url: Absolute url to the image
|
||||
:param link: Hyperlink to the website
|
||||
:param title: Text to display if the image could not be shown
|
||||
:param description: Specifies the text in the HTML title attribute of the
|
||||
link around the image
|
||||
:param width: The width of the image
|
||||
:param height: The height of the image
|
||||
"""
|
||||
|
||||
def __init__(self, url: str, link: str, title: str, description: str = None,
|
||||
width: int = None, height: int = None):
|
||||
self.url = url
|
||||
self.link = link
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.height = height
|
||||
self.width = width
|
||||
|
||||
def to_element(self) -> ElementT:
|
||||
"""Returns image element for xml."""
|
||||
image = create_element('image', children=[
|
||||
create_element('url', self.url),
|
||||
create_element('link', self.link),
|
||||
create_element('title', CDATA(self.title))
|
||||
])
|
||||
|
||||
if self.description:
|
||||
image.append(create_element('description', CDATA(self.description)))
|
||||
if self.height:
|
||||
image.append(create_element('height', self.height))
|
||||
if self.width:
|
||||
image.append(create_element('width', self.width))
|
||||
|
||||
return image
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Union[str, int]]):
|
||||
"""Makes image data from dict."""
|
||||
return cls(**data)
|
|
@ -1,10 +1,11 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, Union, Dict
|
||||
|
||||
from lxml.etree import CDATA
|
||||
|
||||
from .utils import ElementT, create_element
|
||||
from .enclosure import Enclosure
|
||||
from .enclosure import Enclosure, EnclosureOrDictT
|
||||
from .image import Image
|
||||
|
||||
__all__ = ('Item',)
|
||||
|
||||
|
@ -25,6 +26,7 @@ class Item:
|
|||
:param categories: If provided, each array item will be added as a
|
||||
category element
|
||||
:param enclosure: An enclosure object
|
||||
:param image: An image 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
|
||||
|
@ -38,11 +40,15 @@ class Item:
|
|||
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.enclosure: Optional[Enclosure, Dict] = kwargs.pop('enclosure',
|
||||
None)
|
||||
self.image: Optional[Image, Dict] = kwargs.pop('image', None)
|
||||
self.pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
|
||||
|
||||
if isinstance(self.enclosure, dict):
|
||||
self.enclosure = Enclosure.from_dict(self.enclosure)
|
||||
if isinstance(self.image, dict):
|
||||
self.image = Image.from_dict(self.image)
|
||||
|
||||
def to_element(self) -> ElementT:
|
||||
"""Returns item element for xml."""
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import pytest
|
||||
|
||||
IMAGE_URL = 'http://s3.smartfridge.me/image.jpg'
|
||||
IMAGE_URL = 'https://s3.smartfridge.me/image.jpg'
|
||||
SITE_URL = 'https://smartfridge.me/'
|
||||
SITE_TITLE = 'Smart Fridge'
|
||||
|
||||
IMAGE_DESCRIPTION = 'a'*100
|
||||
IMAGE_HEIGHT = 100
|
||||
IMAGE_WIDTH = 100
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
|
@ -21,3 +28,29 @@ def enclosure_tuple(request):
|
|||
])
|
||||
def enclosure_dict(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
pytest.param((None, None, None), id='-/-/-'),
|
||||
pytest.param((IMAGE_DESCRIPTION, None, None), id='+/-/-'),
|
||||
pytest.param((IMAGE_DESCRIPTION, 100, None), id='+/+/-'),
|
||||
pytest.param((IMAGE_DESCRIPTION, 100, 200), id='+/+/+'),
|
||||
])
|
||||
def image_tuple(request):
|
||||
return (IMAGE_URL, SITE_URL, SITE_TITLE) + request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
pytest.param(dict(), id='-/-/-'),
|
||||
pytest.param(dict(description=IMAGE_DESCRIPTION), id='+/-/-'),
|
||||
pytest.param(dict(description=IMAGE_DESCRIPTION, width=100), id='+/+/-'),
|
||||
pytest.param(dict(description=IMAGE_DESCRIPTION, width=100, height=100),
|
||||
id='+/+/+'),
|
||||
])
|
||||
def image_dict(request):
|
||||
return dict(
|
||||
url=IMAGE_URL,
|
||||
link=SITE_URL,
|
||||
title=SITE_TITLE,
|
||||
**request.param
|
||||
)
|
||||
|
|
|
@ -2,13 +2,13 @@ import pytest
|
|||
from genrss import Enclosure
|
||||
|
||||
|
||||
def test_init_fails():
|
||||
def test_init_enclosure_fails():
|
||||
with pytest.raises(TypeError):
|
||||
Enclosure()
|
||||
assert False
|
||||
|
||||
|
||||
def test_init(enclosure_tuple):
|
||||
def test_init_enclosure(enclosure_tuple):
|
||||
url, size, type = enclosure_tuple
|
||||
enclosure = Enclosure(url, size, type)
|
||||
assert enclosure.url == url
|
||||
|
@ -16,7 +16,7 @@ def test_init(enclosure_tuple):
|
|||
assert enclosure.type == (type or 'image/jpeg')
|
||||
|
||||
|
||||
def test_init_from_dict(enclosure_dict):
|
||||
def test_init_enclosure_from_dict(enclosure_dict):
|
||||
enclosure = Enclosure.from_dict(enclosure_dict)
|
||||
assert enclosure.url == enclosure_dict.get('url')
|
||||
assert enclosure.size == enclosure_dict.get('size', 0)
|
||||
|
|
29
tests/test_image.py
Normal file
29
tests/test_image.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import pytest
|
||||
from genrss import Image
|
||||
|
||||
|
||||
def test_init_image_fails():
|
||||
with pytest.raises(TypeError):
|
||||
Image()
|
||||
assert False
|
||||
|
||||
|
||||
def test_init_image(image_tuple):
|
||||
url, link, title, description, width, height = image_tuple
|
||||
image = Image(url, link, title, description, width, height)
|
||||
assert image.url == url
|
||||
assert image.link == link
|
||||
assert image.title == title
|
||||
assert image.description == description
|
||||
assert image.width == width
|
||||
assert image.height == height
|
||||
|
||||
|
||||
def test_init_image_from_dict(image_dict):
|
||||
image = Image.from_dict(image_dict)
|
||||
assert image.url == image_dict.get('url')
|
||||
assert image.link == image_dict.get('link')
|
||||
assert image.title == image_dict.get('title')
|
||||
assert image.description == image_dict.get('description')
|
||||
assert image.width == image_dict.get('width')
|
||||
assert image.height == image_dict.get('height')
|
Loading…
Reference in a new issue