Just-what-you-need blogging with bari
Created:
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
= None
suf if len(sys.argv) > 1:
= sys.argv[1]
suf = time.strftime('%Y-%m-%d')
today = len(glob.glob('%s*.md' % (today, )))
mdl = len(glob.glob('%s*.txt' % (today, )))
txtl # print(f'{mdl}, {txtl}')
= len(glob.glob('%s*.md' % (today, ))) + len(glob.glob('%s*.txt' % (today, ))) + 1
cnt if suf:
= '%s-%s-%s.txt' % (today, cnt, suf)
fname else:
= '%s-%s.txt' % (today, cnt)
fname print(f'New draft file: {fname}')
= Path(fname)
p
p.touch()'bbedit', fname]) subprocess.call([
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:
= f.readlines()
lines = lines[0].strip()
title = md[:10]
date for line in lines:
if line.startswith('◊'):
= [
doctags '◊').strip() for tag in line.split(' ')
tag.lstrip(
]for dt in doctags:
= md.replace('.md', '.html')
link = {'title': title, 'date': date, 'link': link}
meta if dt in tags.keys():
tags[dt].append(meta)else:
= [meta]
tags[dt] return tags
with open('index.md', 'a') as f:
'\n## Tags\n')
f.write('<dl>')
f.write(for k in sorted(tags.keys()):
f.write('<dt><span id="{k}" class="tagged">{k}</span></dt>'.format(
=k))
k= ''.join([
tsoup '<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)'</dl>\n\n')
f.write(
def gen_home(self):
= len(self.allmd)
noteno = self.allmd[-self.top:]
selected with open('index.md', 'w') as f:
# write the top n posts
for post in reversed(selected):
with open(post, 'r') as fn:
= post[:10]
date = post.replace('.md', '.html')
link = fn.readlines()
lines = lines[0]
title 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.write(f'')
for line in self.proc_file(post):
f.write(line)'</div>\n')
f.write(-= 1
noteno # write the archive part
= list(
years reversed(sorted(list(set([md[:4] for md in self.allmd])))))
if len(self.allmd) <= self.SHOW_ON_HOME_PAGE:
return
'\n## Archive\n(Reverse chronologic)')
f.write('<dl id="archive-links">')
f.write(for post in reversed(self.allmd[:-self.SHOW_ON_HOME_PAGE]):
= post[:10]
date with open(post, 'r') as fn:
= fn.readlines()
lines = lines[0]
title = post.replace('.md', '.html')
link = post[:4]
year if year in years:
f'<dt class="archive-year" id="y{year}">{year}</dt>\n')
f.write(
years.remove(year)f'<dd class="archive-item"><p><span id="{noteno}">°{noteno}. <a href="{link}">{title}</a></span>\
f.write( <date>{date}</date></p></dd>')
-= 1
noteno '</dl>')
f.write(# write the tags
= self.get_tags()
tags '\n## Tags\n(Chronologic)\n')
f.write('<dl>')
f.write(for k in sorted(tags.keys()):
f.write('<dt><span id="{k}" class="tagged">{k}</span></dt>'.format(
=k))
k= ''.join([
tsoup '<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)'</dl>\n\n')
f.write(
def gen_atom_feed(self):
= '''<?xml version="1.0" encoding="utf-8"?>
feed_templ <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>
entry_templ <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:
= self.allmd[-self.top:]
selected with open('atom.xml', 'w') as of:
= ''
entries = 0
cnt for post in reversed(selected):
= post[:10] + 'T00:00:00.00Z'
updated if cnt == 0:
= updated
feed_updated += 1
cnt with open(post, 'r') as fn:
= fn.readlines()
lines = lines[0].strip()
title = post.replace('.md', '.html')
link = f'https://btbytes.github.io/{link}'
url = str(uuid.uuid4())
uid = entry_templ.format(
entry =title,
title=url,
url=uid,
uid=updated,
updated=title)
summary+= entry
entries = feed_templ.format(
content =feed_updated, entries=entries)
feed_updated
of.write(content)
def process_file(self, args):
print(''.join(self.proc_file(args.fpath)))
def proc_file(self, fpath):
= []
compiled = 0
pcount # 2020-05-01-01-interest-calculation.md -> 20050101
= fpath.split('/')[-1][0:13].replace('-', '')[2:]
docid with open(fpath) as f:
= f.readlines()
lines for line in lines[1:]:
if line.startswith('◊'): # tags
= [
doctags '◊').strip() for tag in line.split(' ')
tag.lstrip(
]' '.join([
compiled.append('<a href="index.html#{tag}" class="tag {tag}">{tag}\
</a> '.format(tag=tag) for tag in doctags
]))'\n')
compiled.append(elif line.startswith('¶'): # new part
= line.replace('¶', '')
line f'<a id="{docid}{pcount}"></a>{line} \
compiled.append( <a href="#{docid}{pcount}">¶</a>\
<div class="part"></div>')
+= 1
pcount else:
compiled.append(line)return compiled
def newdraft(self, args):
"""XXX: Fix"""
print('Create a draft')
= None
suf if len(sys.argv) > 1:
= sys.argv[1]
suf = time.strftime('%Y-%m-%d')
today = len(glob.glob('%s*.md' % (today, ))) + len(
cnt '%s*.txt' % (today, ))) + 1
glob.glob(if suf:
= '%s-%s-%s.txt' % (today, cnt, suf)
fname else:
= '%s-%s.txt' % (today, cnt)
fname print(f'New draft file: {fname}')
= Path(fname)
p
p.touch()'bbedit', fname])
subprocess.call([
def edit(self, args):
print('Edit a draft')
def prompt(zd):
= None
daft while not daft:
for k, v in zd.items():
= v[13:-4].replace('-', ' ').title()
title print(f'{k}. {title}')
print()
= input('Which draft to edit? ')
i = int(i)
i if i in zd.keys():
return zd[i]
elif i == 0:
0)
sys.exit(
= sorted(glob.glob('*.txt'))
drafts if len(drafts) == 0:
print('No drafts to edit.')
elif len(drafts) == 1:
'bbedit', drafts[0]])
subprocess.call([else:
= zip(range(1, len(drafts) + 1), drafts)
zd 'bbedit', prompt(dict(zd))])
subprocess.call([
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 = argparse.ArgumentParser(prog='bari')
parser = parser.add_subparsers(help='sub-command help')
subparsers = subparsers.add_parser('new', help='new draft')
parser_build =bari.newdraft)
parser_build.set_defaults(func= subparsers.add_parser('edit', help='edit a draft')
parser_build =bari.edit)
parser_build.set_defaults(func= subparsers.add_parser('build', help='build the site')
parser_build =bari.build)
parser_build.set_defaults(func= subparsers.add_parser('process', help='process a file')
parser_process 'fpath')
parser_process.add_argument(=bari.process_file)
parser_process.set_defaults(func= parser.parse_args()
args args.func(args)
edit
#!/usr/bin/env python
"""edit a draft"""
import glob
import os
import subprocess
import sys
def prompt(zd):
= None
daft while not daft:
for k, v in zd.items():
= v[13:-4].replace('-', ' ').title()
title print(f'{k}. {title}')
print()
= input('Which draft to edit? ')
i = int(i)
i if i in zd.keys():
return zd[i]
elif i == 0:
0)
sys.exit(
def main():
= sorted(glob.glob('*.txt'))
drafts if len(drafts) == 0:
print('No drafts to edit.')
elif len(drafts) == 1:
'bbedit', drafts[0]])
subprocess.call([else:
= zip(range(1, len(drafts) + 1), drafts)
zd 'bbedit', prompt(dict(zd))])
subprocess.call([
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"""
= time.strftime('%Y-%m-%d')
today = src.replace('.txt', '.md')
dst = today + dst[10:]
dst return dst
def pubfile(src):
= dst_name(src)
dst if os.path.exists(dst):
print(f'{dst} already exists.')
0)
sys.exist(print(f'publishing {src} -> {dst}')
os.rename(src, dst)
def prompt(zd):
= None
daft while not daft:
for k, v in zd.items():
print(f'{k}. {v}')
= input('Which draft to publish? ')
i = int(i)
i if i in zd.keys():
return zd[i]
def main():
= glob.glob('*.txt')
drafts if len(drafts) == 0:
print('No drafts to publish.')
elif len(drafts) == 1:
0])
pubfile(drafts[else:
= zip(range(1, len(drafts) + 1), drafts)
zd dict(zd)))
pubfile(prompt(
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