class MaxLengthValidator(BaseValidator): message = ngettext_lazy( 'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).', 'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).', 'limit_value') code = 'max_length' def compare(self, a, b): return a > b def clean(self, x): return len(x)
import calendar import datetime from djmodels.utils.html import avoid_wrapping from djmodels.utils.timezone import is_aware, utc from djmodels.utils.translation import gettext, ngettext_lazy TIME_STRINGS = { 'year': ngettext_lazy('%d year', '%d years'), 'month': ngettext_lazy('%d month', '%d months'), 'week': ngettext_lazy('%d week', '%d weeks'), 'day': ngettext_lazy('%d day', '%d days'), 'hour': ngettext_lazy('%d hour', '%d hours'), 'minute': ngettext_lazy('%d minute', '%d minutes'), } TIMESINCE_CHUNKS = ( (60 * 60 * 24 * 365, 'year'), (60 * 60 * 24 * 30, 'month'), (60 * 60 * 24 * 7, 'week'), (60 * 60 * 24, 'day'), (60 * 60, 'hour'), (60, 'minute'), ) def timesince(d, now=None, reversed=False, time_strings=None): """ Take two datetime objects and return the time between d and now as a nicely formatted string, e.g. "10 minutes". If d occurs after now, return "0 minutes".
class DecimalValidator: """ Validate that the input does not exceed the maximum number of digits expected, otherwise raise ValidationError. """ messages = { 'invalid': _('Enter a number.'), 'max_digits': ngettext_lazy( 'Ensure that there are no more than %(max)s digit in total.', 'Ensure that there are no more than %(max)s digits in total.', 'max'), 'max_decimal_places': ngettext_lazy( 'Ensure that there are no more than %(max)s decimal place.', 'Ensure that there are no more than %(max)s decimal places.', 'max'), 'max_whole_digits': ngettext_lazy( 'Ensure that there are no more than %(max)s digit before the decimal point.', 'Ensure that there are no more than %(max)s digits before the decimal point.', 'max'), } def __init__(self, max_digits, decimal_places): self.max_digits = max_digits self.decimal_places = decimal_places def __call__(self, value): digit_tuple, exponent = value.as_tuple()[1:] if exponent in {'F', 'n', 'N'}: raise ValidationError(self.messages['invalid']) if exponent >= 0: # A positive exponent adds that many trailing zeros. digits = len(digit_tuple) + exponent decimals = 0 else: # If the absolute value of the negative exponent is larger than the # number of digits, then it's the same as the number of digits, # because it'll consume all of the digits in digit_tuple and then # add abs(exponent) - len(digit_tuple) leading zeros after the # decimal point. if abs(exponent) > len(digit_tuple): digits = decimals = abs(exponent) else: digits = len(digit_tuple) decimals = abs(exponent) whole_digits = digits - decimals if self.max_digits is not None and digits > self.max_digits: raise ValidationError( self.messages['max_digits'], code='max_digits', params={'max': self.max_digits}, ) if self.decimal_places is not None and decimals > self.decimal_places: raise ValidationError( self.messages['max_decimal_places'], code='max_decimal_places', params={'max': self.decimal_places}, ) if (self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places)): raise ValidationError( self.messages['max_whole_digits'], code='max_whole_digits', params={'max': (self.max_digits - self.decimal_places)}, ) def __eq__(self, other): return (isinstance(other, self.__class__) and self.max_digits == other.max_digits and self.decimal_places == other.decimal_places)
class ArrayMinLengthValidator(MinLengthValidator): message = ngettext_lazy( 'List contains %(show_value)d item, it should contain no fewer than %(limit_value)d.', 'List contains %(show_value)d items, it should contain no fewer than %(limit_value)d.', 'limit_value')
class NaturalTimeFormatter: time_strings = { # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' 'past-day': gettext_lazy('%(delta)s ago'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'past-hour': ngettext_lazy('an hour ago', '%(count)s hours ago', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'past-minute': ngettext_lazy('a minute ago', '%(count)s minutes ago', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'past-second': ngettext_lazy('a second ago', '%(count)s seconds ago', 'count'), 'now': gettext_lazy('now'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'future-second': ngettext_lazy('a second from now', '%(count)s seconds from now', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'future-minute': ngettext_lazy('a minute from now', '%(count)s minutes from now', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. 'future-hour': ngettext_lazy('an hour from now', '%(count)s hours from now', 'count'), # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' 'future-day': gettext_lazy('%(delta)s from now'), } past_substrings = { # Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' 'year': npgettext_lazy('naturaltime-past', '%d year', '%d years'), 'month': npgettext_lazy('naturaltime-past', '%d month', '%d months'), 'week': npgettext_lazy('naturaltime-past', '%d week', '%d weeks'), 'day': npgettext_lazy('naturaltime-past', '%d day', '%d days'), 'hour': npgettext_lazy('naturaltime-past', '%d hour', '%d hours'), 'minute': npgettext_lazy('naturaltime-past', '%d minute', '%d minutes'), } future_substrings = { # Translators: 'naturaltime-future' strings will be included in '%(delta)s from now' 'year': npgettext_lazy('naturaltime-future', '%d year', '%d years'), 'month': npgettext_lazy('naturaltime-future', '%d month', '%d months'), 'week': npgettext_lazy('naturaltime-future', '%d week', '%d weeks'), 'day': npgettext_lazy('naturaltime-future', '%d day', '%d days'), 'hour': npgettext_lazy('naturaltime-future', '%d hour', '%d hours'), 'minute': npgettext_lazy('naturaltime-future', '%d minute', '%d minutes'), } @classmethod def string_for(cls, value): if not isinstance(value, date): # datetime is a subclass of date return value now = datetime.now(utc if is_aware(value) else None) if value < now: delta = now - value if delta.days != 0: return cls.time_strings['past-day'] % { 'delta': defaultfilters.timesince( value, now, time_strings=cls.past_substrings), } elif delta.seconds == 0: return cls.time_strings['now'] elif delta.seconds < 60: return cls.time_strings['past-second'] % { 'count': delta.seconds } elif delta.seconds // 60 < 60: count = delta.seconds // 60 return cls.time_strings['past-minute'] % {'count': count} else: count = delta.seconds // 60 // 60 return cls.time_strings['past-hour'] % {'count': count} else: delta = value - now if delta.days != 0: return cls.time_strings['future-day'] % { 'delta': defaultfilters.timeuntil( value, now, time_strings=cls.future_substrings), } elif delta.seconds == 0: return cls.time_strings['now'] elif delta.seconds < 60: return cls.time_strings['future-second'] % { 'count': delta.seconds } elif delta.seconds // 60 < 60: count = delta.seconds // 60 return cls.time_strings['future-minute'] % {'count': count} else: count = delta.seconds // 60 // 60 return cls.time_strings['future-hour'] % {'count': count}