Skip to content

Commit

Permalink
Added support for archives per day (ADD_DAY_ARCHIVES).
Browse files Browse the repository at this point in the history
Added support to create full archives (with list of all posts), which includes archive.html, per-year archives, per-month archives, and per-day archives at once (CREATE_FULL_ARCHIVES).
  • Loading branch information
felixfontein committed Nov 17, 2014
1 parent 9edc6ec commit f958d35
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 86 deletions.
1 change: 1 addition & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dhruvbaldawa <https://github.com/dhruvbaldawa>
dmoisset <https://github.com/dmoisset>
edwinsteele <https://github.com/edwinsteele>
ermeaney <https://github.com/ermeney>
Felix Fontein <https://github.com/felixfontein>
fisadev <https://github.com/fisadev>
fizyk <https://github.com/fizyk>
ivanov <https://github.com/ivanov>
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Features
* Add `robots` meta tag with value `noindex` for drafts (Issue #1489)
* New option TAGLIST_MINIMUM_POSTS allows hiding unpopular tags from
the tag index page (Issue #1484)
* New options CREATE_FULL_ARCHIVES and ADD_DAY_ARCHIVES which allow
to create non-hierarchical archives and archives for days, respectively

Bugfixes
--------
Expand Down
6 changes: 6 additions & 0 deletions nikola/conf.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,16 @@ COMPILERS = ${COMPILERS}
# CREATE_MONTHLY_ARCHIVE = False
# Create one large archive instead of per-year
# CREATE_SINGLE_ARCHIVE = False
# Create year, month, and day archives each with a (long) list of posts
# (overrides both CREATE_MONTHLY_ARCHIVE and CREATE_SINGLE_ARCHIVE)
# CREATE_FULL_ARCHIVES = False
# If monthly archives or full archives are created, adds also one archive per day
# ADD_DAY_ARCHIVES = False
# Final locations for the archives are:
# output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME
# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html
# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / index.html
# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / DAY / index.html
# ARCHIVE_PATH = ""
# ARCHIVE_FILENAME = "archive.html"

Expand Down
2 changes: 2 additions & 0 deletions nikola/nikola.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ def __init__(self, **config):
'COPY_SOURCES': True,
'CREATE_MONTHLY_ARCHIVE': False,
'CREATE_SINGLE_ARCHIVE': False,
'CREATE_FULL_ARCHIVES': False,
'ADD_DAY_ARCHIVES': False,
'DATE_FORMAT': '%Y-%m-%d %H:%M',
'JS_DATE_FORMAT': 'YYYY-MM-DD HH:mm',
'DATE_FANCINESS': 0,
Expand Down
173 changes: 87 additions & 86 deletions nikola/plugins/task/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,48 @@ def set_site(self, site):
site.register_path_handler('archive', self.archive_path)
return super(Archive, self).set_site(site)

def _prepare_task(self, kw, name, lang, post_list, items, template_name,
title, deps_translatable = None):
# name: used to build permalink and destination
# post_list, items: posts or items; only one of them should be used,
# the other be None
# template_name: name of the template to use
# title: the (translated) title for the generated page
# deps_translatable: dependencies (None if not added)
assert post_list != None or items != None

context = {}
context["lang"] = lang
context["title"] = title
context["permalink"] = self.site.link("archive", name, lang)
if post_list != None:
context["posts"] = post_list
n = len(post_list)
else:
context["items"] = items
n = len(items)
task = self.site.generic_post_list_renderer(
lang,
[],
os.path.join(kw['output_folder'],
self.site.path("archive", name, lang)),
template_name,
kw['filters'],
context,
)

task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: n}
if deps_translatable != None:
task_cfg[4] = deps_translatable
task['uptodate'] = [config_changed(task_cfg)]
task['basename'] = self.name
return task

def _generate_postlist_task(self, kw, name, lang, posts, title, deps_translatable = None):
post_list = sorted(posts, key=lambda a: a.date)
post_list.reverse()
yield self._prepare_task(kw, name, lang, post_list, None, "list_post.tmpl", title, deps_translatable)

def gen_tasks(self):
kw = {
"messages": self.site.MESSAGES,
Expand All @@ -49,116 +91,75 @@ def gen_tasks(self):
"filters": self.site.config['FILTERS'],
"create_monthly_archive": self.site.config['CREATE_MONTHLY_ARCHIVE'],
"create_single_archive": self.site.config['CREATE_SINGLE_ARCHIVE'],
"create_full_archives": self.site.config['CREATE_FULL_ARCHIVES'],
"add_day_archives": self.site.config['ADD_DAY_ARCHIVES'],
}
self.site.scan_posts()
yield self.group_task()
# TODO add next/prev links for years
if kw['create_monthly_archive'] and kw['create_single_archive']:
if (kw['create_monthly_archive'] and kw['create_single_archive']) and not kw['create_full_archives']:
raise Exception('Cannot create monthly and single archives at the same time.')
for lang in kw["translations"]:
archdata = self.site.posts_per_year
# A bit of a hack.
if kw['create_single_archive']:
archdata = {None: self.site.posts}
if kw['create_single_archive'] and not kw['create_full_archives']:
# if we are creating one single archive
archdata = { }
else:
# if we are not creating one single archive, start with all years
archdata = dict(self.site.posts_per_year) # create a copy
if kw['create_single_archive'] or kw['create_full_archives']:
# if we are creating one single archive, or full archives
archdata[None] = self.site.posts # for create_single_archive
template_name = "list_post.tmpl"

for year, posts in archdata.items():
output_name = os.path.join(
kw['output_folder'], self.site.path("archive", year, lang))
context = {}
context["lang"] = lang
# Add archive per year or total archive
if year:
context["title"] = kw["messages"][lang]["Posts for year %s"] % year
title = kw["messages"][lang]["Posts for year %s"] % year
else:
context["title"] = kw["messages"][lang]["Archive"]
context["permalink"] = self.site.link("archive", year, lang)
if not kw["create_monthly_archive"]:
template_name = "list_post.tmpl"
post_list = sorted(posts, key=lambda a: a.date)
post_list.reverse()
context["posts"] = post_list
else: # Monthly archives, just list the months
months = set([(m.split('/')[1], self.site.link("archive", m, lang)) for m in self.site.posts_per_month.keys() if m.startswith(str(year))])
months = sorted(list(months))
months.reverse()
template_name = "list.tmpl"
context["items"] = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link] for month, link in months]
post_list = []
task = self.site.generic_post_list_renderer(
lang,
[],
output_name,
template_name,
kw['filters'],
context,
)
n = len(post_list) if 'posts' in context else len(months)

title = kw["messages"][lang]["Archive"]
deps_translatable = {}
for k in self.site._GLOBAL_CONTEXT_TRANSLATABLE:
deps_translatable[k] = self.site.GLOBAL_CONTEXT[k](lang)
if not kw["create_monthly_archive"] or kw["create_full_archives"]:
yield self._generate_postlist_task(kw, year, lang, posts, title, deps_translatable)
else:
months = set([(m.split('/')[1], self.site.link("archive", m, lang)) for m in self.site.posts_per_month.keys() if m.startswith(str(year))])
months = sorted(list(months))
months.reverse()
items = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link] for month, link in months]
yield self._prepare_task(kw, year, lang, None, items, "list.tmpl", title, deps_translatable)

task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: n, 4: deps_translatable}
task['uptodate'] = [config_changed(task_cfg)]
task['basename'] = self.name
yield task

if not kw["create_monthly_archive"]:
if not kw["create_monthly_archive"] and not kw["create_full_archives"]:
continue # Just to avoid nesting the other loop in this if
template_name = "list_post.tmpl"
for yearmonth, posts in self.site.posts_per_month.items():
output_name = os.path.join(
kw['output_folder'], self.site.path("archive", yearmonth,
lang))
# Add archive per month
year, month = yearmonth.split('/')
post_list = sorted(posts, key=lambda a: a.date)
post_list.reverse()
context = {}
context["lang"] = lang
context["posts"] = post_list
context["permalink"] = self.site.link("archive", year, lang)

context["title"] = kw["messages"][lang]["Posts for {month} {year}"].format(
title = kw["messages"][lang]["Posts for {month} {year}"].format(
year=year, month=nikola.utils.LocaleBorg().get_month_name(int(month), lang))
task = self.site.generic_post_list_renderer(
lang,
post_list,
output_name,
template_name,
kw['filters'],
context,
)
task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: len(post_list)}
task['uptodate'] = [config_changed(task_cfg)]
task['basename'] = self.name
yield task

if not kw['create_single_archive']:
yield self._generate_postlist_task(kw, yearmonth, lang, posts, title)

if not kw["create_full_archives"] and not kw["add_day_archives"]:
continue # Just to avoid nesting the other loop in this if
# Add archive per day
days = dict()
for p in posts:
if p.date.day not in days:
days[p.date.day] = list()
days[p.date.day].append(p)
for day, posts in days.items():
title = kw["messages"][lang]["Posts for {month} {day}, {year}"].format(
year=year, month=nikola.utils.LocaleBorg().get_month_name(int(month), lang), day=day)
yield self._generate_postlist_task(kw, yearmonth + '/{0:02d}'.format(day), lang, posts, title)

if not kw['create_single_archive'] and not kw['create_full_archives']:
# And an "all your years" page for yearly and monthly archives
years = list(self.site.posts_per_year.keys())
years.sort(reverse=True)
template_name = "list.tmpl"
kw['years'] = years
for lang in kw["translations"]:
context = {}
output_name = os.path.join(
kw['output_folder'], self.site.path("archive", None,
lang))
context["title"] = kw["messages"][lang]["Archive"]
context["items"] = [(y, self.site.link("archive", y, lang))
items = [(y, self.site.link("archive", y, lang))
for y in years]
context["permalink"] = self.site.link("archive", None, lang)
task = self.site.generic_post_list_renderer(
lang,
[],
output_name,
template_name,
kw['filters'],
context,
)
task_cfg = {1: task['uptodate'][0].config, 2: kw, 3: len(years)}
task['uptodate'] = [config_changed(task_cfg)]
task['basename'] = self.name
yield task
yield self._prepare_task(kw, None, lang, None, items, "list.tmpl", kw["messages"][lang]["Archive"])

def archive_path(self, name, lang):
if name:
Expand Down
20 changes: 20 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,26 @@ def test_monthly_archive(self):
self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'target', 'output', '2012', '03', 'index.html')))


class DayArchiveTest(DemoBuildTest):
"""Check that per-day archives build and are correct."""

@classmethod
def patch_site(self):
"""Set the SITE_URL to have a path"""
conf_path = os.path.join(self.target_dir, "conf.py")
with io.open(conf_path, "r", encoding="utf-8") as inf:
data = inf.read()
data = data.replace('# ADD_DAY_ARCHIVES = False',
'ADD_DAY_ARCHIVES = True')
with io.open(conf_path, "w+", encoding="utf8") as outf:
outf.write(data)
outf.flush()

def test_day_archive(self):
"""See that it builds"""
self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'target', 'output', '2012', '03', '30', 'index.html')))


class SubdirRunningTest(DemoBuildTest):
"""Check that running nikola from subdir works."""

Expand Down

0 comments on commit f958d35

Please sign in to comment.