##// END OF EJS Templates
lib: move some template filter functions from utils2 to webutils...
Mads Kiilerich -
r8593:3cef2caf default
parent child Browse files
Show More
@@ -42,8 +42,8 b' from kallithea.controllers import base'
42 from kallithea.lib import auth_modules, webutils
42 from kallithea.lib import auth_modules, webutils
43 from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator, LoginRequired
43 from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator, LoginRequired
44 from kallithea.lib.exceptions import DefaultUserException, UserCreationError, UserOwnsReposException
44 from kallithea.lib.exceptions import DefaultUserException, UserCreationError, UserOwnsReposException
45 from kallithea.lib.utils2 import datetime_to_time, fmt_date, generate_api_key, safe_int
45 from kallithea.lib.utils2 import datetime_to_time, generate_api_key, safe_int
46 from kallithea.lib.webutils import url
46 from kallithea.lib.webutils import fmt_date, url
47 from kallithea.model import db, meta, userlog
47 from kallithea.model import db, meta, userlog
48 from kallithea.model.api_key import ApiKeyModel
48 from kallithea.model.api_key import ApiKeyModel
49 from kallithea.model.forms import CustomDefaultPermissionsForm, UserForm
49 from kallithea.model.forms import CustomDefaultPermissionsForm, UserForm
@@ -39,7 +39,7 b' from kallithea.controllers import base'
39 from kallithea.lib import feeds, webutils
39 from kallithea.lib import feeds, webutils
40 from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
40 from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
41 from kallithea.lib.diffs import DiffProcessor
41 from kallithea.lib.diffs import DiffProcessor
42 from kallithea.lib.utils2 import asbool, fmt_date, safe_int, safe_str, shorter
42 from kallithea.lib.utils2 import asbool, safe_int, safe_str
43
43
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
@@ -53,11 +53,11 b' class FeedController(base.BaseRepoContro'
53 super(FeedController, self)._before(*args, **kwargs)
53 super(FeedController, self)._before(*args, **kwargs)
54
54
55 def _get_title(self, cs):
55 def _get_title(self, cs):
56 return shorter(cs.message, 160)
56 return webutils.shorter(cs.message, 160)
57
57
58 def __get_desc(self, cs):
58 def __get_desc(self, cs):
59 desc_msg = [(_('%s committed on %s')
59 desc_msg = [(_('%s committed on %s')
60 % (h.person(cs.author), fmt_date(cs.date))) + '<br/>']
60 % (h.person(cs.author), webutils.fmt_date(cs.date))) + '<br/>']
61 # branches, tags, bookmarks
61 # branches, tags, bookmarks
62 for branch in cs.branches:
62 for branch in cs.branches:
63 desc_msg.append('branch: %s<br/>' % branch)
63 desc_msg.append('branch: %s<br/>' % branch)
@@ -34,14 +34,13 b' from kallithea.lib.annotate import annot'
34 from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoPermissionLevel
34 from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoPermissionLevel
35 from kallithea.lib.diffs import BIN_FILENODE, CHMOD_FILENODE, DEL_FILENODE, MOD_FILENODE, NEW_FILENODE, RENAMED_FILENODE
35 from kallithea.lib.diffs import BIN_FILENODE, CHMOD_FILENODE, DEL_FILENODE, MOD_FILENODE, NEW_FILENODE, RENAMED_FILENODE
36 from kallithea.lib.pygmentsutils import get_custom_lexer
36 from kallithea.lib.pygmentsutils import get_custom_lexer
37 from kallithea.lib.utils2 import (AttributeDict, age, asbool, credentials_filter, fmt_date, link_to_ref, safe_bytes, safe_int, safe_str, shorter,
37 from kallithea.lib.utils2 import AttributeDict, asbool, credentials_filter, link_to_ref, safe_bytes, safe_int, safe_str, time_to_datetime
38 time_to_datetime)
39 from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
38 from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
40 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError
39 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError
41 from kallithea.lib.vcs.utils import author_email, author_name
40 from kallithea.lib.vcs.utils import author_email, author_name
42 from kallithea.lib.webutils import (HTML, Option, canonical_url, checkbox, chop_at, end_form, escape, form, format_byte_size, hidden, js, jshtml, link_to,
41 from kallithea.lib.webutils import (HTML, Option, age, canonical_url, checkbox, chop_at, end_form, escape, fmt_date, form, format_byte_size, hidden, js, jshtml,
43 literal, password, pop_flash_messages, radio, render_w_mentions, reset, safeid, select, session_csrf_secret_name,
42 link_to, literal, password, pop_flash_messages, radio, render_w_mentions, reset, safeid, select, session_csrf_secret_name,
44 session_csrf_secret_token, submit, text, textarea, url, urlify_text, wrap_paragraphs)
43 session_csrf_secret_token, shorter, submit, text, textarea, url, urlify_text, wrap_paragraphs)
45 from kallithea.model import db
44 from kallithea.model import db
46 from kallithea.model.changeset_status import ChangesetStatusModel
45 from kallithea.model.changeset_status import ChangesetStatusModel
47
46
@@ -49,10 +48,12 b' from kallithea.model.changeset_status im'
49 # mute pyflakes "imported but unused"
48 # mute pyflakes "imported but unused"
50 # from webutils
49 # from webutils
51 assert Option
50 assert Option
51 assert age
52 assert canonical_url
52 assert canonical_url
53 assert checkbox
53 assert checkbox
54 assert chop_at
54 assert chop_at
55 assert end_form
55 assert end_form
56 assert fmt_date
56 assert form
57 assert form
57 assert format_byte_size
58 assert format_byte_size
58 assert hidden
59 assert hidden
@@ -67,6 +68,7 b' assert safeid'
67 assert select
68 assert select
68 assert session_csrf_secret_name
69 assert session_csrf_secret_name
69 assert session_csrf_secret_token
70 assert session_csrf_secret_token
71 assert shorter
70 assert submit
72 assert submit
71 assert text
73 assert text
72 assert textarea
74 assert textarea
@@ -77,11 +79,8 b' assert HasPermissionAny'
77 assert HasRepoGroupPermissionLevel
79 assert HasRepoGroupPermissionLevel
78 assert HasRepoPermissionLevel
80 assert HasRepoPermissionLevel
79 # from utils2
81 # from utils2
80 assert age
81 assert credentials_filter
82 assert credentials_filter
82 assert fmt_date
83 assert link_to_ref
83 assert link_to_ref
84 assert shorter
85 assert time_to_datetime
84 assert time_to_datetime
86 # from vcs
85 # from vcs
87 assert EmptyChangeset
86 assert EmptyChangeset
@@ -42,12 +42,9 b' from distutils.version import StrictVers'
42
42
43 import bcrypt
43 import bcrypt
44 import urlobject
44 import urlobject
45 from dateutil import relativedelta
46 from sqlalchemy.engine import url as sa_url
45 from sqlalchemy.engine import url as sa_url
47 from sqlalchemy.exc import ArgumentError
46 from sqlalchemy.exc import ArgumentError
48 from tg import tmpl_context
47 from tg import tmpl_context
49 from tg.i18n import ugettext as _
50 from tg.i18n import ungettext
51 from tg.support.converters import asbool, aslist
48 from tg.support.converters import asbool, aslist
52 from webhelpers2.text import collapse, remove_formatting, strip_tags
49 from webhelpers2.text import collapse, remove_formatting, strip_tags
53
50
@@ -171,124 +168,6 b' def remove_prefix(s, prefix):'
171 return s
168 return s
172
169
173
170
174 def shorter(s, size=20, firstline=False, postfix='...'):
175 """Truncate s to size, including the postfix string if truncating.
176 If firstline, truncate at newline.
177 """
178 if firstline:
179 s = s.split('\n', 1)[0].rstrip()
180 if len(s) > size:
181 return s[:size - len(postfix)] + postfix
182 return s
183
184
185 def age(prevdate, show_short_version=False, now=None):
186 """
187 turns a datetime into an age string.
188 If show_short_version is True, then it will generate a not so accurate but shorter string,
189 example: 2days ago, instead of 2 days and 23 hours ago.
190
191 :param prevdate: datetime object
192 :param show_short_version: if it should approximate the date and return a shorter string
193 :rtype: str
194 :returns: str words describing age
195 """
196 now = now or datetime.datetime.now()
197 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
198 deltas = {}
199 future = False
200
201 if prevdate > now:
202 now, prevdate = prevdate, now
203 future = True
204 if future:
205 prevdate = prevdate.replace(microsecond=0)
206 # Get date parts deltas
207 for part in order:
208 d = relativedelta.relativedelta(now, prevdate)
209 deltas[part] = getattr(d, part + 's')
210
211 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
212 # not 1 hour, -59 minutes and -59 seconds)
213 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
214 part = order[num]
215 carry_part = order[num - 1]
216
217 if deltas[part] < 0:
218 deltas[part] += length
219 deltas[carry_part] -= 1
220
221 # Same thing for days except that the increment depends on the (variable)
222 # number of days in the month
223 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
224 if deltas['day'] < 0:
225 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
226 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)
227 ):
228 deltas['day'] += 29
229 else:
230 deltas['day'] += month_lengths[prevdate.month - 1]
231
232 deltas['month'] -= 1
233
234 if deltas['month'] < 0:
235 deltas['month'] += 12
236 deltas['year'] -= 1
237
238 # In short version, we want nicer handling of ages of more than a year
239 if show_short_version:
240 if deltas['year'] == 1:
241 # ages between 1 and 2 years: show as months
242 deltas['month'] += 12
243 deltas['year'] = 0
244 if deltas['year'] >= 2:
245 # ages 2+ years: round
246 if deltas['month'] > 6:
247 deltas['year'] += 1
248 deltas['month'] = 0
249
250 # Format the result
251 fmt_funcs = {
252 'year': lambda d: ungettext('%d year', '%d years', d) % d,
253 'month': lambda d: ungettext('%d month', '%d months', d) % d,
254 'day': lambda d: ungettext('%d day', '%d days', d) % d,
255 'hour': lambda d: ungettext('%d hour', '%d hours', d) % d,
256 'minute': lambda d: ungettext('%d minute', '%d minutes', d) % d,
257 'second': lambda d: ungettext('%d second', '%d seconds', d) % d,
258 }
259
260 for i, part in enumerate(order):
261 value = deltas[part]
262 if value == 0:
263 continue
264
265 if i < 5:
266 sub_part = order[i + 1]
267 sub_value = deltas[sub_part]
268 else:
269 sub_value = 0
270
271 if sub_value == 0 or show_short_version:
272 if future:
273 return _('in %s') % fmt_funcs[part](value)
274 else:
275 return _('%s ago') % fmt_funcs[part](value)
276 if future:
277 return _('in %s and %s') % (fmt_funcs[part](value),
278 fmt_funcs[sub_part](sub_value))
279 else:
280 return _('%s and %s ago') % (fmt_funcs[part](value),
281 fmt_funcs[sub_part](sub_value))
282
283 return _('just now')
284
285
286 def fmt_date(date):
287 if date:
288 return date.strftime("%Y-%m-%d %H:%M:%S")
289 return ""
290
291
292 def uri_filter(uri):
171 def uri_filter(uri):
293 """
172 """
294 Removes user:password from given url string
173 Removes user:password from given url string
@@ -20,12 +20,16 b' thread-local "global" variables. It shou'
20 imported anywhere - just like the global variables can be used everywhere.
20 imported anywhere - just like the global variables can be used everywhere.
21 """
21 """
22
22
23 import datetime
23 import json
24 import json
24 import logging
25 import logging
25 import random
26 import random
26 import re
27 import re
27
28
29 from dateutil import relativedelta
28 from tg import request, session
30 from tg import request, session
31 from tg.i18n import ugettext as _
32 from tg.i18n import ungettext
29 from webhelpers2.html import HTML, escape, literal
33 from webhelpers2.html import HTML, escape, literal
30 from webhelpers2.html.tags import NotGiven, Option, Options, _input
34 from webhelpers2.html.tags import NotGiven, Option, Options, _input
31 from webhelpers2.html.tags import _make_safe_id_component as safeid
35 from webhelpers2.html.tags import _make_safe_id_component as safeid
@@ -532,3 +536,133 b' def render_w_mentions(source, repo_name='
532 """
536 """
533 s = urlify_text(source, repo_name=repo_name)
537 s = urlify_text(source, repo_name=repo_name)
534 return literal('<div class="formatted-fixed">%s</div>' % s)
538 return literal('<div class="formatted-fixed">%s</div>' % s)
539
540
541 #
542 # Simple filters
543 #
544
545 def shorter(s, size=20, firstline=False, postfix='...'):
546 """Truncate s to size, including the postfix string if truncating.
547 If firstline, truncate at newline.
548 """
549 if firstline:
550 s = s.split('\n', 1)[0].rstrip()
551 if len(s) > size:
552 return s[:size - len(postfix)] + postfix
553 return s
554
555
556 def age(prevdate, show_short_version=False, now=None):
557 """
558 turns a datetime into an age string.
559 If show_short_version is True, then it will generate a not so accurate but shorter string,
560 example: 2days ago, instead of 2 days and 23 hours ago.
561
562 :param prevdate: datetime object
563 :param show_short_version: if it should approximate the date and return a shorter string
564 :rtype: str
565 :returns: str words describing age
566 """
567 now = now or datetime.datetime.now()
568 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
569 deltas = {}
570 future = False
571
572 if prevdate > now:
573 now, prevdate = prevdate, now
574 future = True
575 if future:
576 prevdate = prevdate.replace(microsecond=0)
577 # Get date parts deltas
578 for part in order:
579 d = relativedelta.relativedelta(now, prevdate)
580 deltas[part] = getattr(d, part + 's')
581
582 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
583 # not 1 hour, -59 minutes and -59 seconds)
584 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
585 part = order[num]
586 carry_part = order[num - 1]
587
588 if deltas[part] < 0:
589 deltas[part] += length
590 deltas[carry_part] -= 1
591
592 # Same thing for days except that the increment depends on the (variable)
593 # number of days in the month
594 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
595 if deltas['day'] < 0:
596 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
597 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)
598 ):
599 deltas['day'] += 29
600 else:
601 deltas['day'] += month_lengths[prevdate.month - 1]
602
603 deltas['month'] -= 1
604
605 if deltas['month'] < 0:
606 deltas['month'] += 12
607 deltas['year'] -= 1
608
609 # In short version, we want nicer handling of ages of more than a year
610 if show_short_version:
611 if deltas['year'] == 1:
612 # ages between 1 and 2 years: show as months
613 deltas['month'] += 12
614 deltas['year'] = 0
615 if deltas['year'] >= 2:
616 # ages 2+ years: round
617 if deltas['month'] > 6:
618 deltas['year'] += 1
619 deltas['month'] = 0
620
621 # Format the result
622 fmt_funcs = {
623 'year': lambda d: ungettext('%d year', '%d years', d) % d,
624 'month': lambda d: ungettext('%d month', '%d months', d) % d,
625 'day': lambda d: ungettext('%d day', '%d days', d) % d,
626 'hour': lambda d: ungettext('%d hour', '%d hours', d) % d,
627 'minute': lambda d: ungettext('%d minute', '%d minutes', d) % d,
628 'second': lambda d: ungettext('%d second', '%d seconds', d) % d,
629 }
630
631 for i, part in enumerate(order):
632 value = deltas[part]
633 if value == 0:
634 continue
635
636 if i < 5:
637 sub_part = order[i + 1]
638 sub_value = deltas[sub_part]
639 else:
640 sub_value = 0
641
642 if sub_value == 0 or show_short_version:
643 if future:
644 return _('in %s') % fmt_funcs[part](value)
645 else:
646 return _('%s ago') % fmt_funcs[part](value)
647 if future:
648 return _('in %s and %s') % (fmt_funcs[part](value),
649 fmt_funcs[sub_part](sub_value))
650 else:
651 return _('%s and %s ago') % (fmt_funcs[part](value),
652 fmt_funcs[sub_part](sub_value))
653
654 return _('just now')
655
656
657 def fmt_date(date):
658 if date:
659 return date.strftime("%Y-%m-%d %H:%M:%S")
660 return ""
661
662
663 def capitalize(x):
664 return x.capitalize()
665
666
667 def short_id(x):
668 return x[:12]
@@ -30,7 +30,6 b' from collections import defaultdict'
30
30
31 from kallithea.lib import webutils
31 from kallithea.lib import webutils
32 from kallithea.lib.utils import extract_mentioned_users
32 from kallithea.lib.utils import extract_mentioned_users
33 from kallithea.lib.utils2 import shorter
34 from kallithea.model import db, meta, notification
33 from kallithea.model import db, meta, notification
35
34
36
35
@@ -86,7 +85,7 b' class ChangesetCommentsModel(object):'
86 'cs_comment_url': comment_url,
85 'cs_comment_url': comment_url,
87 'cs_url': webutils.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision),
86 'cs_url': webutils.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision),
88 'message': cs.message,
87 'message': cs.message,
89 'message_short': shorter(cs.message, 50, firstline=True),
88 'message_short': webutils.shorter(cs.message, 50, firstline=True),
90 'cs_author': cs_author,
89 'cs_author': cs_author,
91 'cs_author_username': cs_author.username,
90 'cs_author_username': cs_author.username,
92 'repo_name': repo.repo_name,
91 'repo_name': repo.repo_name,
@@ -116,7 +115,7 b' class ChangesetCommentsModel(object):'
116 # set some variables for email notification
115 # set some variables for email notification
117 email_kwargs = {
116 email_kwargs = {
118 'pr_title': pull_request.title,
117 'pr_title': pull_request.title,
119 'pr_title_short': shorter(pull_request.title, 50),
118 'pr_title_short': webutils.shorter(pull_request.title, 50),
120 'pr_nice_id': pull_request.nice_id(),
119 'pr_nice_id': pull_request.nice_id(),
121 'status_change': status_change,
120 'status_change': status_change,
122 'closing_pr': closing_pr,
121 'closing_pr': closing_pr,
@@ -34,7 +34,6 b' from tg import tmpl_context as c'
34 from tg.i18n import ugettext as _
34 from tg.i18n import ugettext as _
35
35
36 from kallithea.lib import webutils
36 from kallithea.lib import webutils
37 from kallithea.lib.utils2 import fmt_date
38 from kallithea.model import async_tasks, db
37 from kallithea.model import async_tasks, db
39
38
40
39
@@ -101,7 +100,7 b' class NotificationModel(object):'
101 headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading'])
100 headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading'])
102
101
103 # this is passed into template
102 # this is passed into template
104 created_on = fmt_date(datetime.datetime.now())
103 created_on = webutils.fmt_date(datetime.datetime.now())
105 html_kwargs = {
104 html_kwargs = {
106 'body': None if body is None else webutils.render_w_mentions(body, repo_name),
105 'body': None if body is None else webutils.render_w_mentions(body, repo_name),
107 'when': created_on,
106 'when': created_on,
@@ -34,7 +34,7 b' from tg.i18n import ugettext as _'
34
34
35 from kallithea.lib import auth, hooks, webutils
35 from kallithea.lib import auth, hooks, webutils
36 from kallithea.lib.utils import extract_mentioned_users
36 from kallithea.lib.utils import extract_mentioned_users
37 from kallithea.lib.utils2 import ascii_bytes, short_ref_name, shorter
37 from kallithea.lib.utils2 import ascii_bytes, short_ref_name
38 from kallithea.model import changeset_status, comment, db, meta, notification
38 from kallithea.model import changeset_status, comment, db, meta, notification
39
39
40
40
@@ -85,7 +85,7 b' class PullRequestModel(object):'
85 for x in map(pr.org_repo.get_changeset, pr.revisions)]
85 for x in map(pr.org_repo.get_changeset, pr.revisions)]
86 email_kwargs = {
86 email_kwargs = {
87 'pr_title': pr.title,
87 'pr_title': pr.title,
88 'pr_title_short': shorter(pr.title, 50),
88 'pr_title_short': webutils.shorter(pr.title, 50),
89 'pr_user_created': user.full_name_and_username,
89 'pr_user_created': user.full_name_and_username,
90 'pr_repo_url': webutils.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
90 'pr_repo_url': webutils.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
91 'pr_url': pr_url,
91 'pr_url': pr_url,
@@ -345,7 +345,7 b' class CreatePullRequestIterationAction(o'
345 for r in reversed(revisions):
345 for r in reversed(revisions):
346 if r in new_revisions:
346 if r in new_revisions:
347 rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
347 rev_desc = org_repo.get_changeset(r).message.split('\n')[0]
348 infos.append(' %s %s' % (r[:12], shorter(rev_desc, 80)))
348 infos.append(' %s %s' % (r[:12], webutils.shorter(rev_desc, 80)))
349
349
350 if self.create_action.other_ref == old_pull_request.other_ref:
350 if self.create_action.other_ref == old_pull_request.other_ref:
351 infos.append(_("Ancestor didn't change - diff since previous iteration:"))
351 infos.append(_("Ancestor didn't change - diff since previous iteration:"))
@@ -141,7 +141,7 b' class TestLibs(base.TestController):'
141 (dict(years= -3, months= -2), '3 years and 2 months ago'),
141 (dict(years= -3, months= -2), '3 years and 2 months ago'),
142 ])
142 ])
143 def test_age(self, age_args, expected):
143 def test_age(self, age_args, expected):
144 from kallithea.lib.utils2 import age
144 from kallithea.lib.webutils import age
145 with test_context(self.app):
145 with test_context(self.app):
146 n = datetime.datetime(year=2012, month=5, day=17)
146 n = datetime.datetime(year=2012, month=5, day=17)
147 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
147 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
@@ -165,7 +165,7 b' class TestLibs(base.TestController):'
165 (dict(years= -4, months= -8), '5 years ago'),
165 (dict(years= -4, months= -8), '5 years ago'),
166 ])
166 ])
167 def test_age_short(self, age_args, expected):
167 def test_age_short(self, age_args, expected):
168 from kallithea.lib.utils2 import age
168 from kallithea.lib.webutils import age
169 with test_context(self.app):
169 with test_context(self.app):
170 n = datetime.datetime(year=2012, month=5, day=17)
170 n = datetime.datetime(year=2012, month=5, day=17)
171 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
171 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
@@ -183,7 +183,7 b' class TestLibs(base.TestController):'
183 (dict(years=1, months=1), 'in 1 year and 1 month')
183 (dict(years=1, months=1), 'in 1 year and 1 month')
184 ])
184 ])
185 def test_age_in_future(self, age_args, expected):
185 def test_age_in_future(self, age_args, expected):
186 from kallithea.lib.utils2 import age
186 from kallithea.lib.webutils import age
187 with test_context(self.app):
187 with test_context(self.app):
188 n = datetime.datetime(year=2012, month=5, day=17)
188 n = datetime.datetime(year=2012, month=5, day=17)
189 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
189 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now