From e9980b1a760c4afd617849663cda19fc69b40f65 Mon Sep 17 00:00:00 2001
From: David Luevano Alvarado <david@luevano.xyz>
Date: Sun, 23 May 2021 16:54:34 -0600
Subject: add rss support

---
 ChangeLog                  |  4 ++++
 src/pyssg/builder.py       |  9 ++++++++
 src/pyssg/configuration.py |  8 ++++++++
 src/pyssg/pyssg.py         | 12 +++++++++++
 src/pyssg/rss.py           | 51 ++++++++++++++++++++++++++++++++++++++++++++++
 src/pyssg/template.py      | 39 +++++++++++++++++++++++++++++++++++
 6 files changed, 123 insertions(+)
 create mode 100644 src/pyssg/rss.py

diff --git a/ChangeLog b/ChangeLog
index 328d17a..1faf0e6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,10 @@
 CHANGES
 =======
 
+v0.2.4
+------
+
+* lol wrong src and dst variables read
 * fix error on parsing config file
 * finally fix argparse error, questionmark
 * checking argparse errors
diff --git a/src/pyssg/builder.py b/src/pyssg/builder.py
index 8472b20..d2163d2 100644
--- a/src/pyssg/builder.py
+++ b/src/pyssg/builder.py
@@ -28,6 +28,8 @@ class HTMLBuilder:
         self.md_files: list[str] = None
         self.html_files: list[str] = None
 
+        self.all_pages: list[Page] = None
+
 
     def build(self) -> None:
         self.dirs = get_dir_structure(self.src, ['templates'])
@@ -40,6 +42,9 @@ class HTMLBuilder:
         parser: MDParser = MDParser(self.src, self.md_files, self.db)
         parser.parse()
 
+        # just to be able to extract all pages out of this class
+        self.all_pages = parser.all_pages
+
         # create the article index
         self.__create_article_index(parser.all_tags, parser.all_pages)
 
@@ -52,6 +57,10 @@ class HTMLBuilder:
         self.__create_tags(parser.all_tags, parser.all_pages)
 
 
+    def get_pages(self) -> list[Page]:
+        return self.all_pages
+
+
     def __create_dir_structure(self) -> None:
         for d in self.dirs:
             # for the dir structure,
diff --git a/src/pyssg/configuration.py b/src/pyssg/configuration.py
index 5f73c23..8ee592e 100644
--- a/src/pyssg/configuration.py
+++ b/src/pyssg/configuration.py
@@ -8,6 +8,7 @@ class Configuration:
         self.src: str = None
         self.dst: str = None
         self.base_url: str = None
+        self.title: str = None
         self.dformat: str = None
         self.l_dformat: str = None
         self.lsep_dformat: str = None
@@ -47,6 +48,10 @@ class Configuration:
                 self.base_url = opts['BASE_URL']
             except KeyError: pass
 
+            try:
+                self.title = opts['TITLE']
+            except KeyError: pass
+
             try:
                 self.dformat = opts['DATE_FORMAT']
             except KeyError: pass
@@ -78,6 +83,9 @@ class Configuration:
         if self.base_url is None:
             self.base_url = opts['url']
 
+        if self.title is None:
+            self.title = opts['title']
+
         if self.dformat is None:
             self.dformat = opts['date_format']
 
diff --git a/src/pyssg/pyssg.py b/src/pyssg/pyssg.py
index 40c602b..a2f5102 100644
--- a/src/pyssg/pyssg.py
+++ b/src/pyssg/pyssg.py
@@ -6,6 +6,8 @@ from .configuration import Configuration
 from .database import Database
 from .template import Template
 from .builder import HTMLBuilder
+from .page import Page
+from .rss import RSSBuilder
 
 
 def get_options() -> Namespace:
@@ -35,6 +37,11 @@ def get_options() -> Namespace:
                         default='',
                         type=str,
                         help='''base url without trailing slash''')
+    parser.add_argument('-t', '--title',
+                        default='Blog',
+                        type=str,
+                        help='''general title for the website; defaults to
+                        'Blog' ''')
     parser.add_argument('--date-format',
                         default='%a, %b %d, %Y @ %H:%M %Z',
                         type=str,
@@ -102,5 +109,10 @@ def main() -> None:
         builder: HTMLBuilder = HTMLBuilder(config, template, db)
         builder.build()
 
+        # get all parsed pages for rss construction
+        all_pages: list[Page] = builder.get_pages()
+        rss_builder: RSSBuilder = RSSBuilder(template.rss, all_pages)
+        rss_builder.build()
+
         db.write()
         return
diff --git a/src/pyssg/rss.py b/src/pyssg/rss.py
new file mode 100644
index 0000000..07776e1
--- /dev/null
+++ b/src/pyssg/rss.py
@@ -0,0 +1,51 @@
+import os
+import importlib.metadata
+from datetime import datetime, timezone
+
+from .page import Page
+from .configuration import Configuration
+
+
+VERSION = importlib.metadata.version('pyssg')
+DFORMAT = '%a, %d %b %Y %H:%M:%S %Z'
+
+
+class RSSBuilder:
+    def __init__(self, config: Configuration,
+                 template: str,
+                 pages: list[Page]):
+        self.rss: str = template
+        self.pages: list[Page] = pages
+
+
+    def build(self):
+        # initial base replacements
+        self.rss = self.rss.replace('$$TITLE', config.title)
+        self.rss = self.rss.replace('$$LINK', config.base_url)
+        self.rss = self.rss.replace('$$PYSSGVERSION', VERSION)
+        items_formatted: str = __get_items_formatted()
+        self.rss = self.rss.replace('$$ITEMS', items_formatted)
+
+        current_date: str = datetime.now(tz=timezone.utc).strftime(DFORMAT)
+        self.rss = self.rss.replace('$$CURRENTDATE', current_date)
+
+        with open(os.path.join(config.dst, 'rss.xml'), 'w') as f:
+            f.write(self.rss)
+
+
+    def __get_items_formatted(self) -> str:
+        # i_f=items formatted for short
+        i_f: str = ''
+        for p in pages:
+            url: str = f'{config.base_url}/{p.name.replace(".md", ".html")}'
+            date: str = p.c_datetime.strftime(DFORMAT)
+
+            i_f = f'{i_f}    <item>\n'
+            i_f = f'{i_f}      <title>{p.title}</title>\n'
+            i_f = f'{i_f}      <link>{url}</link>\n'
+            i_f = f'{i_f}      <description>{p.summary}</description>\n'
+            i_f = f'{i_f}      <guid isPermaLink="true">{url}</guid>\n'
+            i_f = f'{i_f}      <pubDate>{date}</pubDate>\n'
+            i_f = f'{i_f}    </item>\n'
+
+        return i_f
diff --git a/src/pyssg/template.py b/src/pyssg/template.py
index b3ce48d..a407475 100644
--- a/src/pyssg/template.py
+++ b/src/pyssg/template.py
@@ -26,6 +26,7 @@ class Template(HF):
         self.article: HF = HF()
         self.articles: Common = Common()
         self.tags: Common = Common()
+        self.rss: str = None
 
         self.is_read: bool = False
 
@@ -107,6 +108,37 @@ class Template(HF):
         self.__write_template('footer.html',
                               [''])
 
+        # go back to templates
+        os.chdir('..')
+
+        os.mkdir('rss')
+        os.chdir('rss')
+        self.__write_template('rss.xml',
+                              ['<?xml version="1.0" encoding="UTF-8" ?>\n',
+                               '<rss version="2.0">\n',
+                               '  <channel>\n',
+                               '    <title>$$TITLE</title>\n',
+                               '    <link>$$LINK</link>\n',
+                               '    <atom:link href="EXAMPLE.ORG/RSS.XML" rel="self" type="application/rss+xml"/>\n',
+                               '    <description>SHORT DESCRIPTION.</description>\n',
+                               '    <language>en-us</language>\n',
+                               '    <copyright>COPYRIGHT NOTICE.</copyright>\n',
+                               '    <managingEditor>EMAIL@EXAMPLE.ORG</managingEditor>\n',
+                               '    <webMaster>EMAIL@EXAMPLE.ORG</webMaster>\n',
+                               '    <pubDate>$$CURRENTDATE</pubDate>\n',
+                               '    <lastBuildDate>$$CURRENTDATE</lastBuildDate>\n',
+                               '    <generator>$$PYSSGVERSION</generator>\n',
+                               '    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>\n',
+                               '    <ttl>30</ttl>\n',
+                               '    <image>\n',
+                               '      <url>EXAMPLE.ORG/IMAGE.PNG</url>\n',
+                               '      <title>$$TITLE</title>\n',
+                               '      <link>$$LINK</link>\n',
+                               '    </image>\n',
+                               '$$ITEMS\n',
+                               '  </channel>\n',
+                               '</rss>'])
+
         # return to initial working directory
         os.chdir(iwd)
 
@@ -164,6 +196,13 @@ class Template(HF):
         self.tags.list_footer = self.__read_template('list_footer.html')
         self.tags.footer = self.__read_template('footer.html')
 
+        # go back to templates
+        os.chdir('..')
+
+        # tag
+        os.chdir('rss')
+        self.rss = self.__read_template('rss.xml')
+
         # return to initial working directory
         os.chdir(iwd)
 
-- 
cgit v1.2.3-70-g09d2