Skip to content

Commit 9883b8d

Browse files
committed
Added a module with autodiscovery support for API resource files, and the necessary support for TimedeltaField to be used with tastypie. ALso a top-level resource list generated from settings.INSTALLED_APPS, in order to not have to maintain the top level manually in ietf/urls.py.
- Legacy-Id: 8741
1 parent 1dc7a86 commit 9883b8d

1 file changed

Lines changed: 130 additions & 0 deletions

File tree

ietf/api.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import re
2+
import six
3+
import datetime
4+
5+
from django.conf import settings
6+
from django.http import HttpResponse
7+
8+
from tastypie.api import Api
9+
from tastypie.serializers import Serializer
10+
from tastypie.exceptions import BadRequest, ApiFieldError
11+
from tastypie.utils.mime import determine_format, build_content_type
12+
from tastypie.utils import is_valid_jsonp_callback_value
13+
from tastypie.fields import ApiField
14+
15+
import debug # pyflakes:ignore
16+
17+
#
18+
# /json/doc
19+
# /json/name
20+
#
21+
#
22+
#
23+
#
24+
#
25+
#
26+
#
27+
28+
_api_list = []
29+
30+
for _app in settings.INSTALLED_APPS:
31+
_module_dict = globals()
32+
if '.' in _app:
33+
_root, _name = _app.split('.', 1)
34+
if _root == 'ietf':
35+
if not '.' in _name:
36+
_api = Api(api_name=_name)
37+
_module_dict[_name] = _api
38+
_api_list.append((_name, _api))
39+
40+
def top_level(request):
41+
available_resources = {}
42+
43+
for name in sorted([ name for name, api in _api_list ]):
44+
available_resources[name] = {
45+
'list_endpoint': '/api/%s/' % name,
46+
}
47+
48+
serializer = Serializer()
49+
desired_format = determine_format(request, serializer)
50+
51+
options = {}
52+
53+
if 'text/javascript' in desired_format:
54+
callback = request.GET.get('callback', 'callback')
55+
56+
if not is_valid_jsonp_callback_value(callback):
57+
raise BadRequest('JSONP callback name is invalid.')
58+
59+
options['callback'] = callback
60+
61+
serialized = serializer.serialize(available_resources, desired_format, options)
62+
return HttpResponse(content=serialized, content_type=build_content_type(desired_format))
63+
64+
def autodiscover():
65+
"""
66+
Auto-discover INSTALLED_APPS resources.py modules and fail silently when
67+
not present. This forces an import on them to register any admin bits they
68+
may want.
69+
"""
70+
71+
from django.conf import settings
72+
from django.utils.importlib import import_module
73+
from django.utils.module_loading import module_has_submodule
74+
75+
76+
for app in settings.INSTALLED_APPS:
77+
mod = import_module(app)
78+
# Attempt to import the app's admin module.
79+
try:
80+
import_module('%s.resources' % (app, ))
81+
except:
82+
# Decide whether to bubble up this error. If the app just
83+
# doesn't have an admin module, we can ignore the error
84+
# attempting to import it, otherwise we want it to bubble up.
85+
if module_has_submodule(mod, "resources"):
86+
raise
87+
88+
TIMEDELTA_REGEX = re.compile('^(?P<days>\d+d)?\s?(?P<hours>\d+h)?\s?(?P<minutes>\d+m)?\s?(?P<seconds>\d+s?)$')
89+
90+
class TimedeltaField(ApiField):
91+
dehydrated_type = 'timedelta'
92+
help_text = "A timedelta field, with duration expressed in seconds. Ex: 132"
93+
94+
def convert(self, value):
95+
if value is None:
96+
return None
97+
98+
if isinstance(value, six.string_types):
99+
match = TIMEDELTA_REGEX.search(value)
100+
101+
if match:
102+
data = match.groupdict()
103+
return datetime.timedelta(int(data['days']), int(data['hours']), int(data['minutes']), int(data['seconds']))
104+
else:
105+
raise ApiFieldError("Timedelta provided to '%s' field doesn't appear to be a valid timedelta string: '%s'" % (self.instance_name, value))
106+
107+
return value
108+
109+
def hydrate(self, bundle):
110+
value = super(TimedeltaField, self).hydrate(bundle)
111+
112+
if value and not hasattr(value, 'seconds'):
113+
if isinstance(value, six.string_types):
114+
try:
115+
match = TIMEDELTA_REGEX.search(value)
116+
117+
if match:
118+
data = match.groupdict()
119+
value = datetime.timedelta(int(data['days']), int(data['hours']), int(data['minutes']), int(data['seconds']))
120+
else:
121+
raise ValueError()
122+
except (ValueError, TypeError):
123+
raise ApiFieldError("Timedelta provided to '%s' field doesn't appear to be a valid datetime string: '%s'" % (self.instance_name, value))
124+
125+
else:
126+
raise ApiFieldError("Datetime provided to '%s' field must be a string: %s" % (self.instance_name, value))
127+
128+
return value
129+
130+

0 commit comments

Comments
 (0)