diff --git a/kallithea/controllers/admin/users.py b/kallithea/controllers/admin/users.py --- a/kallithea/controllers/admin/users.py +++ b/kallithea/controllers/admin/users.py @@ -42,8 +42,8 @@ from kallithea.controllers import base from kallithea.lib import auth_modules, webutils from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator, LoginRequired from kallithea.lib.exceptions import DefaultUserException, UserCreationError, UserOwnsReposException -from kallithea.lib.utils2 import datetime_to_time, fmt_date, generate_api_key, safe_int -from kallithea.lib.webutils import url +from kallithea.lib.utils2 import datetime_to_time, generate_api_key, safe_int +from kallithea.lib.webutils import fmt_date, url from kallithea.model import db, meta, userlog from kallithea.model.api_key import ApiKeyModel from kallithea.model.forms import CustomDefaultPermissionsForm, UserForm diff --git a/kallithea/controllers/feed.py b/kallithea/controllers/feed.py --- a/kallithea/controllers/feed.py +++ b/kallithea/controllers/feed.py @@ -39,7 +39,7 @@ from kallithea.controllers import base from kallithea.lib import feeds, webutils from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired from kallithea.lib.diffs import DiffProcessor -from kallithea.lib.utils2 import asbool, fmt_date, safe_int, safe_str, shorter +from kallithea.lib.utils2 import asbool, safe_int, safe_str log = logging.getLogger(__name__) @@ -53,11 +53,11 @@ class FeedController(base.BaseRepoContro super(FeedController, self)._before(*args, **kwargs) def _get_title(self, cs): - return shorter(cs.message, 160) + return webutils.shorter(cs.message, 160) def __get_desc(self, cs): desc_msg = [(_('%s committed on %s') - % (h.person(cs.author), fmt_date(cs.date))) + '
'] + % (h.person(cs.author), webutils.fmt_date(cs.date))) + '
'] # branches, tags, bookmarks for branch in cs.branches: desc_msg.append('branch: %s
' % branch) diff --git a/kallithea/lib/helpers.py b/kallithea/lib/helpers.py --- a/kallithea/lib/helpers.py +++ b/kallithea/lib/helpers.py @@ -34,14 +34,13 @@ from kallithea.lib.annotate import annot from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoPermissionLevel from kallithea.lib.diffs import BIN_FILENODE, CHMOD_FILENODE, DEL_FILENODE, MOD_FILENODE, NEW_FILENODE, RENAMED_FILENODE from kallithea.lib.pygmentsutils import get_custom_lexer -from kallithea.lib.utils2 import (AttributeDict, age, asbool, credentials_filter, fmt_date, link_to_ref, safe_bytes, safe_int, safe_str, shorter, - time_to_datetime) +from kallithea.lib.utils2 import AttributeDict, asbool, credentials_filter, link_to_ref, safe_bytes, safe_int, safe_str, time_to_datetime from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError from kallithea.lib.vcs.utils import author_email, author_name -from kallithea.lib.webutils import (HTML, Option, canonical_url, checkbox, chop_at, end_form, escape, form, format_byte_size, hidden, js, jshtml, link_to, - literal, password, pop_flash_messages, radio, render_w_mentions, reset, safeid, select, session_csrf_secret_name, - session_csrf_secret_token, submit, text, textarea, url, urlify_text, wrap_paragraphs) +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, + link_to, literal, password, pop_flash_messages, radio, render_w_mentions, reset, safeid, select, session_csrf_secret_name, + session_csrf_secret_token, shorter, submit, text, textarea, url, urlify_text, wrap_paragraphs) from kallithea.model import db from kallithea.model.changeset_status import ChangesetStatusModel @@ -49,10 +48,12 @@ from kallithea.model.changeset_status im # mute pyflakes "imported but unused" # from webutils assert Option +assert age assert canonical_url assert checkbox assert chop_at assert end_form +assert fmt_date assert form assert format_byte_size assert hidden @@ -67,6 +68,7 @@ assert safeid assert select assert session_csrf_secret_name assert session_csrf_secret_token +assert shorter assert submit assert text assert textarea @@ -77,11 +79,8 @@ assert HasPermissionAny assert HasRepoGroupPermissionLevel assert HasRepoPermissionLevel # from utils2 -assert age assert credentials_filter -assert fmt_date assert link_to_ref -assert shorter assert time_to_datetime # from vcs assert EmptyChangeset diff --git a/kallithea/lib/utils2.py b/kallithea/lib/utils2.py --- a/kallithea/lib/utils2.py +++ b/kallithea/lib/utils2.py @@ -42,12 +42,9 @@ from distutils.version import StrictVers import bcrypt import urlobject -from dateutil import relativedelta from sqlalchemy.engine import url as sa_url from sqlalchemy.exc import ArgumentError from tg import tmpl_context -from tg.i18n import ugettext as _ -from tg.i18n import ungettext from tg.support.converters import asbool, aslist from webhelpers2.text import collapse, remove_formatting, strip_tags @@ -171,124 +168,6 @@ def remove_prefix(s, prefix): return s -def shorter(s, size=20, firstline=False, postfix='...'): - """Truncate s to size, including the postfix string if truncating. - If firstline, truncate at newline. - """ - if firstline: - s = s.split('\n', 1)[0].rstrip() - if len(s) > size: - return s[:size - len(postfix)] + postfix - return s - - -def age(prevdate, show_short_version=False, now=None): - """ - turns a datetime into an age string. - If show_short_version is True, then it will generate a not so accurate but shorter string, - example: 2days ago, instead of 2 days and 23 hours ago. - - :param prevdate: datetime object - :param show_short_version: if it should approximate the date and return a shorter string - :rtype: str - :returns: str words describing age - """ - now = now or datetime.datetime.now() - order = ['year', 'month', 'day', 'hour', 'minute', 'second'] - deltas = {} - future = False - - if prevdate > now: - now, prevdate = prevdate, now - future = True - if future: - prevdate = prevdate.replace(microsecond=0) - # Get date parts deltas - for part in order: - d = relativedelta.relativedelta(now, prevdate) - deltas[part] = getattr(d, part + 's') - - # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00, - # not 1 hour, -59 minutes and -59 seconds) - for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours - part = order[num] - carry_part = order[num - 1] - - if deltas[part] < 0: - deltas[part] += length - deltas[carry_part] -= 1 - - # Same thing for days except that the increment depends on the (variable) - # number of days in the month - month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - if deltas['day'] < 0: - if prevdate.month == 2 and (prevdate.year % 4 == 0 and - (prevdate.year % 100 != 0 or prevdate.year % 400 == 0) - ): - deltas['day'] += 29 - else: - deltas['day'] += month_lengths[prevdate.month - 1] - - deltas['month'] -= 1 - - if deltas['month'] < 0: - deltas['month'] += 12 - deltas['year'] -= 1 - - # In short version, we want nicer handling of ages of more than a year - if show_short_version: - if deltas['year'] == 1: - # ages between 1 and 2 years: show as months - deltas['month'] += 12 - deltas['year'] = 0 - if deltas['year'] >= 2: - # ages 2+ years: round - if deltas['month'] > 6: - deltas['year'] += 1 - deltas['month'] = 0 - - # Format the result - fmt_funcs = { - 'year': lambda d: ungettext('%d year', '%d years', d) % d, - 'month': lambda d: ungettext('%d month', '%d months', d) % d, - 'day': lambda d: ungettext('%d day', '%d days', d) % d, - 'hour': lambda d: ungettext('%d hour', '%d hours', d) % d, - 'minute': lambda d: ungettext('%d minute', '%d minutes', d) % d, - 'second': lambda d: ungettext('%d second', '%d seconds', d) % d, - } - - for i, part in enumerate(order): - value = deltas[part] - if value == 0: - continue - - if i < 5: - sub_part = order[i + 1] - sub_value = deltas[sub_part] - else: - sub_value = 0 - - if sub_value == 0 or show_short_version: - if future: - return _('in %s') % fmt_funcs[part](value) - else: - return _('%s ago') % fmt_funcs[part](value) - if future: - return _('in %s and %s') % (fmt_funcs[part](value), - fmt_funcs[sub_part](sub_value)) - else: - return _('%s and %s ago') % (fmt_funcs[part](value), - fmt_funcs[sub_part](sub_value)) - - return _('just now') - - -def fmt_date(date): - if date: - return date.strftime("%Y-%m-%d %H:%M:%S") - return "" - - def uri_filter(uri): """ Removes user:password from given url string diff --git a/kallithea/lib/webutils.py b/kallithea/lib/webutils.py --- a/kallithea/lib/webutils.py +++ b/kallithea/lib/webutils.py @@ -20,12 +20,16 @@ thread-local "global" variables. It shou imported anywhere - just like the global variables can be used everywhere. """ +import datetime import json import logging import random import re +from dateutil import relativedelta from tg import request, session +from tg.i18n import ugettext as _ +from tg.i18n import ungettext from webhelpers2.html import HTML, escape, literal from webhelpers2.html.tags import NotGiven, Option, Options, _input from webhelpers2.html.tags import _make_safe_id_component as safeid @@ -532,3 +536,133 @@ def render_w_mentions(source, repo_name= """ s = urlify_text(source, repo_name=repo_name) return literal('
%s
' % s) + + +# +# Simple filters +# + +def shorter(s, size=20, firstline=False, postfix='...'): + """Truncate s to size, including the postfix string if truncating. + If firstline, truncate at newline. + """ + if firstline: + s = s.split('\n', 1)[0].rstrip() + if len(s) > size: + return s[:size - len(postfix)] + postfix + return s + + +def age(prevdate, show_short_version=False, now=None): + """ + turns a datetime into an age string. + If show_short_version is True, then it will generate a not so accurate but shorter string, + example: 2days ago, instead of 2 days and 23 hours ago. + + :param prevdate: datetime object + :param show_short_version: if it should approximate the date and return a shorter string + :rtype: str + :returns: str words describing age + """ + now = now or datetime.datetime.now() + order = ['year', 'month', 'day', 'hour', 'minute', 'second'] + deltas = {} + future = False + + if prevdate > now: + now, prevdate = prevdate, now + future = True + if future: + prevdate = prevdate.replace(microsecond=0) + # Get date parts deltas + for part in order: + d = relativedelta.relativedelta(now, prevdate) + deltas[part] = getattr(d, part + 's') + + # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00, + # not 1 hour, -59 minutes and -59 seconds) + for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours + part = order[num] + carry_part = order[num - 1] + + if deltas[part] < 0: + deltas[part] += length + deltas[carry_part] -= 1 + + # Same thing for days except that the increment depends on the (variable) + # number of days in the month + month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + if deltas['day'] < 0: + if prevdate.month == 2 and (prevdate.year % 4 == 0 and + (prevdate.year % 100 != 0 or prevdate.year % 400 == 0) + ): + deltas['day'] += 29 + else: + deltas['day'] += month_lengths[prevdate.month - 1] + + deltas['month'] -= 1 + + if deltas['month'] < 0: + deltas['month'] += 12 + deltas['year'] -= 1 + + # In short version, we want nicer handling of ages of more than a year + if show_short_version: + if deltas['year'] == 1: + # ages between 1 and 2 years: show as months + deltas['month'] += 12 + deltas['year'] = 0 + if deltas['year'] >= 2: + # ages 2+ years: round + if deltas['month'] > 6: + deltas['year'] += 1 + deltas['month'] = 0 + + # Format the result + fmt_funcs = { + 'year': lambda d: ungettext('%d year', '%d years', d) % d, + 'month': lambda d: ungettext('%d month', '%d months', d) % d, + 'day': lambda d: ungettext('%d day', '%d days', d) % d, + 'hour': lambda d: ungettext('%d hour', '%d hours', d) % d, + 'minute': lambda d: ungettext('%d minute', '%d minutes', d) % d, + 'second': lambda d: ungettext('%d second', '%d seconds', d) % d, + } + + for i, part in enumerate(order): + value = deltas[part] + if value == 0: + continue + + if i < 5: + sub_part = order[i + 1] + sub_value = deltas[sub_part] + else: + sub_value = 0 + + if sub_value == 0 or show_short_version: + if future: + return _('in %s') % fmt_funcs[part](value) + else: + return _('%s ago') % fmt_funcs[part](value) + if future: + return _('in %s and %s') % (fmt_funcs[part](value), + fmt_funcs[sub_part](sub_value)) + else: + return _('%s and %s ago') % (fmt_funcs[part](value), + fmt_funcs[sub_part](sub_value)) + + return _('just now') + + +def fmt_date(date): + if date: + return date.strftime("%Y-%m-%d %H:%M:%S") + return "" + + +def capitalize(x): + return x.capitalize() + + +def short_id(x): + return x[:12] diff --git a/kallithea/model/comment.py b/kallithea/model/comment.py --- a/kallithea/model/comment.py +++ b/kallithea/model/comment.py @@ -30,7 +30,6 @@ from collections import defaultdict from kallithea.lib import webutils from kallithea.lib.utils import extract_mentioned_users -from kallithea.lib.utils2 import shorter from kallithea.model import db, meta, notification @@ -86,7 +85,7 @@ class ChangesetCommentsModel(object): 'cs_comment_url': comment_url, 'cs_url': webutils.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision), 'message': cs.message, - 'message_short': shorter(cs.message, 50, firstline=True), + 'message_short': webutils.shorter(cs.message, 50, firstline=True), 'cs_author': cs_author, 'cs_author_username': cs_author.username, 'repo_name': repo.repo_name, @@ -116,7 +115,7 @@ class ChangesetCommentsModel(object): # set some variables for email notification email_kwargs = { 'pr_title': pull_request.title, - 'pr_title_short': shorter(pull_request.title, 50), + 'pr_title_short': webutils.shorter(pull_request.title, 50), 'pr_nice_id': pull_request.nice_id(), 'status_change': status_change, 'closing_pr': closing_pr, diff --git a/kallithea/model/notification.py b/kallithea/model/notification.py --- a/kallithea/model/notification.py +++ b/kallithea/model/notification.py @@ -34,7 +34,6 @@ from tg import tmpl_context as c from tg.i18n import ugettext as _ from kallithea.lib import webutils -from kallithea.lib.utils2 import fmt_date from kallithea.model import async_tasks, db @@ -101,7 +100,7 @@ class NotificationModel(object): headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading']) # this is passed into template - created_on = fmt_date(datetime.datetime.now()) + created_on = webutils.fmt_date(datetime.datetime.now()) html_kwargs = { 'body': None if body is None else webutils.render_w_mentions(body, repo_name), 'when': created_on, diff --git a/kallithea/model/pull_request.py b/kallithea/model/pull_request.py --- a/kallithea/model/pull_request.py +++ b/kallithea/model/pull_request.py @@ -34,7 +34,7 @@ from tg.i18n import ugettext as _ from kallithea.lib import auth, hooks, webutils from kallithea.lib.utils import extract_mentioned_users -from kallithea.lib.utils2 import ascii_bytes, short_ref_name, shorter +from kallithea.lib.utils2 import ascii_bytes, short_ref_name from kallithea.model import changeset_status, comment, db, meta, notification @@ -85,7 +85,7 @@ class PullRequestModel(object): for x in map(pr.org_repo.get_changeset, pr.revisions)] email_kwargs = { 'pr_title': pr.title, - 'pr_title_short': shorter(pr.title, 50), + 'pr_title_short': webutils.shorter(pr.title, 50), 'pr_user_created': user.full_name_and_username, 'pr_repo_url': webutils.canonical_url('summary_home', repo_name=pr.other_repo.repo_name), 'pr_url': pr_url, @@ -345,7 +345,7 @@ class CreatePullRequestIterationAction(o for r in reversed(revisions): if r in new_revisions: rev_desc = org_repo.get_changeset(r).message.split('\n')[0] - infos.append(' %s %s' % (r[:12], shorter(rev_desc, 80))) + infos.append(' %s %s' % (r[:12], webutils.shorter(rev_desc, 80))) if self.create_action.other_ref == old_pull_request.other_ref: infos.append(_("Ancestor didn't change - diff since previous iteration:")) diff --git a/kallithea/tests/other/test_libs.py b/kallithea/tests/other/test_libs.py --- a/kallithea/tests/other/test_libs.py +++ b/kallithea/tests/other/test_libs.py @@ -141,7 +141,7 @@ class TestLibs(base.TestController): (dict(years= -3, months= -2), '3 years and 2 months ago'), ]) def test_age(self, age_args, expected): - from kallithea.lib.utils2 import age + from kallithea.lib.webutils import age with test_context(self.app): n = datetime.datetime(year=2012, month=5, day=17) delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) @@ -165,7 +165,7 @@ class TestLibs(base.TestController): (dict(years= -4, months= -8), '5 years ago'), ]) def test_age_short(self, age_args, expected): - from kallithea.lib.utils2 import age + from kallithea.lib.webutils import age with test_context(self.app): n = datetime.datetime(year=2012, month=5, day=17) delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) @@ -183,7 +183,7 @@ class TestLibs(base.TestController): (dict(years=1, months=1), 'in 1 year and 1 month') ]) def test_age_in_future(self, age_args, expected): - from kallithea.lib.utils2 import age + from kallithea.lib.webutils import age with test_context(self.app): n = datetime.datetime(year=2012, month=5, day=17) delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)