diff options
Diffstat (limited to '')
-rwxr-xr-x | pyblog | 178 | ||||
-rw-r--r-- | rss/__init__.py | 4 | ||||
-rw-r--r-- | rss/atom_entry.py | 39 | ||||
-rw-r--r-- | rss/atom_feed.py | 54 | ||||
-rw-r--r-- | rss/rss_feed.py | 57 | ||||
-rw-r--r-- | rss/rss_item.py | 30 |
6 files changed, 188 insertions, 174 deletions
@@ -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") |