summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--README.md33
-rw-r--r--src/pyssg/builder.py45
-rw-r--r--src/pyssg/configuration.py90
-rw-r--r--src/pyssg/pyssg.py57
5 files changed, 182 insertions, 48 deletions
diff --git a/ChangeLog b/ChangeLog
index 4c27f95..3104adb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,11 @@
CHANGES
=======
+v0.2.2
+------
+
+* fix sorting of pages and update default templates
+
v0.2.1
------
diff --git a/README.md b/README.md
index 79d6707..67649b8 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,28 @@
-# pyssg
+# pyssg - Static Site Generator written in Python
-Static Site Generator inspired by Roman Zolotarev's [`ssg5`](https://rgz.ee/bin/ssg5) and [`rssg`](https://rgz.ee/bin/rssg), Luke Smith's [`lb` and `sup`](https://github.com/LukeSmithxyz/lb) and, pedantic.software's [`blogit`](https://pedantic.software/git/blogit/).
+Inspired (initially) by Roman Zolotarev's [`ssg5`](https://rgz.ee/bin/ssg5) and [`rssg`](https://rgz.ee/bin/rssg), Luke Smith's [`lb` and `sup`](https://github.com/LukeSmithxyz/lb) and, pedantic.software's great (but *"mamador"*, as I would say in spanish) [`blogit`](https://pedantic.software/git/blogit/).
-The reason of making this in python is because I was tired of wanting (some) features from all of these minimal programs, but being a pain in the ass to maintain or add features on my own, specially to `blogit`...; making minimalist software it's great and all, but there is a limit on how pretentious and elitist a software can be for me to accept it.
+I'm writing this in *pYtHoN* (thought about doing it in Go, but I'm most comfortable with Python at the moment) because I want features from all of these minimal programs (and more), but I don't really want to be stitching each one of the features on any of these programs, because they're written in a way to only work as how they were first imagined to work like; I already tried adding features to `ssg` and ended up rewriting it in POSIX shell, but it was a pain in the ass when I tried to add even more, and don't get me started on trying to extend `blogit`... And also because I want to.
## Current features
-This is still a WIP. Still doesn't build `sitemap.xml` or `rss.xml` files.
+**This is still a WIP. Still doesn't build `sitemap.xml` or `rss.xml` files.**
- [x] Build static site parsing `markdown` files ( `*.md` -> `*.html`)
+ - [x] Using plain `*.html` files for templates.
+ - [ ] Would like to change to something more flexible and easier to manage ([`jinja`](), for example).
- [x] Preserves hand-made `*.html` files.
- [x] Tag functionality.
- [ ] Open Graph (and similar) support.
- [ ] Build `sitemap.xml` file.
- [ ] Build `rss.xml` file.
+- [x] Only build page if `*.md` is new or updated.
+ - [ ] Extend this to tag pages and index (right now all tags and index is built no matter if no new/updated file is present).
+- [x] Configuration file as an alternative to using command line flags (configuration file options are prioritized).
-## Markdown features
+### Markdown features
-This program uses [`python-markdown`](https://python-markdown.github.io/) package with the following [extensions](https://python-markdown.github.io/extensions/):
+This program uses the base [`markdown` syntax]() plus additional syntax, all thanks to [`python-markdown`](https://python-markdown.github.io/) that provides [extensions](https://python-markdown.github.io/extensions/). The following extensions are used:
- Extra (collection of QoL extensions).
- Meta-Data.
@@ -26,8 +31,20 @@ This program uses [`python-markdown`](https://python-markdown.github.io/) packag
- Table of Contents.
- WikiLinks.
+## Installation
+
+Just install it with `pip`:
+
+```sh
+pip install pyssg
+```
+
+*EW!*, I know..., I will try to make a PKBUILD and release it in AUR or something; hit me up if you do it to add it here.
+
## Usage
+It is intended to be used as a standalone terminal program running on the "root" directory where you have the `src` and `dst` directories in (defaults for both flags).
+
First initialize the directories you're going to use for the source files and destination files:
```sh
@@ -39,7 +56,7 @@ That creates the desired directories with the basic templates that can be edited
Build the site with:
```sh
-pyssg -s src_dir -d dst_dir -b
+pyssg -s src_dir -d dst_dir -u https://base.url -b
```
-That creates all `*.html` for the site and can be easily moved to the server. Where an optional `-u` flag can be provided for the base URL (don't include the trailing slash `/`)
+That creates all `*.html` for the site and can be easily moved to the server. Here, the `-u` flag is technically optional in the sense that you'll not receive a warning/error, but it's used to prepend links with this URL (not strictly required everywhere), so don't ignore it; also don't include the trailing `/`.
diff --git a/src/pyssg/builder.py b/src/pyssg/builder.py
index b8acf08..8472b20 100644
--- a/src/pyssg/builder.py
+++ b/src/pyssg/builder.py
@@ -2,6 +2,7 @@ import os
import shutil
from copy import deepcopy
+from .configuration import Configuration
from .template import Template
from .database import Database
from .parser import MDParser
@@ -9,37 +10,19 @@ from .page import Page
from .discovery import get_file_list, get_dir_structure
class HTMLBuilder:
- def __init__(self, src: str,
- dst: str,
- base_url: str,
+ def __init__(self, config: Configuration,
template: Template,
- db: Database,
- dformat: str=None,
- l_dformat: str=None,
- lsep_dformat: str=None):
- self.src: str = src
- self.dst: str = dst
- self.base_url: str = base_url
+ db: Database):
+ self.src: str = config.src
+ self.dst: str = config.dst
+ self.base_url: str = config.base_url
+ self.dformat: str = config.dformat
+ self.l_dformat: str = config.l_dformat
+ self.lsep_dformat: str = config.lsep_dformat
+ self.force: bool = config.force
+
self.template: Template = template
self.db: Database = db
- self.dformat: str = None
- self.l_dformat: str = None
- self.lsep_dformat: str = None
-
- if dformat is not None:
- self.dformat = dformat
- else:
- self.dformat = "%a, %d %b, %Y @ %H:%M %Z"
-
- if l_dformat is not None:
- self.l_dformat = l_dformat
- else:
- self.l_dformat = "%b %d"
-
- if lsep_dformat is not None:
- self.lsep_dformat = lsep_dformat
- else:
- self.lsep_dformat = "%B %Y"
self.dirs: list[str] = None
self.md_files: list[str] = None
@@ -61,7 +44,11 @@ class HTMLBuilder:
self.__create_article_index(parser.all_tags, parser.all_pages)
# create each category of html pages
- self.__create_articles(parser.updated_pages)
+ # check if all pages should be created
+ if self.force:
+ self.__create_articles(parser.all_pages)
+ else:
+ self.__create_articles(parser.updated_pages)
self.__create_tags(parser.all_tags, parser.all_pages)
diff --git a/src/pyssg/configuration.py b/src/pyssg/configuration.py
new file mode 100644
index 0000000..3f7acfe
--- /dev/null
+++ b/src/pyssg/configuration.py
@@ -0,0 +1,90 @@
+import os
+
+
+class Configuration:
+ def __init__(self, path: str):
+ self.path: str = path
+ self.src: str = None
+ self.dst: str = None
+ self.base_url: str = None
+ self.dformat: str = None
+ self.l_dformat: str = None
+ self.lsep_dformat: str = None
+ self.force: bool = None
+
+
+ def read(self):
+ try:
+ lines: list[str] = None
+ with open(self.path, 'r') as f:
+ lines = f.readlines()
+
+ opts: dict[str, Union[str, bool]] = dict()
+ for l in lines:
+ kv: list[str] = l.split('=', 1)
+ if len(kv) != 2:
+ raise Exception('wrong config syntax')
+
+ k: str = kv[0].strip()
+ k_temp: str = kv[0].strip()
+ # check if value should be a boolean true
+ v: Union[str, bool] = k_temp\
+ if k_temp.lower() not in ['true', '1', 'yes']\
+ else True
+
+ opts[k] = v
+
+ try:
+ self.src = opts['SRC_PATH']
+ except KeyError: pass
+
+ try:
+ self.dst = opts['SRC_PATH']
+ except KeyError: pass
+
+ try:
+ self.base_url = opts['BASE_URL']
+ except KeyError: pass
+
+ try:
+ self.dformat = opts['DATE_FORMAT']
+ except KeyError: pass
+
+ try:
+ self.l_dformat = opts['LIST_DATE_FORMAT']
+ except KeyError: pass
+
+ try:
+ self.lsep_dformat = opts['LIST_SEP_DATE_FORMAT']
+ except KeyError: pass
+
+ try:
+ # if the parser above didn't read a boolean true, then take it
+ # as a false anyways
+ self.force = opts['FORCE'] if opts['FORCE'] is True else False
+ except KeyError: pass
+
+ except OSError: pass
+
+
+ def fill_missing(self, opts: dict[str, Union[str, bool]]) -> None:
+ if self.src is None:
+ self.src = opts['src']
+
+ if self.dst is None:
+ self.dst = opts['dst']
+
+ if self.base_url is None:
+ self.base_url = opts['url']
+
+ if self.dformat is None:
+ self.dformat = opts['date-format']
+
+ if self.l_dformat is None:
+ self.l_dformat = opts['list-date-format']
+
+ if self.lsep_dformat is None:
+ self.lsep_dformat = opts['list-sep-date-format']
+
+ if self.force is None:
+ self.force = opts['force']
diff --git a/src/pyssg/pyssg.py b/src/pyssg/pyssg.py
index 3cda0bc..2f46c18 100644
--- a/src/pyssg/pyssg.py
+++ b/src/pyssg/pyssg.py
@@ -1,6 +1,7 @@
import os
from argparse import ArgumentParser, Namespace
+from .configuration import Configuration
from .database import Database
from .template import Template
from .builder import HTMLBuilder
@@ -9,7 +10,16 @@ from .builder import HTMLBuilder
def get_options() -> Namespace:
parser = ArgumentParser(prog='pyssg',
description='''Static Site Generator that reads
- Markdown files and creates HTML files.''')
+ Markdown files and creates HTML files.\nIf
+ [-c]onfig file is provided (or exists in default
+ location) all other options are ignored.\nFor
+ datetime formats see:
+ https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes''')
+ parser.add_argument('-c', '--config',
+ default='$XDG_CONFIG_HOME/pyssg/pyssgrc',
+ type=str,
+ help='''config file (path) to read from; defaults to
+ '$XDG_CONFIG_HOME/pyssg/pyssgrc' ''')
parser.add_argument('-s', '--src',
default='src',
type=str,
@@ -24,6 +34,24 @@ def get_options() -> Namespace:
default='',
type=str,
help='''base url without trailing slash''')
+ parser.add_argument('--date-format',
+ default='%a, %b %d, %Y @ %H:%M %Z',
+ type=str,
+ help='''date format used inside pages (for creation and
+ modification times, for example); defaults to '%a, %b
+ %d, %Y @ %H:%M %Z' ('Tue, Mar 16, 2021 @ 02:46 UTC',
+ for example)''')
+ parser.add_argument('--list-date-format',
+ default='%b %d',
+ type=str,
+ help='''date format used for page entries in a list;
+ defaults to '%b %d' ('Mar 16', for example)''')
+ parser.add_argument('--list-sep-date-format',
+ default='%B %Y',
+ type=str,
+ help='''date format used for the separator between page
+ entries in a list; defaults to '%B %Y' ('March 2021',
+ for example)''')
parser.add_argument('-i', '--init',
action='store_true',
help='''initializes the dir structure, templates,
@@ -32,38 +60,45 @@ def get_options() -> Namespace:
action='store_true',
help='''generates all html files and passes over
existing (handmade) ones''')
+ parser.add_argument('-f', '--force',
+ action='store_true',
+ help='''force building all pages and not only the
+ updated ones''')
return parser.parse_args()
def main() -> None:
- opts: dict[str] = vars(get_options())
- src: str = opts['src']
- dst: str = opts['dst']
- base_url: str = opts['url']
+ opts: dict[str, Union[str, bool]] = vars(get_options())
+ conf_path: str = opts['config']
+ conf_path = os.path.expandvars(conf_path)
+
+ config: Configuration = Configuration(conf_path)
+ config.read()
+ config.fill_missing(opts)
if opts['init']:
try:
- os.mkdir(src)
- os.makedirs(os.path.join(dst, 'tag'))
+ os.mkdir(config.src)
+ os.makedirs(os.path.join(config.dst, 'tag'))
except FileExistsError:
pass
# write default templates
- template: Template = Template(src)
+ template: Template = Template(config.src)
template.write()
return
if opts['build']:
# start the db
- db: Database = Database(os.path.join(src, '.files'))
+ db: Database = Database(os.path.join(config.src, '.files'))
db.read()
# read templates
- template: Template = Template(src)
+ template: Template = Template(config.src)
template.read()
- builder: HTMLBuilder = HTMLBuilder(src, dst, base_url, template, db)
+ builder: HTMLBuilder = HTMLBuilder(config, template, db)
builder.build()
db.write()