1
0
Fork 0
forked from github/pelican

Implement period_archives common context variable

Also, set default patterns for time-period *_ARCHIVE_URL settings.
This commit is contained in:
DJ Ramones 2023-06-18 11:07:39 +08:00
commit 5214248344
No known key found for this signature in database
GPG key ID: D0EB42161D927EB0
5 changed files with 256 additions and 70 deletions

View file

@ -295,6 +295,7 @@ class ArticlesGenerator(CachingGenerator):
self.drafts = [] # only drafts in default language
self.drafts_translations = []
self.dates = {}
self.period_archives = defaultdict(list)
self.tags = defaultdict(list)
self.categories = defaultdict(list)
self.related_posts = []
@ -483,64 +484,17 @@ class ArticlesGenerator(CachingGenerator):
except PelicanTemplateNotFound:
template = self.get_template('archives')
period_save_as = {
'year': self.settings['YEAR_ARCHIVE_SAVE_AS'],
'month': self.settings['MONTH_ARCHIVE_SAVE_AS'],
'day': self.settings['DAY_ARCHIVE_SAVE_AS'],
}
for granularity in list(self.period_archives.keys()):
for period in self.period_archives[granularity]:
period_url = {
'year': self.settings['YEAR_ARCHIVE_URL'],
'month': self.settings['MONTH_ARCHIVE_URL'],
'day': self.settings['DAY_ARCHIVE_URL'],
}
period_date_key = {
'year': attrgetter('date.year'),
'month': attrgetter('date.year', 'date.month'),
'day': attrgetter('date.year', 'date.month', 'date.day')
}
def _generate_period_archives(dates, key, save_as_fmt, url_fmt):
"""Generate period archives from `dates`, grouped by
`key` and written to `save_as`.
"""
# `dates` is already sorted by date
for _period, group in groupby(dates, key=key):
archive = list(group)
articles = [a for a in self.articles if a in archive]
# arbitrarily grab the first date so that the usual
# format string syntax can be used for specifying the
# period archive dates
date = archive[0].date
save_as = save_as_fmt.format(date=date)
url = url_fmt.format(date=date)
context = self.context.copy()
context['period'] = period['period']
context['period_num'] = period['period_num']
if key == period_date_key['year']:
context["period"] = (_period,)
context["period_num"] = (_period,)
else:
month_name = calendar.month_name[_period[1]]
if key == period_date_key['month']:
context["period"] = (_period[0],
month_name)
else:
context["period"] = (_period[0],
month_name,
_period[2])
context["period_num"] = tuple(_period)
write(save_as, template, context, articles=articles,
dates=archive, template_name='period_archives',
blog=True, url=url, all_articles=self.articles)
for period in 'year', 'month', 'day':
save_as = period_save_as[period]
url = period_url[period]
if save_as:
key = period_date_key[period]
_generate_period_archives(self.dates, key, save_as, url)
write(period['save_as'], template, context,
articles=period['articles'], dates=period['dates'],
template_name='period_archives', blog=True,
url=period['url'], all_articles=self.articles)
def generate_direct_templates(self, write):
"""Generate direct templates pages"""
@ -680,6 +634,74 @@ class ArticlesGenerator(CachingGenerator):
self.dates.sort(key=attrgetter('date'),
reverse=self.context['NEWEST_FIRST_ARCHIVES'])
def _build_period_archives(sorted_articles):
period_archives = defaultdict(list)
period_archives_settings = {
'year': {
'save_as': self.settings['YEAR_ARCHIVE_SAVE_AS'],
'url': self.settings['YEAR_ARCHIVE_URL'],
},
'month': {
'save_as': self.settings['MONTH_ARCHIVE_SAVE_AS'],
'url': self.settings['MONTH_ARCHIVE_URL'],
},
'day': {
'save_as': self.settings['DAY_ARCHIVE_SAVE_AS'],
'url': self.settings['DAY_ARCHIVE_URL'],
},
}
granularity_key_func = {
'year': attrgetter('date.year'),
'month': attrgetter('date.year', 'date.month'),
'day': attrgetter('date.year', 'date.month', 'date.day'),
}
for granularity in 'year', 'month', 'day':
save_as_fmt = period_archives_settings[granularity]['save_as']
url_fmt = period_archives_settings[granularity]['url']
key_func = granularity_key_func[granularity]
if not save_as_fmt:
# the archives for this period granularity are not needed
continue
for period, group in groupby(sorted_articles, key=key_func):
period_archive = {}
dates = list(group)
period_archive['dates'] = dates
period_archive['articles'] = [
a for a in self.articles if a in dates
]
# use the first date to specify the period archive URL
# and save_as; the specific date used does not matter as
# they all belong to the same period
d = dates[0].date
period_archive['save_as'] = save_as_fmt.format(date=d)
period_archive['url'] = url_fmt.format(date=d)
if granularity == 'year':
period_archive['period'] = (period,)
period_archive['period_num'] = (period,)
else:
month_name = calendar.month_name[period[1]]
if granularity == 'month':
period_archive['period'] = (period[0], month_name)
else:
period_archive['period'] = (period[0],
month_name,
period[2])
period_archive['period_num'] = tuple(period)
period_archives[granularity].append(period_archive)
return period_archives
self.period_archives = _build_period_archives(self.dates)
# and generate the output :)
# order the categories per name
@ -694,6 +716,9 @@ class ArticlesGenerator(CachingGenerator):
'articles', 'drafts', 'hidden_articles',
'dates', 'tags', 'categories',
'authors', 'related_posts'))
# _update_context flattens dicts, which should not happen to
# period_archives, so we update the context directly for it:
self.context['period_archives'] = self.period_archives
self.save_cache()
self.readers.save_cache()
signals.article_generator_finalized.send(self)

View file

@ -90,11 +90,11 @@ DEFAULT_CONFIG = {
(1, '{name}{extension}', '{name}{extension}'),
(2, '{name}{number}{extension}', '{name}{number}{extension}'),
],
'YEAR_ARCHIVE_URL': '',
'YEAR_ARCHIVE_URL': 'posts/{date:%Y}/',
'YEAR_ARCHIVE_SAVE_AS': '',
'MONTH_ARCHIVE_URL': '',
'MONTH_ARCHIVE_URL': 'posts/{date:%Y}/{date:%b}/',
'MONTH_ARCHIVE_SAVE_AS': '',
'DAY_ARCHIVE_URL': '',
'DAY_ARCHIVE_URL': 'posts/{date:%Y}/{date:%b}/{date:%d}/',
'DAY_ARCHIVE_SAVE_AS': '',
'RELATIVE_URLS': False,
'DEFAULT_LANG': 'en',

View file

@ -405,6 +405,103 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertIn(custom_template, self.articles)
self.assertIn(standard_template, self.articles)
def test_period_archives_context(self):
"""Test correctness of the period_archives context values."""
old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, 'C')
settings = get_settings()
settings['CACHE_PATH'] = self.temp_cache
# No period archives enabled:
context = get_context(settings)
generator = ArticlesGenerator(
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
period_archives = generator.context['period_archives']
self.assertEqual(len(period_archives.items()), 0)
# Year archives enabled:
settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
context = get_context(settings)
generator = ArticlesGenerator(
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
period_archives = generator.context['period_archives']
self.assertEqual(len(period_archives.items()), 1)
self.assertIn('year', period_archives.keys())
archive_years = [p['period'][0] for p in period_archives['year']]
self.assertIn(1970, archive_years)
self.assertIn(2014, archive_years)
# Month archives enabled:
settings['MONTH_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/index.html'
settings['MONTH_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/'
context = get_context(settings)
generator = ArticlesGenerator(
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
period_archives = generator.context['period_archives']
self.assertEqual(len(period_archives.items()), 2)
self.assertIn('month', period_archives.keys())
month_archives_tuples = [p['period'] for p in period_archives['month']]
self.assertIn((1970, 'January'), month_archives_tuples)
self.assertIn((2014, 'February'), month_archives_tuples)
# Day archives enabled:
settings['DAY_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
settings['DAY_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/'
context = get_context(settings)
generator = ArticlesGenerator(
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
period_archives = generator.context['period_archives']
self.assertEqual(len(period_archives.items()), 3)
self.assertIn('day', period_archives.keys())
day_archives_tuples = [p['period'] for p in period_archives['day']]
self.assertIn((1970, 'January', 1), day_archives_tuples)
self.assertIn((2014, 'February', 9), day_archives_tuples)
# Further item values tests
filtered_archives = [
p for p in period_archives['day']
if p['period'] == (2014, 'February', 9)
]
self.assertEqual(len(filtered_archives), 1)
sample_archive = filtered_archives[0]
self.assertEqual(sample_archive['period_num'], (2014, 2, 9))
self.assertEqual(
sample_archive['save_as'], 'posts/2014/Feb/09/index.html')
self.assertEqual(
sample_archive['url'], 'posts/2014/Feb/09/')
articles = [
d for d in generator.articles if
d.date.year == 2014 and
d.date.month == 2 and
d.date.day == 9
]
self.assertEqual(len(sample_archive['articles']), len(articles))
dates = [
d for d in generator.dates if
d.date.year == 2014 and
d.date.month == 2 and
d.date.day == 9
]
self.assertEqual(len(sample_archive['dates']), len(dates))
self.assertEqual(sample_archive['dates'][0].title, dates[0].title)
self.assertEqual(sample_archive['dates'][0].date, dates[0].date)
locale.setlocale(locale.LC_ALL, old_locale)
def test_period_in_timeperiod_archive(self):
"""
Test that the context of a generated period_archive is passed