Merge lp:~dholbach/harvest/581730 into lp:harvest

Proposed by Daniel Holbach
Status: Merged
Merged at revision: 244
Proposed branch: lp:~dholbach/harvest/581730
Merge into: lp:harvest
Diff against target: 231 lines (+194/-0)
5 files modified
harvest/services/__init__.py (+155/-0)
harvest/services/urls.py (+12/-0)
harvest/services/views.py (+25/-0)
harvest/settings.py (+1/-0)
harvest/urls.py (+1/-0)
To merge this branch: bzr merge lp:~dholbach/harvest/581730
Reviewer Review Type Date Requested Status
James Westby Approve
Review via email: mp+33387@code.launchpad.net
To post a comment you must log in.
Revision history for this message
James Westby (james-w) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'harvest/services'
2=== added file 'harvest/services/__init__.py'
3--- harvest/services/__init__.py 1970-01-01 00:00:00 +0000
4+++ harvest/services/__init__.py 2010-08-23 14:17:42 +0000
5@@ -0,0 +1,155 @@
6+# Portions of this code comes from the Oct09 project
7+# developed by the Moffitt Cancer Center.
8+# License: http://bitbucket.org/oct09/main/src/tip/COPYING
9+# Project: http://bitbucket.org/oct09/main/wiki/Home
10+
11+from django.db.models import Model
12+from django.http import HttpResponse
13+from django.forms.fields import EMPTY_VALUES
14+from django.utils import simplejson
15+import re
16+import decimal
17+
18+CHUNK_SIZE = 500
19+
20+def parse_number_argument(arguments, which):
21+ if not arguments.has_key(which):
22+ return None
23+ if arguments[which] in EMPTY_VALUES:
24+ return None
25+ try:
26+ number = int(arguments[which])
27+ except:
28+ number = None
29+ return number
30+
31+def model_service(model, request, url, include=None, exclude=None):
32+ (instance_id, rem_url) = get_model_id(request, url)
33+
34+ try:
35+ if instance_id is not None and instance_id != '':
36+ return model_entity(model, request, url, include, exclude)
37+ else:
38+ return model_collection(model, request, url, include, exclude)
39+ except Exception, e:
40+ return HttpResponse(encode_error(e))
41+
42+def model_entity(model, request, url, include=None, exclude=None):
43+ (entity_id, rem_url) = get_model_id(request, url)
44+
45+ try:
46+ entity = model.objects.get(pk=entity_id)
47+ except:
48+ return HttpResponse(encode_error("Entity not found"))
49+
50+
51+ # Otherwise process the entity request
52+ if request.method == "GET":
53+ return HttpResponse(encode(entity, include, exclude), mimetype=get_mimetype())
54+
55+ elif request.method == "PUT":
56+ return HttpResponse(encode_error("Write operations are not supported"))
57+
58+ elif request.method == "DELETE":
59+ return HttpResponse(encode_error("Write operations are not supported"))
60+
61+ return HttpResponse(encode_error("%s Not Implemented"%request.method))
62+
63+def model_collection(model, request, url, include=None, exclude=None):
64+ if request.method == "GET":
65+ results = do_search(model, request.GET, include, exclude)
66+
67+ collection = list(results)
68+ return HttpResponse(encode(collection, include, exclude), mimetype=get_mimetype())
69+
70+ elif request.method == "POST":
71+ return HttpResponse(encode_error("Write operations are not supported"))
72+
73+ return HttpResponse(encode_error("%s Not Implemented"%request.method))
74+
75+def do_search(model, request, include=None, exclude=None):
76+ search_fields = include
77+ if search_fields is None:
78+ search_fields = [f.name for f in model._meta.local_fields+model._meta.many_to_many if not f.name.endswith("_ptr")]
79+ if exclude is not None:
80+ search_fields = [f for f in search_fields if f not in exclude]
81+ search_values = dict([(str(key), value) for (key, value) in request.items()])
82+
83+ extra_arguments = {}
84+ for key in search_values.keys():
85+ if key.startswith('_'):
86+ extra_arguments[key] = search_values.pop(key)
87+ offset = parse_number_argument(extra_arguments, '_offset')
88+ length = parse_number_argument(extra_arguments, '_length')
89+ if not length or length > CHUNK_SIZE:
90+ length = CHUNK_SIZE
91+ results = model.objects.filter(**search_values)[offset:length]
92+
93+ if '_sortby' in request and request['_sortby'] not in EMPTY_VALUES:
94+ results = results.order_by(request.get('_sortby'))
95+ return results
96+
97+def get_model_id(request, url):
98+ instance_id = None
99+ rem_url = None
100+ if url is not None and len(url) > 0:
101+ m = re.match(r"^([^/]+)/?(.*)", url)
102+
103+ if m is not None:
104+ instance_id = m.group(1)
105+ rem_url = m.group(2)
106+ return (instance_id, rem_url)
107+
108+def encode(entity, include=None, exclude=None):
109+ json = JSONEncoder(include, exclude)
110+ return json.encode(entity)
111+
112+def decode(klass, entity, request, url=None):
113+ return request.raw_post_data
114+
115+def get_mimetype():
116+ return "application/json"
117+
118+def encode_error(error):
119+ json = JSONEncoder()
120+ return json.encode({'error': unicode(error)})
121+
122+class JSONEncoder(simplejson.JSONEncoder):
123+
124+ def __init__(self, include=None, exclude=None):
125+ self.include = include
126+ self.exclude = exclude
127+ super(JSONEncoder, self).__init__()
128+
129+ def get_field_value(self, o, field):
130+ f = getattr(o, field)
131+ try:
132+ if isinstance(f, Model):
133+ if hasattr(f, 'pk'):
134+ return self.default(f.pk)
135+ else:
136+ return None
137+ if isinstance(f, decimal.Decimal):
138+ return o._meta.get_field_by_name(field)[0]._format(f)
139+ else:
140+ return self.default(f)
141+ except Exception:
142+ return None
143+
144+ def default(self, o=None):
145+
146+ if isinstance(o, Model):
147+ model_fields = self.include
148+ if model_fields is None:
149+ model_fields = [f.name for f in o._meta.fields+o._meta.many_to_many if not f.name.endswith("_ptr")]
150+ if self.exclude is not None:
151+ model_fields = [f for f in model_fields if f not in self.exclude]
152+ d = dict([(field, self.get_field_value(o, field)) for field in model_fields])
153+ return d
154+ elif o.__class__.__name__ == 'ManyRelatedManager' or o.__class__.__name__ == 'RelatedManager':
155+ return [r.pk for r in o.all()]
156+ elif isinstance(o, (int, long, float)):
157+ return o
158+ elif o is None:
159+ return None
160+ return str(o)
161
162=== added file 'harvest/services/urls.py'
163--- harvest/services/urls.py 1970-01-01 00:00:00 +0000
164+++ harvest/services/urls.py 2010-08-23 14:17:42 +0000
165@@ -0,0 +1,12 @@
166+from django.conf.urls.defaults import *
167+
168+urlpatterns = patterns('',
169+ url(r'^users/(.*)$', 'services.views.user_service', name='user_service'),
170+ url(r'^packagesets/(.*)$', 'services.views.packageset_service', name='packageset_service'),
171+ url(r'^sourcepackages/(.*)$', 'services.views.sourcepackage_service', name='sourcepackage_service'),
172+ url(r'^opportunitylists/(.*)$', 'services.views.opportunitylist_service', name='opportunitylist_service'),
173+ url(r'^opportunities/(.*)$', 'services.views.opportunity_service', name='opportunity_service'),
174+ url(r'^notes/(.*)$', 'services.views.note_service', name='note_service'),
175+ url(r'^actionlogentries/(.*)$', 'services.views.actionlogentry_service', name='actionlogentry_service'),
176+)
177+
178
179=== added file 'harvest/services/views.py'
180--- harvest/services/views.py 1970-01-01 00:00:00 +0000
181+++ harvest/services/views.py 2010-08-23 14:17:42 +0000
182@@ -0,0 +1,25 @@
183+from opportunities.models import PackageSet, SourcePackage, OpportunityList, Opportunity, Note, ActionLogEntry
184+from django.contrib.auth.models import User
185+
186+from services import model_service
187+
188+def user_service(request, url):
189+ return model_service(User, request, url, include=['id', 'username', 'groups'])
190+
191+def packageset_service(request, url):
192+ return model_service(PackageSet, request, url)
193+
194+def sourcepackage_service(request, url):
195+ return model_service(SourcePackage, request, url)
196+
197+def opportunitylist_service(request, url):
198+ return model_service(OpportunityList, request, url)
199+
200+def opportunity_service(request, url):
201+ return model_service(Opportunity, request, url)
202+
203+def note_service(request, url):
204+ return model_service(Note, request, url)
205+
206+def actionlogentry_service(request, url):
207+ return model_service(ActionLogEntry, request, url)
208
209=== modified file 'harvest/settings.py'
210--- harvest/settings.py 2010-07-14 19:52:01 +0000
211+++ harvest/settings.py 2010-08-23 14:17:42 +0000
212@@ -111,6 +111,7 @@
213 'django_openid_auth',
214 'opportunities',
215 'filters',
216+ 'services',
217 'common',
218 )
219
220
221=== modified file 'harvest/urls.py'
222--- harvest/urls.py 2009-08-26 14:57:36 +0000
223+++ harvest/urls.py 2010-08-23 14:17:42 +0000
224@@ -7,6 +7,7 @@
225 urlpatterns = patterns('',
226 url(r'^$', 'common.views.index', name='home'),
227 url(r'^opportunities/', include('opportunities.urls')),
228+ url(r'^services/', include('services.urls')),
229 url(r'^openid/', include('django_openid_auth.urls')),
230 url(r'^logout$', 'common.views.site_logout'),
231 url(r'^admin/', include(admin.site.urls)),

Subscribers

People subscribed via source and target branches

to all changes: