|
1 | | -import datetime |
2 | 1 | import os |
3 | | -import re |
4 | 2 | import codecs |
5 | 3 |
|
6 | 4 | from django import forms |
7 | | -from django.core.validators import ValidationError |
8 | 5 | from django.db.models import Q |
9 | | -from django.forms.fields import Field |
10 | | -from django.utils.encoding import force_text |
11 | | -from django.utils import six |
| 6 | +from django.forms.fields import DurationField |
12 | 7 |
|
13 | 8 | from ietf.doc.models import Document, DocAlias, State, NewRevisionDocEvent |
14 | 9 | from ietf.doc.utils import get_document_content |
|
26 | 21 | countries.insert(0, ('', '')) |
27 | 22 | timezones.insert(0, ('', '-' * 9)) |
28 | 23 |
|
29 | | -# ------------------------------------------------- |
30 | | -# DurationField from Django 1.8 |
31 | | -# ------------------------------------------------- |
32 | | - |
33 | | - |
34 | | -def duration_string(duration): |
35 | | - days = duration.days |
36 | | - seconds = duration.seconds |
37 | | - microseconds = duration.microseconds |
38 | | - |
39 | | - minutes = seconds // 60 |
40 | | - seconds = seconds % 60 |
41 | | - |
42 | | - hours = minutes // 60 |
43 | | - minutes = minutes % 60 |
44 | | - |
45 | | - # string = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds) |
46 | | - string = '{:02d}:{:02d}'.format(hours, minutes) |
47 | | - if days: |
48 | | - string = '{} '.format(days) + string |
49 | | - if microseconds: |
50 | | - string += '.{:06d}'.format(microseconds) |
51 | | - |
52 | | - return string |
53 | | - |
54 | | -custom_duration_re = re.compile( |
55 | | - r'^(?P<hours>\d+):(?P<minutes>\d+)$' |
56 | | -) |
57 | | - |
58 | | -standard_duration_re = re.compile( |
59 | | - r'^' |
60 | | - r'(?:(?P<days>-?\d+) (days?, )?)?' |
61 | | - r'((?:(?P<hours>\d+):)(?=\d+:\d+))?' |
62 | | - r'(?:(?P<minutes>\d+):)?' |
63 | | - r'(?P<seconds>\d+)' |
64 | | - r'(?:\.(?P<microseconds>\d{1,6})\d{0,6})?' |
65 | | - r'$' |
66 | | -) |
67 | | - |
68 | | -# Support the sections of ISO 8601 date representation that are accepted by |
69 | | -# timedelta |
70 | | -iso8601_duration_re = re.compile( |
71 | | - r'^P' |
72 | | - r'(?:(?P<days>\d+(.\d+)?)D)?' |
73 | | - r'(?:T' |
74 | | - r'(?:(?P<hours>\d+(.\d+)?)H)?' |
75 | | - r'(?:(?P<minutes>\d+(.\d+)?)M)?' |
76 | | - r'(?:(?P<seconds>\d+(.\d+)?)S)?' |
77 | | - r')?' |
78 | | - r'$' |
79 | | -) |
80 | | - |
81 | | - |
82 | | -def parse_duration(value): |
83 | | - """Parses a duration string and returns a datetime.timedelta. |
84 | | -
|
85 | | - The preferred format for durations in Django is '%d %H:%M:%S.%f'. |
86 | | -
|
87 | | - Also supports ISO 8601 representation. |
88 | | - """ |
89 | | - match = custom_duration_re.match(value) |
90 | | - if not match: |
91 | | - match = standard_duration_re.match(value) |
92 | | - if not match: |
93 | | - match = iso8601_duration_re.match(value) |
94 | | - if match: |
95 | | - kw = match.groupdict() |
96 | | - if kw.get('microseconds'): |
97 | | - kw['microseconds'] = kw['microseconds'].ljust(6, '0') |
98 | | - kw = {k: float(v) for k, v in six.iteritems(kw) if v is not None} |
99 | | - return datetime.timedelta(**kw) |
100 | | - |
101 | | - |
102 | | -class DurationField(Field): |
103 | | - default_error_messages = { |
104 | | - 'invalid': 'Enter a valid duration.', |
105 | | - } |
106 | | - |
107 | | - def prepare_value(self, value): |
108 | | - if isinstance(value, datetime.timedelta): |
109 | | - return duration_string(value) |
110 | | - return value |
111 | | - |
112 | | - def to_python(self, value): |
113 | | - if value in self.empty_values: |
114 | | - return None |
115 | | - if isinstance(value, datetime.timedelta): |
116 | | - return value |
117 | | - value = parse_duration(force_text(value)) |
118 | | - if value is None: |
119 | | - raise ValidationError(self.error_messages['invalid'], code='invalid') |
120 | | - return value |
121 | | - |
122 | | - |
123 | 24 | # ------------------------------------------------- |
124 | 25 | # Helpers |
125 | 26 | # ------------------------------------------------- |
|
0 commit comments