all repos — nasg @ 9f73a9b111535191800f681e968ec3a2d8f7f61c

- gopher? gopher.
- no js needed button
- removed donation, I'll figure out something better, sometimes, in the future
- text/plain alternate, also for gopher
- better Pandoc subclassing
- bye message
- first image becomes OG image
- removed duplicate reply symbol
- DAT .well-known prepare, not active
- oembed singular vars, should they ever be needed
- fixed target lookup for webmentions so it works both with index.html or with path only
Peter Molnar hello@petermolnar.eu
Mon, 25 Feb 2019 22:40:01 +0000
commit

9f73a9b111535191800f681e968ec3a2d8f7f61c

parent

67662b69e95840b66b0da682fd2a55a96d2f8c27

A assets/icomoon/SVG/facebook.svg

@@ -0,0 +1,5 @@

+<!-- Generated by IcoMoon.io --> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> +<title>facebook</title> +<path fill="#3b5998" d="M15.117 0h-14.235c-0.487 0-0.883 0.395-0.883 0.883v14.235c0 0.488 0.395 0.883 0.883 0.883h7.663v-6.196h-2.086v-2.414h2.086v-1.783c0-2.066 1.263-3.19 3.106-3.19 0.883 0 1.643 0.065 1.864 0.094v2.16h-1.281c-1 0-1.195 0.481-1.195 1.181v1.541h2.389l-0.31 2.42h-2.079v6.188h4.077c0.489 0 0.883-0.395 0.883-0.883v-14.235c0-0.487-0.395-0.883-0.883-0.883z"></path> +</svg>
A assets/nojs-button.svg

@@ -0,0 +1,27 @@

+<?xml version="1.0" encoding="UTF-8"?> +<svg width="80" height="15" version="1.1" viewBox="0 0 80 15" xmlns="http://www.w3.org/2000/svg"> + <rect width="80" height="15" fill="#666"/> + <rect x="1" y="1" width="78" height="13" fill="#fff"/> + <rect x="16" y="2" width="62" height="11" fill="#666"/> + <g fill="#fff"> + <path d="m27 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/> + <path d="m20 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/> + <rect x="-1" y="259.03" width="78" height="13"/> + <path d="m70 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/> + <path d="m66 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/> + <circle cx="7.5" cy="7.5" r="5.5" stroke="#666" style="paint-order:markers fill stroke"/> + </g> + <g fill="#666"> + <rect transform="rotate(-45)" x="-6" y="10" width="11" height="1"/> + <path d="m9 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/> + <path d="m6 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/> + </g> + <g fill="#fff"> + <path d="m40 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/> + <path d="m37 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/> + <path d="m57 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/> + <path d="m61 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/> + <path d="m53 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/> + <path d="m47 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/> + </g> +</svg>
M meta.pymeta.py

@@ -18,6 +18,7 @@ r'^(?P<year>[0-9]{4}):(?P<month>[0-9]{2}):(?P<day>[0-9]{2})\s+'

r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$' ) + class CachedMeta(dict): def __init__(self, fpath): self.fpath = fpath

@@ -25,7 +26,7 @@

@property def cfile(self): fname = os.path.basename(self.fpath) - if fname == 'index.md': + if fname == 'index.md': fname = os.path.basename(os.path.dirname(self.fpath)) return os.path.join(
M nasg.pynasg.py

@@ -21,6 +21,7 @@ from math import ceil

from urllib.parse import urlparse from collections import OrderedDict, namedtuple import logging +import csv import arrow import langdetect

@@ -31,14 +32,21 @@ import frontmatter

from feedgen.feed import FeedGenerator from slugify import slugify import requests +import lxml.etree as etree -from pandoc import PandocMarkdown +from pandoc import PandocMarkdown, PandocTXT from meta import Exif import settings from settings import struct import keys logger = logging.getLogger('NASG') + +CATEGORY = 'category' +MDFILE = 'index.md' +TXTFILE = 'index.txt' +HTMLFILE = 'index.html' +GOPHERFILE = 'gophermap' MarkdownImage = namedtuple( 'MarkdownImage',

@@ -71,6 +79,7 @@ """ return seconds level mtime or 0 (chomp microsecs) """

if os.path.exists(path): return int(os.path.getmtime(path)) return 0 + def utfyamldump(data): """ dump YAML with actual UTF-8 chars """

@@ -81,6 +90,7 @@ indent=4,

allow_unicode=True ) + def url2slug(url, limit=200): """ convert URL to max 200 char ASCII string """ return slugify(

@@ -89,13 +99,16 @@ only_ascii=True,

lower=True )[:limit] + J2.filters['url2slug'] = url2slug + def rfc3339todt(rfc3339): """ nice dates for humans """ t = arrow.get(rfc3339).format('YYYY-MM-DD HH:mm ZZZ') return "%s" % (t) + J2.filters['printdate'] = rfc3339todt RE_MYURL = re.compile(

@@ -104,6 +117,7 @@ settings.site.url,

settings.site.url ) ) + def relurl(text, baseurl=None): if not baseurl:

@@ -118,14 +132,16 @@ url = standalone

r = os.path.relpath(url, baseurl) if url.endswith('/') and not r.endswith('/'): - r = "%s/index.html" % r + r = "%s/%s" % (r, HTMLFILE) if needsquotes: r = '"%s"' % r logger.debug("RELURL: %s => %s (base: %s)", match, r, baseurl) text = text.replace(match, r) return text + J2.filters['relurl'] = relurl + def writepath(fpath, content, mtime=0): """ f.write with extras """

@@ -163,6 +179,7 @@

class AQ: """ Async queue which starts execution right on population """ + def __init__(self): self.loop = asyncio.get_event_loop() self.queue = asyncio.Queue(loop=self.loop)

@@ -174,7 +191,7 @@ async def consume(self):

while not self.queue.empty(): item = await self.queue.get() self.queue.task_done() - #asyncio.gather() ? + # asyncio.gather() ? def run(self): consumer = asyncio.ensure_future(self.consume())

@@ -183,6 +200,7 @@

class Webmention(object): """ outgoing webmention class """ + def __init__(self, source, target, dpath, mtime=0): self.source = source self.target = target

@@ -275,7 +293,7 @@ return self._parsed[1]

def pandoc(self, c): if c and len(c): - c = PandocMarkdown(c) + c = str(PandocMarkdown(c)) c = RE_PRECODE.sub( '<pre><code lang="\g<1>" class="language-\g<1>">', c) return c

@@ -305,7 +323,9 @@

@property def targetname(self): t = urlparse(self.meta.get('target')) - return t.path.rstrip('/').strip('/').split('/')[-1] + return os.path.split(t.path.lstrip('/'))[0] + #t = urlparse(self.meta.get('target')) + #return t.path.rstrip('/').strip('/').split('/')[-1] @property def source(self):

@@ -335,11 +355,10 @@

@property def type(self): return self.meta.get('type', 'webmention') - #if len(self.content): - #maybe = clean(self.content, strip=True) - #if maybe in UNICODE_EMOJI: - #return maybe - + # if len(self.content): + #maybe = clean(self.content, strip=True) + # if maybe in UNICODE_EMOJI: + # return maybe @cached_property def jsonld(self):

@@ -418,10 +437,24 @@ if c.dt > maybe:

maybe = c.dt return maybe + + @property + def dt(self): + dt = int(MarkdownDoc.dt.fget(self)) + for maybe in self.comments.keys(): + if int(dt) < int(maybe): + dt = int(maybe) + return dt + @property def sameas(self): r = [] - for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.copy')): + for k in glob.glob( + os.path.join( + os.path.dirname(self.fpath), + '*.copy' + ) + ): with open(k, 'rt') as f: r.append(f.read()) return r

@@ -436,7 +469,7 @@ comments = {}

files = [ k for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.md')) - if os.path.basename(k) != 'index.md' + if os.path.basename(k) != MDFILE ] for f in files: c = Comment(f)

@@ -463,9 +496,9 @@ if imghdr.what(imgpath):

images.update({match: WebImage(imgpath, mdimg, self)}) else: logger.error("Missing image: %s, referenced in %s", - imgpath, - self.fpath - ) + imgpath, + self.fpath + ) return images @property

@@ -493,7 +526,7 @@ """

if len(self.images) != 1: return False photo = next(iter(self.images.values())) - maybe = self.fpath.replace("index.md", "%s.jpg" % (self.name)) + maybe = self.fpath.replace(MDFILE, "%s.jpg" % (self.name)) if photo.fpath == maybe: return True return False

@@ -630,46 +663,46 @@ return True

else: return False - #@cached_property - #def oembed_xml(self): - #oembed = etree.Element("oembed", version="1.0") - #xmldoc = etree.ElementTree(oembed) - #for k, v in self.oembed_json.items(): - #x = etree.SubElement(oembed, k).text = "%s" % (v) - #s = etree.tostring( - #xmldoc, - #encoding='utf-8', - #xml_declaration=True, - #pretty_print=True - #) - #return s + @cached_property + def oembed_xml(self): + oembed = etree.Element("oembed", version="1.0") + xmldoc = etree.ElementTree(oembed) + for k, v in self.oembed_json.items(): + x = etree.SubElement(oembed, k).text = "%s" % (v) + s = etree.tostring( + xmldoc, + encoding='utf-8', + xml_declaration=True, + pretty_print=True + ) + return s - #@cached_property - #def oembed_json(self): - #r = { - #"version": "1.0", - #"provider_name": settings.site.name, - #"provider_url": settings.site.url, - #"author_name": settings.author.name, - #"author_url": settings.author.url, - #"title": self.title, - #"type": "link", - #"html": self.html_content, - #} + @cached_property + def oembed_json(self): + r = { + "version": "1.0", + "provider_name": settings.site.name, + "provider_url": settings.site.url, + "author_name": settings.author.name, + "author_url": settings.author.url, + "title": self.title, + "type": "link", + "html": self.html_content, + } - #img = None - #if self.is_photo: - #img = self.photo - #elif not self.is_photo and len(self.images): - #img = list(self.images.values())[0] - #if img: - #r.update({ - #"type": "rich", - #"thumbnail_url": img.jsonld.thumbnail.url, - #"thumbnail_width": img.jsonld.thumbnail.width, - #"thumbnail_height": img.jsonld.thumbnail.height - #}) - #return r + img = None + if self.is_photo: + img = self.photo + elif not self.is_photo and len(self.images): + img = list(self.images.values())[0] + if img: + r.update({ + "type": "rich", + "thumbnail_url": img.jsonld.thumbnail.url, + "thumbnail_width": img.jsonld.thumbnail.width, + "thumbnail_height": img.jsonld.thumbnail.height + }) + return r @cached_property def review(self):

@@ -744,7 +777,7 @@

if self.is_photo: r.update({ "@type": "Photograph", - "image": self.photo.jsonld, + #"image": self.photo.jsonld, }) elif self.has_code: r.update({

@@ -754,11 +787,15 @@ elif self.is_page:

r.update({ "@type": "WebPage", }) - if not self.is_photo and len(self.images): - img = list(self.images.values())[0] - r.update({ - "image": img.jsonld, - }) + if len(self.images): + r["image"] = [] + for img in list(self.images.values()): + r["image"].append(img.jsonld) + # if not self.is_photo and len(self.images): + # img = list(self.images.values())[0] + # r.update({ + # "image": img.jsonld, + # }) if self.is_reply: r.update({

@@ -775,8 +812,8 @@

if self.event: r.update({"subjectOf": self.event}) - for donation in settings.donateActions: - r["potentialAction"].append(donation) + #for donation in settings.donateActions: + #r["potentialAction"].append(donation) for url in list(set(self.syndicate)): r["potentialAction"].append({

@@ -795,15 +832,28 @@ def template(self):

return "%s.j2.html" % (self.__class__.__name__) @property + def gophertemplate(self): + return "%s.j2.txt" % (self.__class__.__name__) + + @property def renderdir(self): - return os.path.dirname(self.renderfile) + return os.path.join( + settings.paths.get('build'), + self.name + ) @property def renderfile(self): return os.path.join( - settings.paths.get('build'), - self.name, - 'index.html' + self.renderdir, + HTMLFILE + ) + + @property + def gopherfile(self): + return os.path.join( + self.renderdir, + TXTFILE ) @property

@@ -831,7 +881,15 @@ self.content,

]) async def copyfiles(self): - exclude = ['.md', '.jpg', '.png', '.gif', '.ping', '.url', '.del', '.copy'] + exclude = [ + '.md', + '.jpg', + '.png', + '.gif', + '.ping', + '.url', + '.del', + '.copy'] files = glob.glob( os.path.join( os.path.dirname(self.fpath),

@@ -854,39 +912,41 @@ continue

logger.info("copying '%s' to '%s'", f, t) cp(f, t) - @cached_property - def html(self): - r = J2.get_template(self.template).render({ + async def render(self): + if self.exists: + return + logger.info("rendering %s", self.name) + v = { 'baseurl': self.url, 'post': self.jsonld, 'site': settings.site, 'menu': settings.menu, 'meta': settings.meta, - }) - return r + } + writepath( + self.renderfile, + J2.get_template(self.template).render(v) + ) - async def render(self): - if self.exists: - return - logger.info("rendering %s", self.name) + g = { + 'post': self.jsonld, + 'summary': PandocTXT(self.summary), + 'content': PandocTXT(self.content) + } writepath( - self.renderfile, - self.html + self.gopherfile, + J2.get_template(self.gophertemplate).render(g) ) + j = settings.site.copy() j.update({ "mainEntity": self.jsonld }) writepath( - os.path.join(self.renderdir,'index.json'), + os.path.join(self.renderdir, 'index.json'), json.dumps(j, indent=4, ensure_ascii=False) ) del(j) - cp( - self.fpath, - os.path.join(self.renderdir,'index.md') - ) - class Home(Singular): def __init__(self, fpath):

@@ -904,7 +964,7 @@ @property

def renderfile(self): return os.path.join( settings.paths.get('build'), - 'index.html' + HTMLFILE ) @property

@@ -916,6 +976,27 @@ if pts > maybe:

maybe = pts return maybe + async def render_gopher(self): + lines = [ + "%s's gopherhole - phlog, if you prefer" % (settings.site.name), + '', + '' + ] + + for category, post in self.posts: + line = "1%s\t/%s/%s\t%s\t70" % ( + category['name'], + CATEGORY, + category['name'], + settings.site.name + ) + lines.append(line) + lines.append('') + lines.append('') + lines = lines + list(map(lambda x: ("%s" % x), settings.bye.split('\n'))) + lines.append('') + writepath(self.renderfile.replace(HTMLFILE,GOPHERFILE), "\r\n".join(lines)) + async def render(self): if self.exists: return

@@ -929,6 +1010,7 @@ 'meta': settings.meta,

'posts': self.posts }) writepath(self.renderfile, r) + await self.render_gopher() class WebImage(object):

@@ -1111,7 +1193,7 @@ mapping = {

'Model': ['Model'], 'FNumber': ['FNumber', 'Aperture'], 'ExposureTime': ['ExposureTime'], - 'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'], + 'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'], 'ISO': ['ISO'], 'LensID': ['LensID', 'LensSpec', 'Lens'], 'CreateDate': ['CreateDate', 'DateTimeOriginal']

@@ -1189,7 +1271,8 @@ @property

def data(self): with open(self.fpath, 'rb') as f: encoded = base64.b64encode(f.read()) - return "data:%s;base64,%s" % (self.parent.mime_type, encoded.decode('utf-8')) + return "data:%s;base64,%s" % ( + self.parent.mime_type, encoded.decode('utf-8')) @property def suffix(self):

@@ -1328,8 +1411,8 @@ def templatefile(self):

raise ValueError('Not implemented') async def render(self): - #if self.exists: - #return + # if self.exists: + # return await self._render()

@@ -1359,7 +1442,7 @@ notindexed=url,

notindexed=mtime, tokenize=porter )''' - ) + ) self.is_changed = False def __exit__(self):

@@ -1548,7 +1631,7 @@

@property def url(self): if len(self.name): - url = "%s/category/%s/" % (settings.site.get('url'), self.name) + url = "%s/%s/%s/" % (settings.site.get('url'), CATEGORY, self.name) else: url = '%s/' % (settings.site.get('url')) return url

@@ -1566,7 +1649,7 @@ def dpath(self):

if len(self.name): return os.path.join( settings.paths.get('build'), - 'category', + CATEGORY, self.name ) else:

@@ -1665,17 +1748,17 @@ },

'posts': posts, } - def indexfpath(self, subpath=None): + def indexfpath(self, subpath=None, fname=HTMLFILE): if subpath: return os.path.join( self.dpath, subpath, - 'index.html' + fname ) else: return os.path.join( self.dpath, - 'index.html' + fname ) async def render_feed(self, xmlformat):

@@ -1711,8 +1794,9 @@ })

fe.category({ 'term': post.category, 'label': post.category, - 'scheme': "%s/category/%s/" % ( + 'scheme': "%s/%s/%s/" % ( settings.site.get('url'), + CATEGORY, post.category ) })

@@ -1758,6 +1842,32 @@ self.tmplvars(self.get_posts())

) writepath(self.indexfpath(), r) + async def render_gopher(self): + lines = [ + '%s - %s' % (self.name, settings.site.name), + '', + '' + ] + for post in self.get_posts(): + line = "0%s\t/%s/%s\t%s\t70" % ( + post.headline, + post.name, + TXTFILE, + settings.site.name + ) + lines.append(line) + if isinstance(post['image'], list): + for img in post['image']: + line = "I%s\t/%s/%s\t%s\t70" % ( + img.headline, + post.name, + img.name, + settings.site.name + ) + lines.append(line) + lines.append('') + writepath(self.indexfpath(fname=GOPHERFILE), "\r\n".join(lines)) + async def render_archives(self): for year in self.years.keys(): if year == self.newest_year:

@@ -1779,7 +1889,7 @@ if value >= tsmin and index > end:

end = index if self.is_uptodate(fpath, self[self.sortedkeys[start]].dt): - logger.info("%s / %d is up to date", self.name, year) + logger.info("%s / %d is up to date", self.name, year) else: logger.info("updating %s / %d", self.name, year) logger.info("getting posts from %d to %d", start, end)

@@ -1788,7 +1898,7 @@ self.tmplvars(

# I don't know why end needs the +1, but without that # some posts disappear # TODO figure this out... - self.get_posts(start, end+1), + self.get_posts(start, end + 1), tyear ) )

@@ -1818,10 +1928,11 @@ logger.info(

'%s ATOM feed up to date', self.name ) - async def render(self): await self.render_feeds() + if not self.is_uptodate(self.indexfpath(), self.newest()): + await self.render_gopher() if not self.is_paginated: if not self.is_uptodate(self.indexfpath(), self.newest()): logger.info(

@@ -1829,6 +1940,7 @@ '%s flat index outdated, generating new',

self.name ) await self.render_flat() + else: logger.info( '%s flat index is up to date',

@@ -1837,6 +1949,7 @@ )

return else: await self.render_archives() + class Sitemap(dict):

@@ -1875,7 +1988,7 @@ def since(self):

newest = 0 content = settings.paths.get('content') for e in glob.glob(os.path.join(content, '*', '*', '*.md')): - if os.path.basename(e) == 'index.md': + if os.path.basename(e) == MDFILE: continue # filenames are like [received epoch]-[slugified source url].md try:

@@ -1889,7 +2002,7 @@ )

continue if mtime > newest: newest = mtime - return arrow.get(newest+1) + return arrow.get(newest + 1) def makecomment(self, webmention): if 'published_ts' in webmention.get('data'):

@@ -1899,12 +2012,19 @@ dt = arrow.get(webmention.get('verified_date'))

else: dt = arrow.get(webmention.get('data').get('published')) - slug = webmention.get('target').strip('/').split('/')[-1] + slug = os.path.split(urlparse(webmention.get('target')).path.lstrip('/'))[0] + # ignore selfpings if slug == settings.site.get('name'): return - fdir = glob.glob(os.path.join(settings.paths.get('content'), '*', slug)) + fdir = glob.glob( + os.path.join( + settings.paths.get('content'), + '*', + slug + ) + ) if not len(fdir): logger.error( "couldn't find post for incoming webmention: %s",

@@ -1962,6 +2082,7 @@ except ValueError as e:

logger.error('failed to query webmention.io: %s', e) pass + def make(): start = int(round(time.time() * 1000)) last = 0

@@ -1989,7 +2110,7 @@ categories = {}

frontposts = Category() home = Home(settings.paths.get('home')) - for e in sorted(glob.glob(os.path.join(content, '*', '*', 'index.md'))): + for e in sorted(glob.glob(os.path.join(content, '*', '*', MDFILE))): post = Singular(e) # deal with images, if needed for i in post.images.values():

@@ -2060,10 +2181,17 @@ # copy static files

for e in glob.glob(os.path.join(content, '*.*')): if e.endswith('.md'): continue - t = os.path.join(settings.paths.get('build'),os.path.basename(e)) + t = os.path.join(settings.paths.get('build'), os.path.basename(e)) if os.path.exists(t) and mtime(e) <= mtime(t): continue cp(e, t) + + # ... + #for url in settings.site.sameAs: + #if "dat://" in url: + #p = os.path.join(settings.paths.build, '.well-known', 'dat') + #if not os.path.exists(p): + #writepath(p, "%s\nTTL=3600" % (url)) end = int(round(time.time() * 1000)) logger.info('process took %d ms' % (end - start))
M pandoc.pypandoc.py

@@ -7,34 +7,43 @@

import subprocess import logging -class PandocMarkdown(str): - def __new__(cls, text): - """ Pandoc command line call with piped in- and output """ - cmd = ( + +class PandocBase(str): + in_format = 'html' + in_options = [] + out_format = 'plain' + out_options = [] + columns = None + + def __init__(self, text): + self.source = text + conv_to = '--to=%s' % (self.out_format) + if (len(self.out_options)): + conv_to = '%s+%s' % ( + conv_to, + '+'.join(self.out_options) + ) + + conv_from = '--from=%s' % (self.in_format) + if (len(self.in_options)): + conv_from = '%s+%s' % ( + conv_from, + '+'.join(self.in_options) + ) + + cmd = [ 'pandoc', '-o-', - '--from=markdown+%s' % ( - '+'.join([ - 'footnotes', - 'pipe_tables', - 'strikeout', - #'superscript', - #'subscript', - 'raw_html', - 'definition_lists', - 'backtick_code_blocks', - 'fenced_code_attributes', - 'shortcut_reference_links', - 'lists_without_preceding_blankline', - 'autolink_bare_uris', - ]) - ), - '--to=html5', + conv_to, + conv_from, '--quiet', '--no-highlight' - ) + ] + if self.columns: + cmd.append(self.columns) + p = subprocess.Popen( - cmd, + tuple(cmd), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,

@@ -48,46 +57,158 @@ cmd,

stderr ) r = stdout.decode('utf-8').strip() - return str.__new__(cls, r) + self.result = r + + def __str__(self): + return str(self.result) + + def __repr__(self): + return str(self.result) + + +class PandocMarkdown(PandocBase): + in_format = 'markdown' + in_options = [ + 'footnotes', + 'pipe_tables', + 'strikeout', + # 'superscript', + # 'subscript', + 'raw_html', + 'definition_lists', + 'backtick_code_blocks', + 'fenced_code_attributes', + 'shortcut_reference_links', + 'lists_without_preceding_blankline', + 'autolink_bare_uris', + ] + out_format = 'html5' + out_options = [] + + +class PandocHTML(PandocBase): + in_format = 'html' + in_options = [] + out_format = 'markdown' + out_options = [ + 'footnotes', + 'pipe_tables', + 'strikeout', + # 'superscript', + # 'subscript', + 'raw_html', + 'definition_lists', + 'backtick_code_blocks', + 'fenced_code_attributes', + 'shortcut_reference_links', + 'lists_without_preceding_blankline', + 'autolink_bare_uris', + ] + + +class PandocTXT(PandocBase): + in_format = 'markdown' + in_options = [ + 'footnotes', + 'pipe_tables', + 'strikeout', + # 'superscript', + # 'subscript', + 'raw_html', + 'definition_lists', + 'backtick_code_blocks', + 'fenced_code_attributes', + 'shortcut_reference_links', + 'lists_without_preceding_blankline', + 'autolink_bare_uris', + ] + out_format = 'plain' + out_options = [] + columns = '--columns=72' + + +#class PandocMarkdown(str): + #def __new__(cls, text): + #""" Pandoc command line call with piped in- and output """ + #cmd = ( + #'pandoc', + #'-o-', + #'--from=markdown+%s' % ( + #'+'.join([ + #'footnotes', + #'pipe_tables', + #'strikeout', + ## 'superscript', + ## 'subscript', + #'raw_html', + #'definition_lists', + #'backtick_code_blocks', + #'fenced_code_attributes', + #'shortcut_reference_links', + #'lists_without_preceding_blankline', + #'autolink_bare_uris', + #]) + #), + #'--to=html5', + #'--quiet', + #'--no-highlight' + #) + #p = subprocess.Popen( + #cmd, + #stdin=subprocess.PIPE, + #stdout=subprocess.PIPE, + #stderr=subprocess.PIPE, + #) -class PandocHTML(str): - def __new__(cls, text): - """ Pandoc command line call with piped in- and output """ - cmd = ( - 'pandoc', - '-o-', - '--to=markdown+%s' % ( - '+'.join([ - 'footnotes', - 'pipe_tables', - 'strikeout', - #'superscript', - #'subscript', - 'raw_html', - 'definition_lists', - 'backtick_code_blocks', - 'fenced_code_attributes', - 'shortcut_reference_links', - 'lists_without_preceding_blankline', - 'autolink_bare_uris', - ]) - ), - '--from=html', - '--quiet', - ) - p = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) + #stdout, stderr = p.communicate(input=text.encode()) + #if stderr: + #logging.warning( + #"Error during pandoc covert:\n\t%s\n\t%s", + #cmd, + #stderr + #) + #r = stdout.decode('utf-8').strip() + #return str.__new__(cls, r) - stdout, stderr = p.communicate(input=text.encode()) - if stderr: - logging.warning( - "Error during pandoc covert:\n\t%s\n\t%s", - cmd, - stderr - ) - r = stdout.decode('utf-8').strip() - return str.__new__(cls, r) + +#class PandocHTML(str): + #def __new__(cls, text): + #""" Pandoc command line call with piped in- and output """ + #cmd = ( + #'pandoc', + #'-o-', + #'--to=markdown+%s' % ( + #'+'.join([ + #'footnotes', + #'pipe_tables', + #'strikeout', + ## 'superscript', + ## 'subscript', + #'raw_html', + #'definition_lists', + #'backtick_code_blocks', + #'fenced_code_attributes', + #'shortcut_reference_links', + #'lists_without_preceding_blankline', + #'autolink_bare_uris', + #]) + #), + #'--from=html', + #'--quiet', + #) + #p = subprocess.Popen( + #cmd, + #stdin=subprocess.PIPE, + #stdout=subprocess.PIPE, + #stderr=subprocess.PIPE, + #) + + #stdout, stderr = p.communicate(input=text.encode()) + #if stderr: + #logging.warning( + #"Error during pandoc covert:\n\t%s\n\t%s", + #cmd, + #stderr + #) + #r = stdout.decode('utf-8').strip() + #return str.__new__(cls, r)
M settings.pysettings.py

@@ -47,6 +47,9 @@ "url": "https://petermolnar.net",

"name": "petermolnar.net", "image": "https://petermolnar.net/favicon.ico", "license": "https://spdx.org/licenses/%s.html" % (licence['_default']), + #"sameAs": [ + #"dat://8d03735af11d82fff82028e0f830f9ac470f5e9fbe10ab5eb6feb877232714a2" + #], "author": { "@context": "http://schema.org", "@type": "Person",

@@ -88,30 +91,28 @@ "@context": "http://schema.org",

"@type": "FollowAction", "url": "https://petermolnar.net/follow/", "name": "follow" - } + }, + #{ + #"@context": "http://schema.org", + #"@type": "DonateAction", + #"description": "Monzo (only in the UK or via Google Pay)", + #"name": "monzo", + #"price": "3GBP", + #"url": "https://monzo.me/petermolnar/3", + #"recipient": author + #}, + #{ + #"@context": "http://schema.org", + #"@type": "DonateAction", + #"description": "Paypal", + #"name": "paypal", + #"price": "3GBP", + #"url": "https://paypal.me/petermolnar/3GBP", + #"recipient": author + #} ] }) -donateActions = [ - { - "@context": "http://schema.org", - "@type": "DonateAction", - "description": "Monzo (only in the UK or via Google Pay)", - "name": "monzo", - "price": "3GBP", - "url": "https://monzo.me/petermolnar/3", - "recipient": author - }, - { - "@context": "http://schema.org", - "@type": "DonateAction", - "description": "Paypal", - "name": "paypal", - "price": "3GBP", - "url": "https://paypal.me/petermolnar/3GBP", - "recipient": author - } -] menu = { 'home': {

@@ -169,6 +170,29 @@ 720: '',

1280: '_b', }, }) + +bye = """ +███████╗███████╗███████╗ ██╗ ██╗ ██████╗ ██╗ ██╗ +██╔════╝██╔════╝██╔════╝ ╚██╗ ██╔╝██╔═══██╗██║ ██║ +███████╗█████╗ █████╗ ╚████╔╝ ██║ ██║██║ ██║ +╚════██║██╔══╝ ██╔══╝ ╚██╔╝ ██║ ██║██║ ██║ +███████║███████╗███████╗ ██║ ╚██████╔╝╚██████╔╝ +╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ + +███████╗██████╗ █████╗ ██████╗███████╗ +██╔════╝██╔══██╗██╔══██╗██╔════╝██╔════╝ +███████╗██████╔╝███████║██║ █████╗ +╚════██║██╔═══╝ ██╔══██║██║ ██╔══╝ +███████║██║ ██║ ██║╚██████╗███████╗ +╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝ + + ██████╗ ██████╗ ██╗ ██╗██████╗ ██████╗ ██╗ ██╗ +██╔════╝██╔═══██╗██║ ██║██╔══██╗██╔═══██╗╚██╗ ██╔╝ +██║ ██║ ██║██║ █╗ ██║██████╔╝██║ ██║ ╚████╔╝ +██║ ██║ ██║██║███╗██║██╔══██╗██║ ██║ ╚██╔╝ +╚██████╗╚██████╔╝╚███╔███╔╝██████╔╝╚██████╔╝ ██║ + ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═════╝ ╚═╝ + """ _parser = argparse.ArgumentParser(description='Parameters for NASG') _booleanparams = {
M templates/Singular.j2.htmltemplates/Singular.j2.html

@@ -10,6 +10,7 @@ <link rel="canonical" href="{{ post.url }}" />

<link rel="alternate" type="application/json" href="{{ post.url }}index.json" /> <link rel="alternate" type="application/ld+json" href="{{ post.url }}index.json" /> <link rel="alternate" type="application/mf2+json" href="https://pin13.net/mf2/?url={{ post.url|urlencode }}" /> + <link rel="alternate" type="text/plain" href="{{ post.url }}index.txt" /> <meta property="og:title" content="{{ post.headline }}" /> <meta property="og:type" content="article" /> <meta property="og:url" content="{{ post.url }}" />

@@ -17,11 +18,11 @@ <meta property="og:description" content="{{ post.description|striptags|e }}" />

<meta property="article:published_time" content="{{ post.datePublished }}" /> <meta property="article:modified_time" content="{{ post.dateModified }}" /> <meta property="article:author" content="{{ post.author.name }} ({{ post.author.email}})" /> - {% if post.image.url is defined %} - <meta property="og:image" content="{{ post.image.url }}" /> - <meta property="og:image:type" content="{{ post.image.encodingFormat }}" /> - <meta property="og:image:width" content="{{ post.image.width }}" /> - <meta property="og:image:height" content="{{ post.image.height }}" /> + {% if post.image is iterable %} + <meta property="og:image" content="{{ post.image[0].url }}" /> + <meta property="og:image:type" content="{{ post.image[0].encodingFormat }}" /> + <meta property="og:image:width" content="{{ post.image[0].width }}" /> + <meta property="og:image:height" content="{{ post.image[0].height }}" /> {% else %} <meta property="og:image" content="{{ post.image }}" /> {% endif %}

@@ -214,24 +215,6 @@ {% if 'InteractAction' == action['@type'] %}

<a href="{{ action.url }}"></a> {% endif %} {% endfor %} - </section> - - <section class="encourage"> - <h2>Encourage creation!</h2> - <p> - If this entry helped you, or you simply liked it, leave a tip via <br /> - {% set counters = {'donation': False} %} - {% for donate in post.potentialAction %} - {% if 'DonateAction' == donate['@type'] %} - {% if counters.donation %} or {% endif %} - <a href="{{ donate.url }}"> - <svg width="16" height="16"> - <use xlink:href="#icon-{{ donate.name }}"></use> - </svg> {{ donate.description }}</a> - {% if counters.update({'donation': True}) %} {% endif %} - {% endif %} - {% endfor %} - </p> </section> {% if post.comment|length %}
A templates/Singular.j2.txt

@@ -0,0 +1,10 @@

+--- +Title: {{ post.headline }} +Author: {{ post.author.name }} <{{ post.author.email}}> +URL: {{ post.url }} +Published: {{ post.datePublished|printdate }} +--- + +{{ summary }} + +{{ content }}
M templates/style.csstemplates/style.css

@@ -288,19 +288,6 @@ li p {

margin: 0; } -.encourage, .encourage a { - color: #090; -} - -.encourage a { - color: #0a0; - font-weight: bold; -} - -.encourage h2 { - border-color: #090; -} - .footnotes hr:before { content: 'Links'; color: #ccc;
M templates/symbols.svgtemplates/symbols.svg

@@ -77,9 +77,6 @@ </symbol>

<symbol id="icon-star" viewBox="0 0 16 16"> <path d="M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899z"></path> </symbol> - <symbol id="icon-reply" viewBox="0 0 16 16"> - <path d="M7 12.119v3.881l-6-6 6-6v3.966c6.98 0.164 6.681-4.747 4.904-7.966 4.386 4.741 3.455 12.337-4.904 12.119z"></path> - </symbol> <symbol id="button-indieweb" viewBox="0 0 80 15"> <rect width="80" height="15" fill="#666"/> <rect x="1" y="1" width="78" height="13" fill="#fff"/>