aboutsummaryrefslogtreecommitdiff
path: root/rss
diff options
context:
space:
mode:
Diffstat (limited to 'rss')
-rw-r--r--rss/__init__.py4
-rw-r--r--rss/atom_entry.py39
-rw-r--r--rss/atom_feed.py54
-rw-r--r--rss/rss_feed.py57
-rw-r--r--rss/rss_item.py30
5 files changed, 184 insertions, 0 deletions
diff --git a/rss/__init__.py b/rss/__init__.py
new file mode 100644
index 00000000..c5d54da5
--- /dev/null
+++ b/rss/__init__.py
@@ -0,0 +1,4 @@
+from rss.atom_feed import AtomFeed
+from rss.atom_entry import AtomEntry
+from rss.rss_feed import RssFeed
+from rss.rss_item import RssItem
diff --git a/rss/atom_entry.py b/rss/atom_entry.py
new file mode 100644
index 00000000..4b6e116a
--- /dev/null
+++ b/rss/atom_entry.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+import lxml.etree as ET
+
+class AtomEntry(object):
+ """Class for storing atom:entry data and metadata."""
+
+ # pylint: disable=invalid-name,too-many-instance-attributes
+
+ def __init__(self):
+ """Define available attributes."""
+ self.author = None # atom:author
+ self.id_text = None # atom:id, just use URI
+ self.id = None # atom:id
+ self.relpath = None # HTML page path relative to home
+ self.link = None # atom:link
+ self.title_text = None # plain text title
+ self.title = None # atom:title
+ self.updated_datetime = None # update time as a datetime object
+ self.updated = None # atom:updated
+ self.content_html = None # content as HTML markup
+ self.content = None # atom:content
+ self.entry = None # atom:entry, assembled
+
+ def assemble_entry(self):
+ """Assemble atom:entry."""
+ self.entry = ET.Element("entry")
+ self.entry.append(self.title)
+ self.entry.append(self.link)
+ self.entry.append(self.updated)
+ self.entry.append(self.id)
+ self.entry.append(self.author)
+ self.entry.append(self.content)
+
+ def dump_entry(self):
+ """Dump atom:entry XML."""
+ if self.entry is None:
+ self.assemble_entry()
+ return ET.tostring(self.entry).decode("utf-8")
diff --git a/rss/atom_feed.py b/rss/atom_feed.py
new file mode 100644
index 00000000..622ecc08
--- /dev/null
+++ b/rss/atom_feed.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+
+import lxml.etree as ET
+
+class AtomFeed(object):
+ """Class for storing atom:feed data and metadata.
+
+ https://tools.ietf.org/html/rfc4287.
+
+ """
+
+ # pylint: disable=invalid-name,too-many-instance-attributes
+
+ def __init__(self):
+ """Define available attributes."""
+ self.author = None # atom:author
+ self.generator = None # atom:generator, optional
+ self.icon = None # atom:icon, optional
+ self.logo = None # atom:logo, optional
+ self.id_text = None # atom:id, just use URI
+ self.id = None # atom:id
+ self.links = [] # list of atom:link
+ self.title_text = None # the text of atom:title
+ self.title = None # atom:title
+ self.subtitle_text = None # the text of atom:subtitle
+ self.subtitle = None # atom:subtitle
+ self.updated_datetime = None # update time as a datetime object
+ self.updated = None # atom:updated
+ self.entries = [] # list of atom:entry, in reverse time order
+ self.feed = None # atom:feed, assembled
+
+ def assemble_feed(self, FEED_MAX_ENTRIES):
+ """Assemble atom:feed."""
+ # pylint: disable=multiple-statements
+ self.feed = ET.Element("feed", xmlns="http://www.w3.org/2005/Atom")
+ self.feed.append(self.title)
+ if self.subtitle is not None: self.feed.append(self.subtitle)
+ for link in self.links:
+ self.feed.append(link)
+ self.feed.append(self.updated)
+ self.feed.append(self.id)
+ self.feed.append(self.author)
+ if self.icon is not None: self.feed.append(self.icon)
+ if self.logo is not None: self.feed.append(self.icon)
+ if self.generator is not None: self.feed.append(self.generator)
+ # include at most FEED_MAX_ENTRIES entries in the feed
+ for entry in self.entries[:FEED_MAX_ENTRIES]:
+ self.feed.append(entry.entry)
+
+ def dump_feed(self, FEED_MAX_ENTRIES):
+ """Dump atom:feed XML."""
+ if self.feed is None:
+ self.assemble_feed(FEED_MAX_ENTRIES)
+ return ET.tostring(self.feed).decode("utf-8")
diff --git a/rss/rss_feed.py b/rss/rss_feed.py
new file mode 100644
index 00000000..5ed6226d
--- /dev/null
+++ b/rss/rss_feed.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+
+import lxml.etree as ET
+
+class RssFeed(object):
+ """Class for storing an RSS 2.0 feed.
+
+ https://validator.w3.org/feed/docs/rss2.html.
+
+ """
+
+ # pylint: disable=too-many-instance-attributes
+
+ REQUIRED_ELEMENTS = ["title", "link", "description"]
+ OPTIONAL_ELEMENTS = ["language", "copyright", "managingEditor", "webMaster",
+ "pubDate", "lastBuildDate", "category", "generator",
+ "docs", "cloud", "ttl", "image", "textInput",
+ "skipHours", "skipDays"]
+
+ def __init__(self):
+ """Define available attributes."""
+ self.rssurl = None # the URL of the rss feed
+ self.atomlink = None
+ for element in self.REQUIRED_ELEMENTS:
+ setattr(self, element, None)
+ for element in self.OPTIONAL_ELEMENTS:
+ setattr(self, element, None)
+ self.docs = ET.Element("docs")
+ self.docs.text = "https://validator.w3.org/feed/docs/rss2.html"
+ self.author_text = None
+ self.update_timestamp = None
+ self.items = []
+ self.rss = None
+ self.channel = None
+
+ def assemble_rss(self, FEED_MAX_ENTRIES):
+ """Assemble RSS 2.0 feed."""
+ self.rss = ET.Element("rss", version="2.0", nsmap={"atom": "http://www.w3.org/2005/Atom"})
+ self.channel = ET.SubElement(self.rss, "channel")
+ # https://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html
+ self.atomlink = ET.SubElement(self.channel, "{http://www.w3.org/2005/Atom}link",
+ href=self.rssurl, rel="self", type="application/rss+xml")
+ for element in self.REQUIRED_ELEMENTS:
+ self.channel.append(getattr(self, element))
+ for element in self.OPTIONAL_ELEMENTS:
+ attr = getattr(self, element)
+ if attr is not None:
+ self.channel.append(attr)
+ # include at most FEED_MAX_ENTRIES items in the RSS feed
+ for item in self.items[:FEED_MAX_ENTRIES]:
+ self.channel.append(item.item)
+
+ def dump_rss(self, FEED_MAX_ENTRIES):
+ """Dump RSS feed XML."""
+ if self.rss is None:
+ self.assemble_rss(FEED_MAX_ENTRIES)
+ return ET.tostring(self.rss).decode("utf-8")
diff --git a/rss/rss_item.py b/rss/rss_item.py
new file mode 100644
index 00000000..8ead20e6
--- /dev/null
+++ b/rss/rss_item.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+import lxml.etree as ET
+
+class RssItem(object):
+ """Class for storing an RSS 2.0 item."""
+
+ ELEMENTS = ["title", "link", "description", "author", "category", "comments",
+ "enclosure", "guid", "pubDate", "source"]
+
+ def __init__(self):
+ """Define available attributes."""
+ for element in self.ELEMENTS:
+ setattr(self, element, None)
+ self.timestamp = None
+ self.item = None
+
+ def assemble_item(self):
+ """Assemble an RSS 2.0 item."""
+ self.item = ET.Element("item")
+ for element in self.ELEMENTS:
+ attr = getattr(self, element)
+ if attr is not None:
+ self.item.append(attr)
+
+ def dump_item(self):
+ """Dump RSS item XML."""
+ if self.item is None:
+ self.assemble_item()
+ return ET.tostring(self.item).decode("utf-8")