0001-templatetags-add-date-filters-36943.patch
combo/public/templatetags/combo.py | ||
---|---|---|
27 | 27 |
from django.template import VariableDoesNotExist |
28 | 28 |
from django.template.base import TOKEN_BLOCK, TOKEN_VAR, TOKEN_COMMENT |
29 | 29 |
from django.template.defaultfilters import stringfilter |
30 |
from django.utils import dateparse |
|
30 |
from django.utils import dateparse, six
|
|
31 | 31 |
from django.utils.encoding import force_text |
32 | 32 | |
33 | 33 |
from combo.data.models import Page, Placeholder |
... | ... | |
269 | 269 |
@register.filter |
270 | 270 |
def startswith(string, substring): |
271 | 271 |
return string and force_text(string).startswith(force_text(substring)) |
272 | ||
273 |
def parse_float(value): |
|
274 |
if isinstance(value, six.string_types): |
|
275 |
# replace , by . for French users comfort |
|
276 |
value = value.replace(',', '.') |
|
277 |
try: |
|
278 |
return float(value) |
|
279 |
except (ValueError, TypeError): |
|
280 |
return '' |
|
281 | ||
282 |
def get_as_datetime(s): |
|
283 |
result = parse_datetime(s) |
|
284 |
if not result: |
|
285 |
result = parse_date(s) |
|
286 |
if result: |
|
287 |
result = datetime.datetime(year=result.year, month=result.month, day=result.day) |
|
288 |
return result |
|
289 | ||
290 |
def make_date(date_var): |
|
291 |
'''Extract a date from a datetime, a date, a struct_time or a string''' |
|
292 |
if isinstance(date_var, datetime.datetime): |
|
293 |
return date_var.date() |
|
294 |
if isinstance(date_var, datetime.date): |
|
295 |
return date_var |
|
296 |
if isinstance(date_var, time.struct_time) or ( |
|
297 |
isinstance(date_var, tuple) and len(date_var) == 9): |
|
298 |
return datetime.date(*date_var[:3]) |
|
299 |
try: |
|
300 |
return get_as_datetime(date_var).date() |
|
301 |
except AttributeError: |
|
302 |
return '' |
|
303 | ||
304 |
def make_datetime(datetime_var): |
|
305 |
'''Extract a date from a datetime, a date, a struct_time or a string''' |
|
306 |
if isinstance(datetime_var, datetime.datetime): |
|
307 |
return datetime_var |
|
308 |
if isinstance(datetime_var, datetime.date): |
|
309 |
return datetime.datetime(year=datetime_var.year, month=datetime_var.month, |
|
310 |
day=datetime_var.day) |
|
311 |
if isinstance(datetime_var, time.struct_time) or ( |
|
312 |
isinstance(datetime_var, tuple) and len(datetime_var) == 9): |
|
313 |
return datetime.datetime(*datetime_var[:6]) |
|
314 |
return get_as_datetime(datetime_var) |
|
315 | ||
316 |
@register.filter(expects_localtime=True, is_safe=False) |
|
317 |
def add_days(value, arg): |
|
318 |
value = make_date(value) # consider only date, not hours |
|
319 |
if not value: |
|
320 |
return '' |
|
321 |
arg = parse_float(arg) |
|
322 |
if not arg: |
|
323 |
return value |
|
324 |
result = value + datetime.timedelta(days=float(arg)) |
|
325 |
return result |
|
326 | ||
327 |
@register.filter(expects_localtime=True, is_safe=False) |
|
328 |
def add_hours(value, arg): |
|
329 |
value = make_datetime(value) |
|
330 |
if not value: |
|
331 |
return '' |
|
332 |
arg = parse_float(arg) |
|
333 |
if not arg: |
|
334 |
return value |
|
335 |
return value + datetime.timedelta(hours=float(arg)) |
|
336 | ||
337 |
@register.filter(expects_localtime=True, is_safe=False) |
|
338 |
def age_in_days(value, today=None): |
|
339 |
value = make_date(value) |
|
340 |
if not value: |
|
341 |
return '' |
|
342 |
if today is not None: |
|
343 |
today = make_date(today) |
|
344 |
if not today: |
|
345 |
return '' |
|
346 |
else: |
|
347 |
today = datetime.date.today() |
|
348 |
return (today - value).days |
|
349 | ||
350 |
@register.filter(expects_localtime=True, is_safe=False) |
|
351 |
def age_in_hours(value, now=None): |
|
352 |
# consider value and now as datetimes (and not dates) |
|
353 |
value = make_datetime(value) |
|
354 |
if not value: |
|
355 |
return '' |
|
356 |
if now is not None: |
|
357 |
now = make_datetime(now) |
|
358 |
if not now: |
|
359 |
return '' |
|
360 |
else: |
|
361 |
now = datetime.datetime.now() |
|
362 |
return int((now - value).total_seconds() / 3600) |
|
363 | ||
364 |
def age_in_years_and_months(born, today=None): |
|
365 |
'''Compute age since today as the number of years and months elapsed''' |
|
366 |
born = make_date(born) |
|
367 |
if not born: |
|
368 |
return '' |
|
369 |
if today is not None: |
|
370 |
today = make_date(today) |
|
371 |
if not today: |
|
372 |
return '' |
|
373 |
else: |
|
374 |
today = datetime.date.today() |
|
375 |
before = (today.month, today.day) < (born.month, born.day) |
|
376 |
years = today.year - born.year |
|
377 |
months = today.month - born.month |
|
378 |
if before: |
|
379 |
years -= 1 |
|
380 |
months += 12 |
|
381 |
if today.day < born.day: |
|
382 |
months -= 1 |
|
383 |
return years, months |
|
384 | ||
385 |
@register.filter(expects_localtime=True, is_safe=False) |
|
386 |
def age_in_years(value, today=None): |
|
387 |
try: |
|
388 |
return age_in_years_and_months(value, today)[0] |
|
389 |
except IndexError: |
|
390 |
return '' |
|
391 | ||
392 |
@register.filter(expects_localtime=True, is_safe=False) |
|
393 |
def age_in_months(value, today=None): |
|
394 |
try: |
|
395 |
years, months = age_in_years_and_months(value, today) |
|
396 |
except ValueError: |
|
397 |
return '' |
|
398 |
return years * 12 + months |
tests/test_public_templatetags.py | ||
---|---|---|
1 |
import datetime |
|
1 | 2 |
import os |
2 | 3 |
import shutil |
3 | 4 |
import time |
... | ... | |
201 | 202 |
assert t.render(context) == '' |
202 | 203 |
context = Context({'foo': 'bar'}) |
203 | 204 |
assert t.render(context) == 'ok' |
205 | ||
206 |
def test_date_maths(): |
|
207 |
tmpl = Template('{{ plop|add_days:4 }}') |
|
208 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 25, 2017' |
|
209 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 25, 2017' |
|
210 |
tmpl = Template('{{ plop|add_days:"-1" }}') |
|
211 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 20, 2017' |
|
212 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 20, 2017' |
|
213 |
tmpl = Template('{{ plop|add_days:1.5 }}') |
|
214 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017' |
|
215 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017' |
|
216 |
tmpl = Template('{{ plop|add_days:"1.5" }}') |
|
217 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017' |
|
218 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017' |
|
219 | ||
220 |
tmpl = Template('{{ plop|add_hours:24 }}') |
|
221 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017, midnight' |
|
222 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017, 6 p.m.' |
|
223 |
tmpl = Template('{{ plop|add_hours:"12.5" }}') |
|
224 |
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 21, 2017, 12:30 p.m.' |
|
225 |
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017, 6:30 a.m.' |
|
226 | ||
227 |
def test_age_in(): |
|
228 |
context = { |
|
229 |
'form_var_datefield': time.strptime('2018-07-31', '%Y-%m-%d'), |
|
230 |
'form_var_datefield2': time.strptime('2018-08-31', '%Y-%m-%d'), |
|
231 |
'form_var_datestring': '2018-07-31', |
|
232 |
'today': datetime.date.today(), |
|
233 |
'now': datetime.datetime.now(), |
|
234 |
} |
|
235 |
for condition_value in ( # hope date is > 2018 |
|
236 |
# age_in_days |
|
237 |
'"1970-01-01"|age_in_days > 0', |
|
238 |
'"2500-01-01"|age_in_days < 0', |
|
239 |
'form_var_datefield|age_in_days > 50', |
|
240 |
'form_var_datefield|age_in_days:form_var_datestring == 0', |
|
241 |
'form_var_datefield|age_in_days:form_var_datefield2 == 31', |
|
242 |
'form_var_datefield2|age_in_days:form_var_datefield == -31', |
|
243 |
'form_var_datefield|age_in_days:form_var_datefield == 0', |
|
244 |
'form_var_datestring|age_in_days:form_var_datefield == 0', |
|
245 |
'form_var_datestring|age_in_days:form_var_datestring == 0', |
|
246 |
'today|add_days:-5|age_in_days == 5', |
|
247 |
'today|add_days:5|age_in_days == -5', |
|
248 |
'today|age_in_days == 0', |
|
249 |
# with datetimes |
|
250 |
'"1970-01-01 02:03"|age_in_days > 0', |
|
251 |
'"2500-01-01 02:03"|age_in_days < 0', |
|
252 |
'now|age_in_days == 0', |
|
253 |
'now|add_hours:-24|age_in_days == 1', |
|
254 |
'now|add_hours:24|age_in_days == -1', |
|
255 |
'"2010-11-12 13:14"|age_in_days:"2010-11-12 13:14" == 0', |
|
256 |
'"2010-11-12 13:14"|age_in_days:"2010-11-12 12:14" == 0', |
|
257 |
'"2010-11-12 13:14"|age_in_days:"2010-11-12 14:14" == 0', |
|
258 |
'"2010-11-12 13:14"|age_in_days:"2010-11-13 13:13" == 1', |
|
259 |
'"2010-11-12 13:14"|age_in_days:"2010-11-13 13:15" == 1', |
|
260 | ||
261 |
# age_in_hours |
|
262 |
'now|add_hours:-5|age_in_hours == 5', |
|
263 |
'now|add_hours:25|age_in_hours == -24', |
|
264 |
'now|age_in_hours == 0', |
|
265 |
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 13:14" == 0', |
|
266 |
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 12:14" == -1', |
|
267 |
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 14:14" == 1', |
|
268 |
'"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:13" == 23', |
|
269 |
'"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:15" == 24', |
|
270 |
'"1970-01-01 02:03"|age_in_hours > 0', |
|
271 |
'"2500-01-01 02:03"|age_in_hours < 0', |
|
272 |
# with dates |
|
273 |
'"1970-01-01"|age_in_hours > 0', |
|
274 |
'"2500-01-01"|age_in_hours < 0', |
|
275 |
'form_var_datefield|age_in_hours > 1200', |
|
276 |
'form_var_datefield|age_in_hours:form_var_datestring == 0', |
|
277 |
'form_var_datefield|age_in_hours:form_var_datefield2 == 744', # 31*24 |
|
278 |
'form_var_datefield2|age_in_hours:form_var_datefield == -744', |
|
279 |
'form_var_datefield|age_in_hours:form_var_datefield == 0', |
|
280 |
'form_var_datestring|age_in_hours:form_var_datefield == 0', |
|
281 |
'form_var_datestring|age_in_hours:form_var_datestring == 0', |
|
282 |
'today|add_days:-1|age_in_hours >= 24', |
|
283 |
'today|add_days:1|age_in_hours <= -0', |
|
284 |
'today|add_days:1|age_in_hours >= -24', |
|
285 |
'today|age_in_hours >= 0', |
|
286 | ||
287 |
# age_in_years |
|
288 |
'"1970-01-01"|age_in_years > 0', |
|
289 |
'"2500-01-01"|age_in_years < 0', |
|
290 |
'form_var_datefield|age_in_years:"2019-07-31" == 1', |
|
291 |
'form_var_datefield|age_in_years:"2019-09-20" == 1', |
|
292 |
'form_var_datefield|age_in_years:"2020-07-30" == 1', |
|
293 |
'form_var_datefield|age_in_years:"2020-07-31" == 2', |
|
294 |
'form_var_datestring|age_in_years:"2019-07-31" == 1', |
|
295 |
'today|age_in_years == 0', |
|
296 |
'today|add_days:-500|age_in_years == 1', |
|
297 |
'today|add_days:-300|age_in_years == 0', |
|
298 |
'today|add_days:300|age_in_years == -1', |
|
299 |
'now|age_in_years == 0', |
|
300 |
'now|add_days:-500|age_in_years == 1', |
|
301 |
'now|add_days:-300|age_in_years == 0', |
|
302 |
'now|add_days:300|age_in_years == -1', |
|
303 |
'"1970-01-01 02:03"|age_in_years > 0', |
|
304 |
'"2500-01-01 02:03"|age_in_years < 0', |
|
305 | ||
306 |
# age_in_months |
|
307 |
'form_var_datefield|age_in_months:form_var_datefield2 == 1', |
|
308 |
'form_var_datefield2|age_in_months:form_var_datefield == -1', |
|
309 |
'form_var_datefield|age_in_months:"2019-07-31" == 12', |
|
310 |
'form_var_datefield|age_in_months:"2019-08-20" == 12', |
|
311 |
'form_var_datefield|age_in_months:"2019-09-20" == 13', |
|
312 |
'form_var_datestring|age_in_months:"2019-09-20" == 13', |
|
313 |
'"1970-01-01"|age_in_months > 0', |
|
314 |
'"2500-01-01"|age_in_months < 0', |
|
315 |
'"1970-01-01 02:03"|age_in_months > 0', |
|
316 |
'"2500-01-01 02:03"|age_in_months < 0', |
|
317 | ||
318 |
# fail produce empty string |
|
319 |
'foobar|age_in_days == ""', |
|
320 |
'"foobar"|age_in_days == ""', |
|
321 |
'"1970-01-01"|age_in_days:"foobar" == ""', |
|
322 |
'foobar|age_in_hours == ""', |
|
323 |
'"foobar"|age_in_hours == ""', |
|
324 |
'"1970-01-01"|age_in_hours:"foobar" == ""', |
|
325 |
'foobar|age_in_years == ""', |
|
326 |
'"foobar"|age_in_years == ""', |
|
327 |
'"1970-01-01"|age_in_years:"foobar" == ""', |
|
328 |
'foobar|age_in_months == ""', |
|
329 |
'"foobar"|age_in_months == ""', |
|
330 |
'"1970-01-01"|age_in_months:"foobar" == ""', |
|
331 |
): |
|
332 |
tmpl = Template('{%% if %s %%}Good{%% endif %%}' % condition_value) |
|
333 |
assert tmpl.render(Context(context)) == 'Good' |
|
204 |
- |