Skip to content

Commit dd02d68

Browse files
committedJul 13, 2016
Fix #1889 -- support dates in post_list
Signed-off-by: Chris Warrick <kwpolska@gmail.com>
1 parent 451c51f commit dd02d68

File tree

5 files changed

+120
-4
lines changed

5 files changed

+120
-4
lines changed
 

‎CHANGES.txt

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ New in master
44
Features
55
--------
66

7+
* Support ``date`` filtering in the post list directive
8+
(Issue #1889)
79
* Added Albanian translation by Vango Stavro
810
* Added ``post-(type)`` class to ``story.tmpl`` (uses the ``type``
911
meta field, defaults to ``post-text`` — same behavior as posts)

‎docs/manual.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,15 @@ The following options are recognized:
19641964
Sort post list by one of each post's attributes, usually ``title`` or a
19651965
custom ``priority``. Defaults to None (chronological sorting).
19661966

1967+
* ``date``: string
1968+
Show posts that match date range specified by this option. Format:
1969+
1970+
* comma-separated clauses (AND)
1971+
* clause: attribute comparison_operator value (spaces optional)
1972+
* attribute: year, month, day, hour, month, second, weekday, isoweekday; or empty for full datetime
1973+
* comparison_operator: == != <= >= < >
1974+
* value: integer or dateutil-compatible date input
1975+
19671976
* ``tags`` : string [, string...]
19681977
Filter posts to show only posts having at least one of the ``tags``.
19691978
Defaults to None.

‎nikola/packages/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ We ship some third-party things with Nikola. They live here, along with their l
33
Packages:
44

55
* tzlocal by Lennart Regebro, CC0 license (modified)
6+
* datecond by Chris Warrick (Nikola contributor), 3-clause BSD license
7+
(modified)

‎nikola/packages/datecond/__init__.py

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
# Date Conditionals (datecond)
4+
# Version 0.1.2
5+
# Copyright © 2015-2016, Chris Warrick.
6+
# All rights reserved.
7+
#
8+
# Redistribution and use in source and binary forms, with or without
9+
# modification, are permitted provided that the following conditions are
10+
# met:
11+
#
12+
# 1. Redistributions of source code must retain the above copyright
13+
# notice, this list of conditions, and the following disclaimer.
14+
#
15+
# 2. Redistributions in binary form must reproduce the above copyright
16+
# notice, this list of conditions, and the following disclaimer in the
17+
# documentation and/or other materials provided with the distribution.
18+
#
19+
# 3. Neither the name of the author of this software nor the names of
20+
# contributors to this software may be used to endorse or promote
21+
# products derived from this software without specific prior written
22+
# consent.
23+
#
24+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35+
36+
"""Date range parser."""
37+
38+
from __future__ import print_function, unicode_literals
39+
import dateutil.parser
40+
import re
41+
import operator
42+
43+
44+
__all__ = ('date_in_range',)
45+
CLAUSE = re.compile('(year|month|day|hour|minute|second|weekday|isoweekday)?'
46+
' ?(==|!=|<=|>=|<|>) ?(.*)')
47+
OPERATORS = {
48+
'==': operator.eq,
49+
'!=': operator.ne,
50+
'<=': operator.le,
51+
'>=': operator.ge,
52+
'<': operator.lt,
53+
'>': operator.gt,
54+
}
55+
56+
57+
def date_in_range(date_range, date, debug=True):
58+
"""Check if date is in the range specified.
59+
60+
Format:
61+
* comma-separated clauses (AND)
62+
* clause: attribute comparison_operator value (spaces optional)
63+
* attribute: year, month, day, hour, month, second, weekday, isoweekday
64+
or empty for full datetime
65+
* comparison_operator: == != <= >= < >
66+
* value: integer or dateutil-compatible date input
67+
"""
68+
out = True
69+
70+
for item in date_range.split(','):
71+
attribute, comparison_operator, value = CLAUSE.match(
72+
item.strip()).groups()
73+
if attribute in ('weekday', 'isoweekday'):
74+
left = getattr(date, attribute)()
75+
right = int(value)
76+
elif attribute:
77+
left = getattr(date, attribute)
78+
right = int(value)
79+
else:
80+
left = date
81+
right = dateutil.parser.parse(value)
82+
if debug: # pragma: no cover
83+
print(" <{0} {1} {2}>".format(left, comparison_operator, right))
84+
out = out and OPERATORS[comparison_operator](left, right)
85+
return out

‎nikola/plugins/compile/rest/post_list.py

+22-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
from nikola import utils
3939
from nikola.plugin_categories import RestExtension
40+
from nikola.packages.datecond import date_in_range
4041

4142
# WARNING: the directive name is post-list
4243
# (with a DASH instead of an UNDERSCORE)
@@ -86,10 +87,19 @@ class PostList(Directive):
8687
Reverse the order of the post-list.
8788
Defaults is to not reverse the order of posts.
8889
89-
``sort``: string
90+
``sort`` : string
9091
Sort post list by one of each post's attributes, usually ``title`` or a
9192
custom ``priority``. Defaults to None (chronological sorting).
9293
94+
``date`` : string
95+
Show posts that match date range specified by this option. Format:
96+
97+
* comma-separated clauses (AND)
98+
* clause: attribute comparison_operator value (spaces optional)
99+
* attribute: year, month, day, hour, month, second, weekday, isoweekday; or empty for full datetime
100+
* comparison_operator: == != <= >= < >
101+
* value: integer or dateutil-compatible date input
102+
93103
``tags`` : string [, string...]
94104
Filter posts to show only posts having at least one of the ``tags``.
95105
Defaults to None.
@@ -136,6 +146,7 @@ class PostList(Directive):
136146
'lang': directives.unchanged,
137147
'template': directives.path,
138148
'id': directives.unchanged,
149+
'date': directives.unchanged,
139150
}
140151

141152
def run(self):
@@ -154,17 +165,21 @@ def run(self):
154165
lang = self.options.get('lang', utils.LocaleBorg().current_lang)
155166
template = self.options.get('template', 'post_list_directive.tmpl')
156167
sort = self.options.get('sort')
168+
date = self.options.get('date')
157169

158170
output = _do_post_list(start, stop, reverse, tags, categories, slugs, post_type,
159-
show_all, lang, template, sort, state=self.state, site=self.site)
171+
show_all, lang, template, sort, state=self.state, site=self.site, date=date)
160172
self.state.document.settings.record_dependencies.add("####MAGIC####TIMELINE")
161-
return [nodes.raw('', output, format='html')]
173+
if output:
174+
return [nodes.raw('', output, format='html')]
175+
else:
176+
return []
162177

163178

164179
def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=None,
165180
slugs=None, post_type='post', show_all=False, lang=None,
166181
template='post_list_directive.tmpl', sort=None, id=None,
167-
data=None, state=None, site=None):
182+
data=None, state=None, site=None, date=None):
168183
if lang is None:
169184
lang = utils.LocaleBorg().current_lang
170185
if site.invariant: # for testing purposes
@@ -213,6 +228,9 @@ def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=No
213228
if sort:
214229
filtered_timeline = natsort.natsorted(filtered_timeline, key=lambda post: post.meta[lang][sort], alg=natsort.ns.F | natsort.ns.IC)
215230

231+
if date:
232+
filtered_timeline = [p for p in filtered_timeline if date_in_range(date, p.date)]
233+
216234
for post in filtered_timeline[start:stop:step]:
217235
if slugs:
218236
cont = True

0 commit comments

Comments
 (0)
Please sign in to comment.