diff --git a/nasg.py b/nasg.py index 54da21a..b83857e 100644 --- a/nasg.py +++ b/nasg.py @@ -19,6 +19,7 @@ from shutil import copy2 as cp from math import ceil from urllib.parse import urlparse from collections import OrderedDict, namedtuple +import logging import arrow import langdetect import wand.image @@ -36,6 +37,8 @@ import keys from pprint import pprint +logger = logging.getLogger('NASG') + MarkdownImage = namedtuple( 'MarkdownImage', ['match', 'alt', 'fname', 'title', 'css'] @@ -64,6 +67,18 @@ RE_PRECODE = re.compile( r'
'
)
+def writepath(fpath, content, mtime=0):
+ d = os.path.dirname(fpath)
+ if not os.path.isdir(d):
+ logger.debug('creating directory tree %s', d)
+ os.makedirs(d)
+ with open(fpath, 'wt') as f:
+ logger.info('writing file %s', fpath)
+ f.write(content)
+ # TODO
+ #if (mtime > 0):
+
+
#def relurl(url,base=settings.site.get('url')):
#url =urlparse(url)
#base = urlparse(base)
@@ -118,11 +133,7 @@ class Webmention(object):
return False
async def save(self, content):
- d = os.path.dirname(self.fpath)
- if not os.path.isdir(d):
- os.makedirs(d)
- with open(self.fpath, 'wt') as f:
- f.write(content)
+ writepath(self.fpath, content)
async def send(self):
if self.exists:
@@ -134,13 +145,13 @@ class Webmention(object):
'target': '%s' % (self.target)
}
r = requests.post(telegraph_url, data=telegraph_params)
- settings.logger.info(
+ logger.info(
"sent webmention to telegraph from %s to %s",
self.source,
self.target
)
if r.status_code not in [200, 201, 202]:
- settings.logger.error('sending failed: %s %s', r.status_code, r.text)
+ logger.error('sending failed: %s %s', r.status_code, r.text)
else:
await self.save(r.text)
@@ -149,7 +160,7 @@ class MarkdownDoc(object):
@cached_property
def _parsed(self):
with open(self.fpath, mode='rt') as f:
- settings.logger.debug('parsing YAML+MD file %s', self.fpath)
+ logger.debug('parsing YAML+MD file %s', self.fpath)
meta, txt = frontmatter.parse(f.read())
return(meta, txt)
@@ -571,19 +582,17 @@ class Singular(MarkdownDoc):
def template(self):
return "%s.j2.html" % (self.__class__.__name__)
- @cached_property
+ @property
def renderdir(self):
- d = os.path.join(
- settings.paths.get('build'),
- self.name
- )
- if not os.path.isdir(d):
- os.makedirs(d)
- return d
+ return os.path.dirname(self.renderfile)
@property
def renderfile(self):
- return os.path.join(self.renderdir, 'index.html')
+ return os.path.join(
+ settings.paths.get('build'),
+ self.name,
+ 'index.html'
+ )
@property
def exists(self):
@@ -610,7 +619,7 @@ class Singular(MarkdownDoc):
#fm.metadata = self.meta
#fm.content = self.content
#with open(fpath, 'wt') as f:
- #settings.logger.info("updating %s", fpath)
+ #logger.info("updating %s", fpath)
#f.write(frontmatter.dumps(fm))
async def copyfiles(self):
@@ -631,13 +640,13 @@ class Singular(MarkdownDoc):
)
if os.path.exists(t) and os.path.getmtime(f) <= os.path.getmtime(t):
continue
- settings.logger.info("copying '%s' to '%s'", f, t)
+ logger.info("copying '%s' to '%s'", f, t)
cp(f, t)
async def render(self):
if self.exists:
return
- settings.logger.info("rendering %s", self.name)
+ logger.info("rendering %s", self.name)
r = J2.get_template(self.template).render({
'post': self.tmplvars,
'site': settings.site,
@@ -646,17 +655,12 @@ class Singular(MarkdownDoc):
'licence': settings.licence,
'tips': settings.tips,
})
- if not os.path.isdir(self.renderdir):
- settings.logger.info("creating directory: %s", self.renderdir)
- os.makedirs(self.renderdir)
- with open(self.renderfile, 'wt') as f:
- settings.logger.info("saving to %s", self.renderfile)
- f.write(r)
+ writepath(self.renderfile, r)
class WebImage(object):
def __init__(self, fpath, mdimg, parent):
- settings.logger.debug("loading image: %s", fpath)
+ logger.debug("loading image: %s", fpath)
self.mdimg = mdimg
self.fpath = fpath
self.parent = parent
@@ -868,7 +872,7 @@ class WebImage(object):
img = self._maybe_watermark(img)
for size, resized in self.resized_images:
if not resized.exists or settings.args.get('regenerate'):
- settings.logger.info(
+ logger.info(
"resizing image: %s to size %d",
os.path.basename(self.fpath),
size
@@ -985,7 +989,7 @@ class WebImage(object):
# this is to make sure pjpeg happens
with open(self.fpath, 'wb') as f:
- settings.logger.info("writing %s", self.fpath)
+ logger.info("writing %s", self.fpath)
thumb.save(file=f)
@@ -1129,9 +1133,7 @@ class Search(PHPFile):
'licence': settings.licence,
'tips': settings.tips,
})
- with open(self.renderfile, 'wt') as f:
- settings.logger.info("rendering to %s", self.renderfile)
- f.write(r)
+ writepath(self.renderfile, r)
class IndexPHP(PHPFile):
@@ -1169,7 +1171,7 @@ class IndexPHP(PHPFile):
'redirects': self.redirect
})
with open(self.renderfile, 'wt') as f:
- settings.logger.info("rendering to %s", self.renderfile)
+ logger.info("rendering to %s", self.renderfile)
f.write(r)
@@ -1192,7 +1194,7 @@ class WebhookPHP(PHPFile):
'zapier': keys.zapier,
})
with open(self.renderfile, 'wt') as f:
- settings.logger.info("rendering to %s", self.renderfile)
+ logger.info("rendering to %s", self.renderfile)
f.write(r)
@@ -1243,7 +1245,7 @@ class Category(dict):
return url
@property
- def feed(self):
+ def feedurl(self):
return "%sfeed/" % (self.url)
@property
@@ -1251,7 +1253,7 @@ class Category(dict):
return "%s.j2.html" % (self.__class__.__name__)
@property
- def renderdir(self):
+ def dpath(self):
if len(self.name):
return os.path.join(
settings.paths.get('build'),
@@ -1262,9 +1264,14 @@ class Category(dict):
return settings.paths.get('build')
def navlink(self, ts):
+ label = ts.format(self.trange)
+ if arrow.utcnow().format(self.trange) == label:
+ url = self.url
+ else:
+ url = "%s%s/" % (self.url, label)
return {
- 'url': "%s%s/" % (self.url, ts.format(self.trange)),
- 'label': ts.format(self.trange)
+ 'url': url,
+ 'label': label
}
def tmplvars(self, posts=[], c=False, p=False, n=False):
@@ -1288,49 +1295,68 @@ class Category(dict):
'name': self.name,
'display': self.display,
'url': self.url,
- 'feed': "%s%s/" % (self.url, 'feed'),
- #'jsonfeed': "%s%s/index.json" % (self.url, 'feed'),
+ 'feed': self.feedurl,
'title': self.title,
'current': c,
'previous': p,
'next': n,
+ 'currentyear': arrow.utcnow().format('YYYY')
},
'posts': posts,
}
@property
def mtime(self):
- return arrow.get(self[self.sortedkeys[0]].published).timestamp
+ return arrow.get(self[self.sortedkeys[0]].published).timestamp
+
+ # @property
+ # def exists(self):
+ # if settings.args.get('force'):
+ # return False
+ # ismissing = False
+ # for f in [
+ # os.path.join(self.renderdir, 'feed', 'index.xml'),
+ # ]:
+ # if not os.path.exists(f):
+ # ismissing = True
+ # elif self.mtime > os.path.getmtime(f):
+ # ismissing = True
+ # if ismissing:
+ # return False
+ # else:
+ # return True
@property
- def exists(self):
- if settings.args.get('force'):
- return False
- ismissing = False
- for f in [
- os.path.join(self.renderdir, 'feed', 'index.xml'),
- ]:
- if not os.path.exists(f):
- ismissing = True
- elif self.mtime > os.path.getmtime(f):
- ismissing = True
- if ismissing:
- return False
+ def rssfeedfpath(self):
+ return os.path.join(
+ self.dpath,
+ 'feed',
+ 'index.xml'
+ )
+
+ @property
+ def atomfeedfpath(self):
+ return os.path.join(
+ self.dpath,
+ 'feed',
+ 'atom.xml'
+ )
+
+ def indexfpath(self, subpath=None):
+ if subpath:
+ return os.path.join(
+ self.dpath,
+ subpath,
+ 'index.html'
+ )
else:
- return True
-
- async def render_feeds(self):
- await self.render_rss();
- await self.render_atom();
-
- async def render_rss(self):
- await self.render_feed('rss')
-
- async def render_atom(self):
- await self.render_feed('atom')
+ return os.path.join(
+ self.dpath,
+ 'index.html'
+ )
async def render_feed(self, xmlformat):
- settings.logger.info(
+ logger.info(
'rendering category "%s" %s feed',
self.name,
xmlformat
@@ -1338,12 +1364,8 @@ class Category(dict):
start = 0
end = int(settings.site.get('pagination'))
- dirname = os.path.join(self.renderdir, 'feed')
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
-
fg = FeedGenerator()
- fg.id(self.feed)
+ fg.id(self.feedurl)
fg.title(self.title)
fg.author({
'name': settings.author.get('name'),
@@ -1401,74 +1423,74 @@ class Category(dict):
fe.summary(post.get('summary'))
if xmlformat == 'rss':
- fg.link(href=self.feed)
- feedfile = os.path.join(dirname, 'index.xml')
+ fg.link(href=self.feedurl)
+ writepath(self.rssfeedfpath, '%s' % fg.rss_str(pretty=True))
elif xmlformat == 'atom':
- fg.link(href=self.feed, rel='self')
+ fg.link(href=self.feedurl, rel='self')
fg.link(href=settings.meta.get('hub'), rel='hub')
-
- feedfile = os.path.join(dirname, 'atom.xml')
-
- with open(feedfile, 'wb') as f:
- settings.logger.info('writing file: %s', feedfile)
- if xmlformat == 'rss':
- f.write(fg.rss_str(pretty=True))
- elif xmlformat == 'atom':
- f.write(fg.atom_str(pretty=True))
+ writepath(self.atomfeedfpath, '%s' % fg.atom_str(pretty=True))
async def render_flat(self):
r = J2.get_template(self.template).render(
- self.tmplvars([self[k].tmplvars for k in self.sortedkeys])
+ self.tmplvars(self.get_posts())
+ #[self[k].tmplvars for k in self.sortedkeys]
)
+ writepath(self.indexfpath(), r)
- renderfile = os.path.join(self.renderdir, 'index.html')
- with open(renderfile, 'wt') as f:
- f.write(r)
+ def is_uptodate(self, fpath, ts):
+ if not os.path.exists(fpath):
+ return False
+ if os.path.getmtime(fpath) >= ts:
+ return True
+ return False
-
- #async def render_page(self, tmplvars):
-
-
- # async def render_page(self, pagenum=1, pages=1):
- # if self.display == 'flat':
- # start = 0
- # end = -1
- # else:
- # pagination = int(settings.site.get('pagination'))
- # start = int((pagenum - 1) * pagination)
- # end = int(start + pagination)
-
- # posts = self.get_posts(start, end)
- # r = J2.get_template(self.template).render({
- # 'site': settings.site,
- # 'author': settings.author,
- # 'meta': settings.meta,
- # 'licence': settings.licence,
- # 'tips': settings.tips,
- # 'category': self.tmplvars,
- # 'pages': {
- # 'current': pagenum,
- # 'total': pages,
- # },
- # 'posts': posts,
- # })
- # if pagenum > 1:
- # renderdir = os.path.join(self.renderdir, 'page', str(pagenum))
- # else:
- # renderdir = self.renderdir
- # if not os.path.isdir(renderdir):
- # os.makedirs(renderdir)
- # renderfile = os.path.join(renderdir, 'index.html')
- # with open(renderfile, 'wt') as f:
- # f.write(r)
+ def newest(self, start=0, end=-1):
+ if start == end:
+ end = -1
+ s = sorted(
+ [self[k].mtime for k in self.sortedkeys[start:end]],
+ reverse=True
+ )
+ return s[0]
async def render(self):
- if self.exists:
- return
+ newest = self.newest()
+ if not self.is_uptodate(self.rssfeedfpath, newest):
+ logger.info(
+ '%s RSS feed outdated, generating new',
+ self.name
+ )
+ await self.render_feed('rss')
+ else:
+ logger.info(
+ '%s RSS feed up to date',
+ self.name
+ )
+
+ if not self.is_uptodate(self.atomfeedfpath, newest):
+ logger.info(
+ '%s ATOM feed outdated, generating new',
+ self.name
+ )
+ await self.render_feed('atom')
+ else:
+ logger.info(
+ '%s ATOM feed up to date',
+ self.name
+ )
- await self.render_feeds()
if self.display == 'flat':
- await self.render_flat()
+ if not self.is_uptodate(self.indexfpath(), newest):
+ logger.info(
+ '%s flat index outdated, generating new',
+ self.name
+ )
+ await self.render_flat()
+ else:
+ logger.info(
+ '%s flat index is up to date',
+ self.name
+ )
return
by_time = {}
@@ -1482,32 +1504,43 @@ class Category(dict):
keys = list(by_time.keys())
for p, c, n in zip([None]+keys[:-1], keys, keys[1:]+[None]):
- if arrow.utcnow().format(self.trange) == c.format(self.trange):
- renderdir = self.renderdir
+ form = c.format(self.trange)
+ if arrow.utcnow().format(self.trange) == form:
+ fpath = self.indexfpath()
else:
- renderdir = os.path.join(
- self.renderdir,
- c.format(self.trange)
- )
- #
- if not os.path.isdir(renderdir):
- os.makedirs(renderdir)
- renderfile = os.path.join(
- renderdir,
- 'index.html'
- )
+ fpath = self.indexfpath(form)
- r = J2.get_template(self.template).render(
- self.tmplvars(
- [self[k].tmplvars for k in by_time[c]],
- c=c,
- p=p,
- n=n
+ try:
+ findex = self.sortedkeys.index(by_time[c][0])
+ lindex = self.sortedkeys.index(by_time[c][-1])
+ newest = self.newest(findex, lindex)
+ except Exception as e:
+ #logger.info('newest called with start: %s, end: %s', start, end)
+ logger.error('calling newest failed with %s for %s', self.name, c)
+ continue
+
+ if self.is_uptodate(fpath, newest):
+ logger.info(
+ '%s/%s index is up to date',
+ self.name,
+ form
)
- )
- with open(renderfile, 'wt') as f:
- settings.logger.info('writing category archive to: %s', renderfile)
- f.write(r)
+ continue
+ else:
+ logger.info(
+ '%s/%s index is outdated, generating new',
+ self.name,
+ form
+ )
+ r = J2.get_template(self.template).render(
+ self.tmplvars(
+ [self[k].tmplvars for k in by_time[c]],
+ c=c,
+ p=p,
+ n=n
+ )
+ )
+ writepath(fpath, r)
class Sitemap(dict):
@property
@@ -1539,13 +1572,13 @@ def mkcomment(webmention):
fdir = glob.glob(os.path.join(settings.paths.get('content'), '*', slug))
if not len(fdir):
- settings.logger.error(
+ logger.error(
"couldn't find post for incoming webmention: %s",
webmention
)
return
elif len(fdir) > 1:
- settings.logger.error(
+ logger.error(
"multiple posts found for incoming webmention: %s",
webmention
)
@@ -1574,7 +1607,7 @@ def mkcomment(webmention):
else:
fm.content = c
with open(fpath, 'wt') as f:
- settings.logger.info("saving webmention to %s", fpath)
+ logger.info("saving webmention to %s", fpath)
f.write(frontmatter.dumps(fm))
@@ -1596,7 +1629,7 @@ def makecomments():
}
wio_url = "https://webmention.io/api/mentions"
webmentions = requests.get(wio_url, params=wio_params)
- settings.logger.info("queried webmention.io with: %s", webmentions.url)
+ logger.info("queried webmention.io with: %s", webmentions.url)
if webmentions.status_code != requests.codes.ok:
return
try:
@@ -1604,7 +1637,7 @@ def makecomments():
for webmention in mentions.get('links'):
mkcomment(webmention)
except ValueError as e:
- settings.logger.error('failed to query webmention.io: %s', e)
+ logger.error('failed to query webmention.io: %s', e)
pass
@@ -1620,7 +1653,10 @@ def make():
start = int(round(time.time() * 1000))
last = 0
- makecomments()
+ try:
+ makecomments()
+ except Exception as e:
+ logger.error('failed to make comments - are we offline?')
content = settings.paths.get('content')
worker = AsyncWorker()
@@ -1688,7 +1724,7 @@ def make():
worker.add(category.render())
worker.run()
- settings.logger.info('worker finished')
+ logger.info('worker finished')
# copy static
staticfiles = []
@@ -1709,21 +1745,24 @@ def make():
cp(e, t)
end = int(round(time.time() * 1000))
- settings.logger.info('process took %d ms' % (end - start))
+ logger.info('process took %d ms' % (end - start))
if not settings.args.get('nosync'):
- settings.logger.info('starting syncing')
+ logger.info('starting syncing')
os.system(
"rsync -avuhH --delete-after %s/ %s/" % (
settings.paths.get('build'),
settings.syncserver
)
)
- settings.logger.info('syncing finished')
+ logger.info('syncing finished')
- settings.logger.info('sending webmentions')
- webmentions.run()
- settings.logger.info('sending webmentions finished')
+ logger.info('sending webmentions')
+ try:
+ webmentions.run()
+ except Exception as e:
+ logger.error('failed to send webmentions - are we offline?')
+ logger.info('sending webmentions finished')
if __name__ == '__main__':
diff --git a/settings.py b/settings.py
index ec5ebb7..90c8327 100644
--- a/settings.py
+++ b/settings.py
@@ -118,8 +118,10 @@ args = vars(_parser.parse_args())
loglevel = loglevels.get(args.get('loglevel'))
-logger = logging.getLogger("nasg")
+logger = logging.getLogger('NASG')
logger.setLevel(loglevel)
console_handler = logging.StreamHandler()
+formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logging.getLogger('asyncio').setLevel(loglevel)
diff --git a/templates/Category.j2.html b/templates/Category.j2.html
index a5c73e6..2559bb2 100644
--- a/templates/Category.j2.html
+++ b/templates/Category.j2.html
@@ -100,17 +100,32 @@
{% if category.previous %}
-
- « {{ category.previous.label }}
+
+ «
+ {{ category.previous.label }}
+ {% if category.currentyear != category.previous.label %}
+ Jan - Dec
+ {% endif %}
+
{% endif %}
-
- {{ category.current }}
+
+ {{ category.current }}
+ {% if category.currentyear != category.current %}
+ Jan - Dec
+ {% endif %}
+
{% if category.next %}
-
- {{ category.next.label }} »
+
+ {{ category.next.label }}
+ Jan - Dec
+ »
+
{% endif %}
diff --git a/templates/style.css b/templates/style.css
index 9947521..87114b1 100644
--- a/templates/style.css
+++ b/templates/style.css
@@ -276,7 +276,10 @@ body > nav ul {
body > nav li {
margin: 1em 0.6em;
- font-size: 1.6em;
+}
+
+body > nav i {
+ font-style:normal;
}
body > nav .current {