Update 1: attention conservation notice: This site is NOT using bari anymore.
Update 2: I’ve added the code below for reference.
I write this blog using a combination of pandoc
, make
and a small-ish python script called bari.
It is easy for me to design, write and maintain my own code than inherit someone else’s assumptions and “wants” for their software.
I saw another blog https://blog.notryan.com/ that is also written in a similar spirit — uses only C, and a bash script to generate a “text only” blog and RSS feed.
So, go on, write your own little program(s) to do what you need to get writing. Some of it is yak-shaving; but some yaks are small, some of you are expert shavers. You will figure it out.
Putting the code below for posteirity:
new
- a new draft
#!/usr/bin/env python3
"""new draft"""
import time
import subprocess
from pathlib import Path
import sys
import glob
suf = None
if len(sys.argv) > 1:
suf = sys.argv[1]
today = time.strftime('%Y-%m-%d')
mdl = len(glob.glob('%s*.md' % (today, )))
txtl = len(glob.glob('%s*.txt' % (today, )))
# print(f'{mdl}, {txtl}')
cnt = len(glob.glob('%s*.md' % (today, ))) + len(glob.glob('%s*.txt' % (today, ))) + 1
if suf:
fname = '%s-%s-%s.txt' % (today, cnt, suf)
else:
fname = '%s-%s.txt' % (today, cnt)
print(f'New draft file: {fname}')
p = Path(fname)
p.touch()
subprocess.call(['bbedit', fname])
bari
- publish the whole site
#!/usr/bin/env python3
from pathlib import Path
import glob
import os
import subprocess
import sys
import time
import uuid
class Bari(object):
def __init__(self):
self.SHOW_ON_HOME_PAGE = 5
self.allmd = sorted(glob.glob('2*.md'))
if len(self.allmd) > self.SHOW_ON_HOME_PAGE:
self.top = self.SHOW_ON_HOME_PAGE
else:
self.top = len(self.allmd)
def get_tags(self):
tags = {}
for md in self.allmd:
with open(md, 'r') as f:
lines = f.readlines()
title = lines[0].strip()
date = md[:10]
for line in lines:
if line.startswith('◊'):
doctags = [
tag.lstrip('◊').strip() for tag in line.split(' ')
]
for dt in doctags:
link = md.replace('.md', '.html')
meta = {'title': title, 'date': date, 'link': link}
if dt in tags.keys():
tags[dt].append(meta)
else:
tags[dt] = [meta]
return tags
with open('index.md', 'a') as f:
f.write('\n## Tags\n')
f.write('<dl>')
for k in sorted(tags.keys()):
f.write(
'<dt><span id="{k}" class="tagged">{k}</span></dt>'.format(
k=k))
tsoup = ''.join([
'<dd><p><a href="{link}">{title}</a>\
<date style="float:right;">{date}</date></p></dd> '
.format(**meta) for meta in tags[k]
])
f.write(tsoup)
f.write('</dl>\n\n')
def gen_home(self):
noteno = len(self.allmd)
selected = self.allmd[-self.top:]
with open('index.md', 'w') as f:
# write the top n posts
for post in reversed(selected):
with open(post, 'r') as fn:
date = post[:10]
link = post.replace('.md', '.html')
lines = fn.readlines()
title = lines[0]
f.write(f'<div class="post"><div class="post-title"><span id="{noteno}">°{noteno} // <a href="{link}">{title}</a></span> ')
f.write(f'<span class="post-date"><date>{date}</date></span></div>\n')
# f.write(f'')
for line in self.proc_file(post):
f.write(line)
f.write('</div>\n')
noteno -= 1
# write the archive part
years = list(
reversed(sorted(list(set([md[:4] for md in self.allmd])))))
if len(self.allmd) <= self.SHOW_ON_HOME_PAGE:
return
f.write('\n## Archive\n(Reverse chronologic)')
f.write('<dl id="archive-links">')
for post in reversed(self.allmd[:-self.SHOW_ON_HOME_PAGE]):
date = post[:10]
with open(post, 'r') as fn:
lines = fn.readlines()
title = lines[0]
link = post.replace('.md', '.html')
year = post[:4]
if year in years:
f.write(f'<dt class="archive-year" id="y{year}">{year}</dt>\n')
years.remove(year)
f.write(f'<dd class="archive-item"><p><span id="{noteno}">°{noteno}. <a href="{link}">{title}</a></span>\
<date>{date}</date></p></dd>')
noteno -= 1
f.write('</dl>')
# write the tags
tags = self.get_tags()
f.write('\n## Tags\n(Chronologic)\n')
f.write('<dl>')
for k in sorted(tags.keys()):
f.write(
'<dt><span id="{k}" class="tagged">{k}</span></dt>'.format(
k=k))
tsoup = ''.join([
'<dd><p><a href="{link}">{title}</a>\
<date style="float:right;">{date}</date></p></dd> '
.format(**meta) for meta in tags[k]
])
f.write(tsoup)
f.write('</dl>\n\n')
def gen_atom_feed(self):
feed_templ = '''<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>btbytes.github.io feed</title>
<link href="https://btbytes.github.io/"/>
<updated>{feed_updated}</updated>
<author>
<name>Pradeep Gowda</name>
</author>
<id>https://btbytes.github.io/</id>
{entries}
</feed>'''
entry_templ = '''<entry>
<title>{title}</title>
<link href="{url}"/>
<id>{url}</id>
<updated>{updated}</updated>
<summary>{summary}</summary>
</entry>
'''
if len(self.allmd) > self.SHOW_ON_HOME_PAGE:
selected = self.allmd[-self.top:]
with open('atom.xml', 'w') as of:
entries = ''
cnt = 0
for post in reversed(selected):
updated = post[:10] + 'T00:00:00.00Z'
if cnt == 0:
feed_updated = updated
cnt += 1
with open(post, 'r') as fn:
lines = fn.readlines()
title = lines[0].strip()
link = post.replace('.md', '.html')
url = f'https://btbytes.github.io/{link}'
uid = str(uuid.uuid4())
entry = entry_templ.format(
title=title,
url=url,
uid=uid,
updated=updated,
summary=title)
entries += entry
content = feed_templ.format(
feed_updated=feed_updated, entries=entries)
of.write(content)
def process_file(self, args):
print(''.join(self.proc_file(args.fpath)))
def proc_file(self, fpath):
compiled = []
pcount = 0
# 2020-05-01-01-interest-calculation.md -> 20050101
docid = fpath.split('/')[-1][0:13].replace('-', '')[2:]
with open(fpath) as f:
lines = f.readlines()
for line in lines[1:]:
if line.startswith('◊'): # tags
doctags = [
tag.lstrip('◊').strip() for tag in line.split(' ')
]
compiled.append(' '.join([
'<a href="index.html#{tag}" class="tag {tag}">{tag}\
</a> '.format(tag=tag) for tag in doctags
]))
compiled.append('\n')
elif line.startswith('¶'): # new part
line = line.replace('¶', '')
compiled.append(f'<a id="{docid}{pcount}"></a>{line} \
<a href="#{docid}{pcount}">¶</a>\
<div class="part"></div>')
pcount += 1
else:
compiled.append(line)
return compiled
def newdraft(self, args):
"""XXX: Fix"""
print('Create a draft')
suf = None
if len(sys.argv) > 1:
suf = sys.argv[1]
today = time.strftime('%Y-%m-%d')
cnt = len(glob.glob('%s*.md' % (today, ))) + len(
glob.glob('%s*.txt' % (today, ))) + 1
if suf:
fname = '%s-%s-%s.txt' % (today, cnt, suf)
else:
fname = '%s-%s.txt' % (today, cnt)
print(f'New draft file: {fname}')
p = Path(fname)
p.touch()
subprocess.call(['bbedit', fname])
def edit(self, args):
print('Edit a draft')
def prompt(zd):
daft = None
while not daft:
for k, v in zd.items():
title = v[13:-4].replace('-', ' ').title()
print(f'{k}. {title}')
print()
i = input('Which draft to edit? ')
i = int(i)
if i in zd.keys():
return zd[i]
elif i == 0:
sys.exit(0)
drafts = sorted(glob.glob('*.txt'))
if len(drafts) == 0:
print('No drafts to edit.')
elif len(drafts) == 1:
subprocess.call(['bbedit', drafts[0]])
else:
zd = zip(range(1, len(drafts) + 1), drafts)
subprocess.call(['bbedit', prompt(dict(zd))])
def publish(self, args):
print('Publish a draft')
def build(self, args):
self.gen_home()
self.gen_atom_feed()
if __name__ == '__main__':
import argparse
bari = Bari()
parser = argparse.ArgumentParser(prog='bari')
subparsers = parser.add_subparsers(help='sub-command help')
parser_build = subparsers.add_parser('new', help='new draft')
parser_build.set_defaults(func=bari.newdraft)
parser_build = subparsers.add_parser('edit', help='edit a draft')
parser_build.set_defaults(func=bari.edit)
parser_build = subparsers.add_parser('build', help='build the site')
parser_build.set_defaults(func=bari.build)
parser_process = subparsers.add_parser('process', help='process a file')
parser_process.add_argument('fpath')
parser_process.set_defaults(func=bari.process_file)
args = parser.parse_args()
args.func(args)
edit
#!/usr/bin/env python
"""edit a draft"""
import glob
import os
import subprocess
import sys
def prompt(zd):
daft = None
while not daft:
for k, v in zd.items():
title = v[13:-4].replace('-', ' ').title()
print(f'{k}. {title}')
print()
i = input('Which draft to edit? ')
i = int(i)
if i in zd.keys():
return zd[i]
elif i == 0:
sys.exit(0)
def main():
drafts = sorted(glob.glob('*.txt'))
if len(drafts) == 0:
print('No drafts to edit.')
elif len(drafts) == 1:
subprocess.call(['bbedit', drafts[0]])
else:
zd = zip(range(1, len(drafts) + 1), drafts)
subprocess.call(['bbedit', prompt(dict(zd))])
if __name__ == '__main__':
main()
lish
- publish a draft
#!/usr/bin/env python
"""publish a draft"""
import os
import sys
import glob
import time
def dst_name(src, dt=time.strftime('%Y-%m-%d')):
"""return the new filename with today's date"""
today = time.strftime('%Y-%m-%d')
dst = src.replace('.txt', '.md')
dst = today + dst[10:]
return dst
def pubfile(src):
dst = dst_name(src)
if os.path.exists(dst):
print(f'{dst} already exists.')
sys.exist(0)
print(f'publishing {src} -> {dst}')
os.rename(src, dst)
def prompt(zd):
daft = None
while not daft:
for k, v in zd.items():
print(f'{k}. {v}')
i = input('Which draft to publish? ')
i = int(i)
if i in zd.keys():
return zd[i]
def main():
drafts = glob.glob('*.txt')
if len(drafts) == 0:
print('No drafts to publish.')
elif len(drafts) == 1:
pubfile(drafts[0])
else:
zd = zip(range(1, len(drafts) + 1), drafts)
pubfile(prompt(dict(zd)))
if __name__ == '__main__':
# print(dst_name(src='2019-01-01-01-hello.txt', dt='2020-04-01'))
main()
push
- push to github
#!/usr/bin/env bash
make -j4
git add *.md
git add *.html
git commit -am "A new build"
git push origin