aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xpyblog178
-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
6 files changed, 188 insertions, 174 deletions
diff --git a/pyblog b/pyblog
index a9abd8da..e44d2719 100755
--- a/pyblog
+++ b/pyblog
@@ -42,6 +42,8 @@ import requests
import toml
+from rss import *
+
ROOTDIR = os.path.dirname(os.path.realpath(__file__))
@@ -121,178 +123,6 @@ def current_datetime():
return datetime.datetime.fromtimestamp(round(time.time()),
dateutil.tz.tzlocal())
-
-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):
- """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):
- """Dump atom:feed XML."""
- if self.feed is None:
- self.assemble_feed()
- return ET.tostring(self.feed).decode("utf-8")
-
-
-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")
-
-
-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):
- """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):
- """Dump RSS feed XML."""
- if self.rss is None:
- self.assemble_rss()
- return ET.tostring(self.rss).decode("utf-8")
-
-
-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")
-
def generate_menu():
"""Generate menu."""
@@ -1002,11 +832,11 @@ def generate_index_and_feed():
rss.lastBuildDate.text = email.utils.formatdate(rss.update_timestamp, usegmt=True)
with open(ATOM, "w", encoding="utf-8") as atom:
- atom.write("%s\n" % feed.dump_feed())
+ atom.write("%s\n" % feed.dump_feed(FEED_MAX_ENTRIES))
sys.stderr.write("wrote atom.xml\n")
with open(RSS, "w", encoding="utf-8") as rssxml:
- rssxml.write("%s\n" % rss.dump_rss())
+ rssxml.write("%s\n" % rss.dump_rss(FEED_MAX_ENTRIES))
sys.stderr.write("wrote rss.xml\n")
generate_sitemap(feed)
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")