Permalink
Browse files

Merge branch 'master' of github.com:azavea/Open-Data-Catalog

  • Loading branch information...
2 parents 3a5af9b + d10edb2 commit 06f93bab36d22431ff86a87faea4e388d0491846 Carissa Brittain committed Dec 2, 2011
Showing with 689 additions and 15 deletions.
  1. +3 −0 .gitignore
  2. 0 api/__init__.py
  3. +124 −0 api/encoder.py
  4. +1 −0 api/models.py
  5. +47 −0 api/rest.py
  6. +273 −0 api/tests.py
  7. +193 −0 api/views.py
  8. +11 −0 opendata/models.py
  9. +21 −15 opendata/views.py
  10. +16 −0 urls.py
View
3 .gitignore
@@ -4,3 +4,6 @@
media/*
static/*
static/*
+*~
+*#
+.#*
View
0 api/__init__.py
No changes.
View
124 api/encoder.py
@@ -0,0 +1,124 @@
+from opendata.models import Resource, DataType, Tag, CoordSystem, Url, UrlImage, Idea, IdeaImage
+from suggestions.models import Suggestion
+import simplejson as j
+
+def tiny_resource_encoder(obj):
+ return { "name" : obj.name,
+ "id" : obj.id,
+ "url" : "/api/resources/%s/" %(obj.id)
+ }
+
+
+def short_resource_encoder(obj):
+ return { "name" : obj.name,
+ "short_description" : obj.short_description,
+ "release_date" : obj.release_date,
+ "time_period" : obj.time_period,
+ "organization" : obj.organization,
+ "division" : obj.division,
+ "tags" : list(obj.tags.all()),
+
+ "area_of_interest" : obj.area_of_interest,
+ "is_published" : obj.is_published,
+
+ "rating" : obj.rating.score,
+
+ "id" : obj.id,
+ "url" : "/api/resources/%s/" %(obj.id)
+ }
+
+def full_resource_encoder(obj):
+ return { "name" : obj.name,
+ "short_description" : obj.short_description,
+ "release_date" : obj.release_date,
+ "time_period" : obj.time_period,
+ "organization" : obj.organization,
+ "division" : obj.division,
+ "usage" : obj.usage,
+ "tags" : list(obj.tags.all()),
+ "data_types" : list(obj.data_types.all()),
+ "data_formats" : obj.data_formats,
+
+ "description" : obj.description,
+ "contact_phone" : obj.contact_phone,
+ "contact_email" : obj.contact_email,
+ "contact_url" : obj.contact_url,
+
+ "updates" : obj.updates.update_frequency if obj.updates else None,
+ "update_frequency" : obj.update_frequency,
+ "area_of_interest" : obj.area_of_interest,
+ "is_published" : obj.is_published,
+
+ "created_by" : obj.created_by.username,
+ "last_updated_by" : obj.last_updated_by.username,
+ "last_updated" : obj.last_updated,
+ "metadata_contact" : obj.metadata_contact,
+ "metadata_notes" : obj.metadata_notes,
+
+ "coord_sys" : list(obj.coord_sys.all()),
+ "proj_coord_sys" : obj.proj_coord_sys,
+ "rating" : obj.rating.score,
+
+ "urls" : list(obj.url_set.all()),
+ "id" : obj.id
+ }
+
+def encode_resource(resource_encoder):
+ def encode_resource_with_encoder(obj):
+ if isinstance(obj, Resource):
+ return resource_encoder(obj)
+ elif isinstance(obj, Suggestion):
+ return { "text" : obj.text,
+ "suggested_by" : obj.suggested_by.username,
+ "suggested_date" : obj.suggested_date,
+ "last_modified_date" : obj.last_modified_date,
+ "rating" : obj.rating.votes,
+ "url" : "/api/suggestions/%s/" %(obj.id),
+ "id" : obj.pk
+ }
+ elif isinstance(obj, Idea):
+ return { "title" : obj.title,
+ "id" : obj.pk,
+ "description" : obj.description,
+ "author" : obj.author,
+ "created_by" : obj.created_by.username,
+ "created_by_date" : obj.created_by_date,
+ "updated_by" : obj.updated_by.username,
+ "updated_by_date" : obj.updated_by_date,
+ "resources" : list(obj.resources.all()),
+ "images" : list(IdeaImage.objects.filter(idea = obj).all())
+ }
+ elif isinstance(obj, Url):
+ return { "url" : obj.url,
+ "label" : obj.url_label,
+ "type" : obj.url_type.url_type,
+ "images" : list(obj.urlimage_set.all())
+ }
+ elif isinstance(obj, CoordSystem):
+ return { "name": obj.name,
+ "description": obj.description,
+ "EPSG_code" : obj.EPSG_code
+ }
+ elif isinstance(obj, UrlImage) or isinstance(obj, IdeaImage):
+ return { "title" : obj.title,
+ "source" : obj.source,
+ "source_url" : obj.source_url,
+ "image_thumb_url" : "/media/" + obj.image.thumbnail.relative_url,
+ "image_url" : obj.image.url
+ }
+ elif isinstance(obj, DataType):
+ return obj.data_type
+ elif isinstance(obj, Tag):
+ return { "name" : obj.tag_name,
+ "url" : "/api/tags/%s/" % obj.tag_name }
+ elif hasattr(obj, "strftime"):
+ return obj.strftime("%Y-%m-%d")
+ else:
+ raise TypeError(repr(obj) + " is not JSON serializable")
+ return encode_resource_with_encoder
+
+def json_encode(obj, rsrc = short_resource_encoder):
+ return j.dumps(obj, default = encode_resource(rsrc))
+
+def json_load(jsonstr):
+ return j.loads(jsonstr)
View
1 api/models.py
@@ -0,0 +1 @@
+# Keep the test runner happy
View
47 api/rest.py
@@ -0,0 +1,47 @@
+from django.http import HttpResponse
+from django.contrib.auth import authenticate
+import re
+import base64
+
+def http_unauth():
+ res = HttpResponse("Unauthorized")
+ res.status_code = 401
+ res['WWW-Authenticate'] = 'Basic realm="Secure Area"'
+ return res
+
+def match_first(regx, strg):
+ m = re.match(regx, strg)
+ if (m == None):
+ return None
+ else:
+ return m.group(1)
+
+def decode_auth(strg):
+ if (strg == None):
+ return None
+ else:
+ m = re.match(r'([^:]*)\:(.*)', base64.decodestring(strg))
+ if (m != None):
+ return (m.group(1), m.group(2))
+ else:
+ return None
+
+def parse_auth_string(authstr):
+ auth = decode_auth(match_first('Basic (.*)', authstr))
+ if (auth == None):
+ return None
+ else:
+ return authenticate(username = auth[0], password = auth[1])
+
+def login_required(view_f):
+ def wrapperf(request, *args, **kwargs):
+ if (request.META.has_key('HTTP_AUTHORIZATION')):
+ auth = request.META['HTTP_AUTHORIZATION']
+ user = parse_auth_string(auth)
+ if (user != None):
+ request.user = user
+ return view_f(request, *args, **kwargs)
+ return http_unauth()
+
+ return wrapperf
+
View
273 api/tests.py
@@ -0,0 +1,273 @@
+"""
+A few test to verify basic functionality. The encoder should probably be tested directly
+
+This file demonstrates writing tests using the unittest module. These shoud pass
+when you run "manage.py test".
+"""
+from opendata.models import *
+from suggestions.models import *
+
+from django.test import TestCase
+from django.test.client import Client
+from django.contrib.auth.models import User
+
+from datetime import datetime
+
+import simplejson as j
+import base64
+
+class RestTestCase(TestCase):
+ def setUp(self):
+ self.c = Client()
+ self.password = "password"
+ self.u = User.objects.create(username="testuser")
+ self.u.set_password(self.password)
+ self.u.save()
+
+ self.u2 = User.objects.create(username="testuser2")
+ self.u2.set_password(self.password)
+ self.u2.save()
+
+ def mkrsrc(self,name,**kwargs):
+ return Resource.objects.create(
+ name = name,
+ created_by = self.u,
+ last_updated_by=self.u,
+ created = datetime.now(),
+ **kwargs)
+
+ def mkidea(self,title,**kwargs):
+ return Idea.objects.create(
+ title = title,
+ created_by = self.u,
+ created_by_date = datetime.now(),
+ updated_by = self.u,
+ **kwargs)
+
+ def mktag(self,tag_name,**kwargs):
+ return Tag.objects.create(
+ tag_name = tag_name,
+ **kwargs)
+
+ def mksug(self,text, **kwargs):
+ return Suggestion.objects.create(
+ text = text,
+ suggested_by = self.u,
+ **kwargs)
+
+ def verify_ids(self, objdict, objlist, dict_key="id", list_key = "pk"):
+ self.assertEquals(
+ set(map(lambda obj: obj[dict_key], objdict)),
+ set(map(lambda obj: getattr(obj, list_key), objlist)))
+
+ def get(self, url):
+ return j.loads(self.c.get(url).content)
+
+ def assertCode(self, resp, code):
+ self.assertEquals(resp.status_code, code)
+
+ def assertEmptyList(self, url):
+ self.assertEquals(self.get(url), list())
+
+ def auth_pair(self, user, password = None):
+ if not password:
+ password = self.password
+
+ base64_auth = base64.encodestring(user.username + ":" + password)
+ return {"HTTP_AUTHORIZATION" : "Basic " + base64_auth}
+
+class SuggestionsTest(RestTestCase):
+ def test_empty_case(self):
+ self.assertEmptyList("/api/suggestions/")
+
+ def test_many(self):
+ sug1 = self.mksug("sug1")
+ sug2 = self.mksug("sug2")
+
+ self.verify_ids(self.get("/api/suggestions/"), [sug1,sug2])
+
+ def test_one(self):
+ sug1 = self.mksug("sug1")
+ sug2 = self.mksug("sug2")
+
+ self.assertEquals(self.get("/api/suggestions/%d/" % sug1.pk)["id"], sug1.pk)
+ self.assertEquals(self.get("/api/suggestions/%d/" % sug2.pk)["id"], sug2.pk)
+
+ def test_search(self):
+ sug1 = self.mksug("sug_a_b")
+ sug2 = self.mksug("sug_c_b")
+ sug3 = self.mksug("a_sug_c")
+
+ # All results
+ self.verify_ids(self.get("/api/suggestions/search?qs="), [sug1,sug2,sug3])
+
+ # Basic searches
+ self.verify_ids(self.get("/api/suggestions/search?qs=sug_a"), [sug1])
+ self.verify_ids(self.get("/api/suggestions/search?qs=sug_"), [sug1,sug2,sug3])
+ self.verify_ids(self.get("/api/suggestions/search?qs=_c"), [sug2,sug3])
+
+ # No results
+ self.assertEqual(self.get("/api/suggestions/search?qs=fail"), [])
+
+ def test_invalid_url(self):
+ self.assertCode(self.c.get("/api/suggestions/22/"), 404)
+ self.assertCode(self.c.get("/api/suggestions/f/"), 404)
+
+ def test_invalid_login(self):
+ sug1 = self.mksug("sug1")
+
+ resp = self.c.put("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u,"fail"))
+ self.assertEquals(resp.status_code, 401)
+
+
+ def test_vote(self):
+ sug1 = self.mksug("sug1")
+
+ self.assertEqual(sug1.rating.votes, 0)
+
+ # Single vote
+ self.c.put("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 1)
+
+ # Only allow 1 vote
+ self.c.put("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 1)
+
+ # Remove vote (does not exist)
+ self.c.delete("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u2))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 1)
+
+ # Second vote
+ self.c.put("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u2))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 2)
+
+ # Only allow 1 vote
+ self.c.put("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u2))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 2)
+
+ # Remove vote
+ self.c.delete("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 1)
+
+ # Only remove once
+ self.c.delete("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 1)
+
+ # Back to zero
+ self.c.delete("/api/suggestions/%s/vote"%sug1.pk, {}, **self.auth_pair(self.u2))
+ self.assertEqual(Suggestion.objects.all()[0].rating.votes, 0)
+
+class TagTest(RestTestCase):
+ def test_empty_case(self):
+ self.assertEmptyList("/api/tags/")
+
+ def test_many(self):
+ tag1 = self.mktag("tag1")
+ tag2 = self.mktag("tag2")
+
+ self.verify_ids(self.get("/api/tags/"), [tag1, tag2], "name", "tag_name")
+
+ def test_one_empty(self):
+ tag1 = self.mktag("tag1")
+
+ self.assertEmptyList("/api/tags/%s/" % tag1.tag_name)
+
+ def test_one(self):
+ tag1 = self.mktag("tag1")
+ tag2 = self.mktag("tag2")
+
+ rsrc1 = self.mkrsrc("rsrc1")
+ rsrc2 = self.mkrsrc("rsrc2")
+ rsrc3 = self.mkrsrc("rsrc3")
+
+ rsrc1.tags.add(tag1)
+ rsrc2.tags.add(tag1)
+ rsrc2.tags.add(tag2)
+ rsrc3.tags.add(tag2)
+
+ self.verify_ids(self.get("/api/tags/%s/" % tag1.tag_name),[rsrc1,rsrc2])
+ self.verify_ids(self.get("/api/tags/%s/" % tag2.tag_name),[rsrc2,rsrc3])
+
+class IdeaTest(RestTestCase):
+ def test_empty_case(self):
+ self.assertEmptyList("/api/ideas/")
+
+ def test_many(self):
+ idea1 = self.mkidea("idea1")
+ idea2 = self.mkidea("idea2")
+
+ self.verify_ids(self.get("/api/ideas/"), [idea1,idea2])
+
+ def test_single_case(self):
+ idea1 = self.mkidea("idea1")
+
+ self.assertEquals(self.get("/api/ideas/%d/" % idea1.pk)["id"], idea1.pk)
+
+ def test_invalid_url(self):
+ self.assertCode(self.c.get("/api/ideas/22/"), 404)
+ self.assertCode(self.c.get("/api/ideas/f/"), 404)
+
+
+
+class ResourceTest(RestTestCase):
+
+ def test_empty_json_case(self):
+ self.assertEmptyList("/api/resources/")
+
+ def test_search(self):
+ # Search fields:
+ # name, description, org, div
+
+ rsrc1 = self.mkrsrc("rsrc_a")
+ rsrc2 = self.mkrsrc("rsrc_a", description = "descr_a")
+ rsrc3 = self.mkrsrc("rsrc_b", description = "descr_b")
+ rsrc4 = self.mkrsrc("f_rsrc_c", description = "descr_b", organization = "org_a")
+ rsrc5 = self.mkrsrc("f_rsrc_d", organization = "org_a", division = "div_a")
+ rsrc6 = self.mkrsrc("f_rsrc_e", division = "div_a", organization = "orb_c")
+
+ # Empty search - return everything
+ self.verify_ids(self.get("/api/resources/search?qs="), [rsrc1,rsrc2, rsrc3, rsrc4, rsrc5, rsrc6])
+
+ # No search match
+ self.assertEquals(self.get("/api/resources/search?qs=fail"), list())
+
+ # Name partial search
+ self.verify_ids(self.get("/api/resources/search?qs=f_rsrc_"), [rsrc4,rsrc5,rsrc6])
+
+ # Name exact search
+ self.verify_ids(self.get("/api/resources/search?qs=rsrc_c"), [rsrc4])
+
+ # Description search
+ self.verify_ids(self.get("/api/resources/search?qs=descr_b"), [rsrc3,rsrc4])
+
+ # Organization search
+ self.verify_ids(self.get("/api/resources/search?qs=org_a"), [rsrc4,rsrc5])
+
+ # Division search
+ self.verify_ids(self.get("/api/resources/search?qs=div_a"), [rsrc5,rsrc6])
+
+ def test_multi_case(self):
+
+ rsrc1 = self.mkrsrc("rsrc1")
+ rsrc2 = self.mkrsrc("rsrc2")
+ rsrc3 = self.mkrsrc("rsrc3", is_published=False)
+
+ # Don't show non-published data
+ self.verify_ids(self.get("/api/resources/"), [rsrc1, rsrc2])
+
+ rsrc3.is_published = True
+ rsrc3.save()
+
+ self.verify_ids(self.get("/api/resources/"), [rsrc1, rsrc2, rsrc3])
+
+ def test_single_case(self):
+ rsrc1 = self.mkrsrc("rsrc1")
+
+ self.assertEquals(self.get("/api/resources/%d/" % rsrc1.pk)["id"], rsrc1.pk)
+
+ def test_invalid_url(self):
+ self.assertCode(self.c.get("/api/resources/22/"), 404)
+ self.assertCode(self.c.get("/api/resources/f/"), 404)
+
+
+
View
193 api/views.py
@@ -0,0 +1,193 @@
+# Create your views here
+from django.http import HttpResponse, Http404
+from opendata.models import *
+from opendata.views import send_email
+from suggestions.models import Suggestion
+from datetime import datetime
+from encoder import *
+from rest import login_required
+from django.views.decorators.csrf import csrf_exempt
+
+def http_badreq(body = ""):
+ res = HttpResponse("Bad Request\n" + body)
+ res.status_code = 400
+ return res
+
+@login_required
+def vote(request, suggestion_id):
+ suggestion = Suggestion.objects.get(pk=suggestion_id)
+ remote_addr = request.META['REMOTE_ADDR']
+ if request.method == 'PUT' and suggestion != None:
+ did_vote = suggestion.rating.get_rating_for_user(request.user, remote_addr)
+
+ if did_vote == None:
+ suggestion.rating.add(score=1, user=request.user, ip_address=remote_addr)
+
+ return HttpResponse(json_encode(suggestion))
+
+ elif request.method == "DELETE" and suggestion != None:
+ vote = suggestion.rating.get_ratings().filter(user = request.user)
+ if vote:
+ vote.delete()
+
+ return HttpResponse(json_encode(suggestion))
+
+ raise Http404
+
+def add_suggestion(user, text, remote_addr):
+ sug = Suggestion()
+ sug.suggested_by = user
+ sug.text = text
+
+ sug.save()
+ sug.rating.add(score=1, user=user, ip_address=remote_addr)
+
+ return sug
+
+@login_required
+def add_suggestion_view(request):
+ json_string = request.raw_post_data
+ json_dict = json_load(json_string)
+
+ if (json_dict.has_key("text") == False):
+ return http_badreq()
+
+ text = json_dict["text"]
+
+ return HttpResponse(json_encode(add_suggestion(request.user, text, request.META['REMOTE_ADDR'])))
+
+def suggestion(request, suggestion_id):
+ objs = Suggestion.objects.filter(pk = suggestion_id)
+
+ if objs and len(objs) == 1:
+ return HttpResponse(json_encode(objs[0]))
+ else:
+ raise Http404
+
+@csrf_exempt
+def suggestions(request):
+ if (request.method == 'POST'):
+ return add_suggestion_view(request)
+ elif (request.method == 'GET'):
+ return HttpResponse(json_encode(list(Suggestion.objects.all())))
+ else:
+ raise Http404
+
+def search_suggestions(request):
+ if 'qs' in request.GET:
+ qs = request.GET['qs'].replace("+"," ")
+
+ return HttpResponse(json_encode(list(Suggestion.objects.filter(text__icontains=qs))))
+ else:
+ return http_badreq("Missing required parameter qs")
+
+def ideas(request):
+ return HttpResponse(json_encode(list(Idea.objects.all()), tiny_resource_encoder))
+
+def idea(request, idea_id):
+ obj = Idea.objects.filter(id = idea_id)
+ if obj and len(obj) == 1:
+ return HttpResponse(json_encode(obj[0]))
+ else:
+ raise Http404
+
+def tags(request):
+ return HttpResponse(json_encode(list(Tag.objects.all())))
+
+def by_tag(request, tag_name):
+ return HttpResponse(json_encode(list(Resource.objects.filter(tags__tag_name = tag_name))))
+
+def resource_search(request):
+ if 'qs' in request.GET:
+ qs = request.GET['qs'].replace("+", " ")
+ search_resources = Resource.search(qs)
+
+ return HttpResponse(json_encode(list(search_resources), short_resource_encoder))
+ else:
+ return http_badreq("Must specify qs search param")
+
+def resource(request, resource_id):
+ rsrc = Resource.objects.filter(id=resource_id, is_published = True)
+ if rsrc and len(rsrc) == 1:
+ return HttpResponse(json_encode(rsrc[0], full_resource_encoder))
+ else:
+ raise Http404
+
+def resources(request):
+ return HttpResponse(json_encode(list(Resource.objects.filter(is_published = True)), short_resource_encoder))
+
+def safe_key_getter(dic):
+ def annon(key, f = lambda x: x):
+ if dic.has_key(key):
+ return f(dic[key])
+ else:
+ return None
+ return annon
+
+@csrf_exempt
+def submit(request):
+ if (request.method == 'POST'):
+ json_dict = safe_key_getter(json_load(request.raw_post_data))
+
+ coord_list = json_dict("coord_system")
+ type_list = json_dict("types")
+ format_list = json_dict("formats")
+ update_frequency_list = json_dict("update_frequency")
+
+ coords, types, formats, updates ="", "", "", ""
+
+ if (coord_list == None):
+ return http_badreq("coord_system should be a list")
+ if (type_list == None):
+ return http_badreq("types should be a list")
+ if (format_list == None):
+ return http_badreq("formats should be a list")
+ if (update_frequency_list == None):
+ return http_badreq("update_frequency should be a list")
+
+
+ for c in coord_list:
+ coords = coords + " EPSG:" + CoordSystem.objects.get(pk=c).EPSG_code.__str__()
+
+ for t in type_list:
+ types = types + " " + UrlType.objects.get(pk=t).url_type
+
+ for f in format_list:
+ formats = formats + " " + DataType.objects.get(pk=f).data_type
+
+ for u in update_frequency_list:
+ if u:
+ updates = updates + " " + UpdateFrequency.objects.get(pk=u).update_frequency
+
+ data = {
+ "submitter": request.user.username,
+ "submit_date": datetime.now(),
+ "dataset_name": json_dict("dataset_name"),
+ "organization": json_dict("organization"),
+ "copyright_holder": json_dict("copyright_holder"),
+ "contact_email": json_dict("contact_email"),
+ "contact_phone": json_dict("contact_phone"),
+ "url": json_dict("url"),
+ "time_period": json_dict("time_period"),
+ "release_date": json_dict("release_date"),
+ "area_of_interest": json_dict("area_of_interest"),
+ "update_frequency": updates,
+ "coord_system": coords,
+ "types": types,
+ "formats": formats,
+ "usage_limitations": json_dict("usage_limitations"),
+ "collection_process": json_dict("collection_process"),
+ "data_purpose": json_dict("data_purpose"),
+ "intended_audience": json_dict("intended_audience"),
+ "why": json_dict("why"),
+ }
+
+ for key in data:
+ if (data[key] == None or (hasattr(data[key], "len") and len(data[key]) == 0)):
+ return http_badreq(key + " is empty or not defined")
+
+ send_email(request.user, data)
+
+ return HttpResponse("Created")
+ else:
+ raise Http404
View
11 opendata/models.py
@@ -1,6 +1,7 @@
import os
from operator import attrgetter
from django.db import models
+from django.db.models import Q
from django.conf import settings
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
@@ -58,6 +59,16 @@ class Meta:
verbose_name = 'Coordinate system'
class Resource(models.Model):
+ @classmethod
+ def search(cls, qs = None, objs = None):
+ if objs == None:
+ objs = cls.objects.filter(is_published = True)
+
+ if qs:
+ objs = objs.filter(Q(name__icontains=qs) | Q(description__icontains=qs) | Q(organization__icontains=qs) | Q(division__icontains=qs))
+
+ return objs
+
# Basic Info
name = models.CharField(max_length=255)
short_description = models.CharField(max_length=255)
View
36 opendata/views.py
@@ -76,7 +76,7 @@ def search_results(request):
search_resources = Resource.objects.all()
if 'qs' in request.GET:
qs = request.GET['qs'].replace("+", " ")
- search_resources = search_resources.filter(Q(name__icontains=qs) | Q(description__icontains=qs) | Q(organization__icontains=qs) | Q(division__icontains=qs))
+ search_resources = Resource.search(qs, search_resources)
if 'filter' in request.GET:
f = request.GET['filter']
search_resources = search_resources.filter(url__url_type__url_type__iexact=f).distinct()
@@ -142,26 +142,32 @@ def suggest_content(request):
}
- subject, user_email = 'OpenDataPhilly - Data Submission', (request.user.first_name + " " + request.user.last_name, request.user.email)
- text_content = render_to_string('submit_email.txt', data)
- text_content_copy = render_to_string('submit_email_copy.txt', data)
- mail_managers(subject, text_content)
-
- msg = EmailMessage(subject, text_content_copy, to=user_email)
- msg.send()
-
- sug_object = Submission()
- sug_object.user = request.user
- sug_object.email_text = text_content
-
- sug_object.save()
-
+ send_email(request.user, data)
return render_to_response('thanks.html', context_instance=RequestContext(request))
else:
form = SubmissionForm()
return render_to_response('submit.html', {'form': form}, context_instance=RequestContext(request))
+def send_email(user, data):
+ subject, user_email = 'OpenDataPhilly - Data Submission', (user.first_name + " " + user.last_name, user.email)
+ text_content = render_to_string('submit_email.txt', data)
+ text_content_copy = render_to_string('submit_email_copy.txt', data)
+
+ mail_managers(subject, text_content)
+
+ msg = EmailMessage(subject, text_content_copy, to=user_email)
+ msg.send()
+
+ sug_object = Submission()
+ sug_object.user = user
+ sug_object.email_text = text_content
+
+ sug_object.save()
+
+ return sug_object
+
+
## views called by js ajax for object lists
def get_tag_list(request):
View
16 urls.py
@@ -58,6 +58,22 @@
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
(r'^robots\.txt$', direct_to_template, {'template': 'robots.txt'}),
+ # API urls (all are GET urls unless stated otherwise)
+ (r'^api/resources/$', 'api.views.resources'),
+ (r'^api/resources/(?P<resource_id>\d+)/$', 'api.views.resource'),
+ (r'^api/resources/search$', 'api.views.resource_search'),
+ (r'^api/tags/$', 'api.views.tags'),
+ (r'^api/tags/(?P<tag_name>.*)/$', 'api.views.by_tag'),
+ (r'^api/ideas/$', 'api.views.ideas'),
+ (r'^api/ideas/(?P<idea_id>\d+)/$', 'api.views.idea'),
+ # GET to list, POST to created
+ (r'^api/suggestions/$', 'api.views.suggestions'),
+ (r'^api/suggestions/search$', 'api.views.search_suggestions'),
+ (r'^api/suggestions/(?P<suggestion_id>\d+)/$', 'api.views.suggestion'),
+ # PUT to vote, DELETE to remove
+ (r'^api/suggestions/(?P<suggestion_id>\d+)/vote$', 'api.views.vote'),
+ # POST to create
+ (r'^api/submit/$', 'api.views.submit'),
# Uncomment the next line to enable the admin:
url(r'^_admin_/', include(admin.site.urls)),

0 comments on commit 06f93ba

Please sign in to comment.