diff --git a/redditors/models.py b/redditors/models.py index 8f519d4..6aeddbe 100644 --- a/redditors/models.py +++ b/redditors/models.py @@ -23,7 +23,7 @@ class Location(models.Model): name = models.CharField(max_length=200) street_address = models.CharField(max_length=200, blank=True) category = models.ForeignKey(LocationCategory, blank=True, null=True) - geometry = models.PointField(srid=4326) + geometry = models.PointField(srid=4326, blank=True, null=True) objects = models.GeoManager() def __str__(self): @@ -33,7 +33,6 @@ class Location(models.Model): """ Tally up how many checkins this location had in the past day. """ - print self.checkin_set.filter(timestamp__gte = datetime.now() + timedelta(days=-1)) return self.checkin_set.filter(timestamp__gte = datetime.now() + timedelta(days=-1)).count() def address_for_geocode(self): diff --git a/redditors/views.py b/redditors/views.py index a84fbe9..571fbec 100644 --- a/redditors/views.py +++ b/redditors/views.py @@ -1,17 +1,26 @@ +import urllib2 +from urllib2 import HTTPError + +try: + # Python 2.6 and up + import json as simplejson +except ImportError: + # This case gets rarer by the day, but if we need to, we can pull it from Django provided it's there. + from django.utils import simplejson + from datetime import datetime, timedelta from django.contrib.gis.geos import * from django.contrib.gis.measure import D from django import forms -from django.contrib.auth.forms import UserCreationForm from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response -from django.utils import simplejson -from django.contrib.auth.models import User from django.template import RequestContext, Context from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator, InvalidPage, EmptyPage +from django.contrib.auth.models import User +from django.contrib.auth.forms import UserCreationForm from drinkkit.redditors.models import LocationCategory, Location, Tip, Checkin @@ -153,28 +162,57 @@ def add_location(request): fixed at some point. ;P """ if request.POST: - if request.POST['location_name'] and request.POST['lat'] and request.POST['long']: - new_location = Location() - new_location.name = request.POST['location_name'] - new_location.geometry = 'POINT(%s %s)' %(request.POST['lat'], request.POST['long']) + # Somewhat ugly, but meh. + if request.POST['location_name'] and (request.POST['street_address'] or (request.POST['lat'] and request.POST['long'])): + new_location = Location() + new_location.name = request.POST['location_name'] + + if request.POST['lat'] and request.POST['long']: + new_location.geometry = 'POINT(%s %s)' %(request.POST['lat'], request.POST['long']) + else: + # If we don't have a lat/long pair, we know they got here by providing a street address, so + # let's do some geocoding via Google APIs and find the lat/long ourselves. + # + # Note: We assume Washington, DC here because of the region this application is suited to. If + # you wanted to expand on this, you'd probably wanna get the region/IP-(lat/long) (which isn't as accurate) + # and use it to sandbox your results. This is a pretty hacky 5AM thing. ;P + fixed_address = request.POST['street_address'].replace(" ", "+") + api_url = "http://maps.googleapis.com/maps/api/geocode/json?&address=%s,Washington,DC&sensor=false" % fixed_address + + try: + # Download and parse the JSON response into native Python objects. + geocode_request = urllib2.Request(api_url) + geocoded_data = simplejson.load(urllib2.urlopen(geocode_request)) + + # Store latitude and longitude, for readability + for result in geocoded_data["results"]: + latitude = result["geometry"]["location"]["lat"] + longitude = result["geometry"]["location"]["lng"] + + # Save our determined geometry coordinates + new_location.geometry = 'POINT(%s %s)' %(latitude, longitude) + except HTTPError: + # You're boned, Google's either blocking you or you done goofed. Ordinarily, you'd + # wanna handle errors properly here, but in my case I want a hard failure. YMMV. + pass + + # If they've supplied a street address, sweet, use it. + if request.POST['street_address']: + new_location.street_address = request.POST['street_address'] - # If they've supplied a street address, sweet, use it. - if request.POST['street_address']: - new_location.street_address = request.POST['street_address'] + # If they set a location type/category, let's record it... (5AM code) + if request.POST['location_type']: + try: + category = LocationCategory.objects.get(id = request.POST['location_type']) + new_location.category = category + except LocationCategory.DoesNotExist: + pass - # If they set a location, let's record it... (5AM code) - if request.POST['location_type']: - try: - category = LocationCategory.objects.get(id = request.POST['location_type']) - new_location.category = category - except LocationCategory.DoesNotExist: - pass - - new_location.save() - return HttpResponseRedirect('/locations/%s/' % str(new_location.id)) + new_location.save() + return HttpResponseRedirect('/locations/%s/' % str(new_location.id)) else: if not request.POST['lat'] or not request.POST['lat']: - errmsg = "We weren't able to get coordinates for where you are right now. Does your phone or device have GPS?" + errmsg = "We weren't able to get coordinates for where you are right now. Does your phone or device have GPS? If not, specify an address and we'll use that instead!" if not request.POST['location_name']: errmsg = "You didn't even bother to enter a name for this location. Wtf?" return render_to_response('locations/add.html', @@ -235,8 +273,17 @@ def add_tip(request, location_id): else: return HttpResponse(status=404) -def find_redditors(request): +def view_redditor(request, redditor_name): """ - Handles locating all Redditors in a given area who recently checked in. + View a Redditor "profile" - right now, all we do is show their checkin + history (where they've been). Stalking is fun, right? + + ...right? """ - pass + try: + redditor = User.objects.get(username = redditor_name) + return render_to_response('redditors/view_profile.html', + context_instance = RequestContext(request, {'redditor': redditor}) + ) + except User.DoesNotExist: + return HttpResponse(status=404) diff --git a/templates/base.html b/templates/base.html index 5f36405..f9da1c7 100644 --- a/templates/base.html +++ b/templates/base.html @@ -13,11 +13,11 @@ body { width: 100%; color: #555; - font-family:Helvetica, "Helvetica Neue", Arial, sans-serif; + font-family: Helvetica, "Helvetica Neue", Arial, sans-serif; height: 100%; padding: 0; margin: 0; - background:#c5ccd3; + background: #c5ccd3; -webkit-font-smoothing: antialiased; } @@ -26,11 +26,28 @@ padding: 10px 12px; margin: 0; } + + .rounded_6px { + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + -khtml-border-radius: 6px; + border-radius: 6px; + } + + .rounded_4px { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + } + + #sandbox { clear: both; } #title { width: 100%; position: relative; background: #cee3f8; + background:-moz-linear-gradient(-90deg, #CEE3F8, #A8C4E0) repeat scroll 0 0 transparent; background: -webkit-gradient(linear, left top, left bottom, from(#cee3f8), to(#a8c4e0)); padding: 7px 0 6px 0; margin: 0; @@ -45,6 +62,7 @@ width: 100%; position: relative; background: #cdcdcd; + background:-moz-linear-gradient(-90deg, #fafafa, #cdcdcd) repeat scroll 0 0 transparent; background: -webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#cdcdcd)); padding: 4px 0 6px; font-size: 1.2em; @@ -58,20 +76,27 @@ background-color: #fff; -webkit-border-radius: 4px; border: 1px solid #8f8f8f; + padding-bottom: 5px; } .button { background-color: #80A2C4; border: 1px solid #517191; + background:-moz-linear-gradient(-90deg, #bfd0e0, #80a2c4) repeat scroll 0 0 transparent; background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#bfd0e0), to(#80a2c4)); - -moz-border-radius: 6px; - -webkit-border-radius: 6px; text-decoration: none; color: #fff; font-size: 11px; padding: 3px 7px; } + .orange_button { + border: 1px solid #ff4500; + background-color: #ff4500; + background:-moz-linear-gradient(-90deg, #ff9f7b, #ff4500) repeat scroll 0 0 transparent; + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ff9f7b), to(#ff4500)); + } + .large { font-size: 14px; padding: 4px 10px; @@ -86,9 +111,8 @@ #home_link, #nearby_link, #search_link, #find_link, #add_location { background-color: #80A2C4; border: 1px solid #517191; + background:-moz-linear-gradient(-90deg, #bfd0e0, #80a2c4) repeat scroll 0 0 transparent; background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#bfd0e0), to(#80a2c4)); - -moz-border-radius: 6px; - -webkit-border-radius: 6px; display: inline-block; text-decoration: none; color: #fff; @@ -102,11 +126,11 @@ h2 { font-size: 18px; - /*color: #ff7041;*/ color: #6c7ca1; margin: 2px; padding: 8px; background-color: #ebf3fc; + background:-moz-linear-gradient(-90deg, #fafcfe, #ebf3fc) repeat scroll 0 0 transparent; background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fafcfe), to(#ebf3fc)); border: 1px solid #c4dbf1; } @@ -133,47 +157,49 @@ } #button_center { text-align: center; } - -/* Oh god I'm so tired... */ -.go_location { - width: 70px !important; - position: absolute; - top: 0px; - right: 10px; - color: #fff !important; - font-size: 14px !important; - font-weight: bold; - padding: 5px 12px !important; - text-align: center !important; - border: 1px solid #517191 !important; - -webkit-border-bottom-left-radius: 6px !important; - -webkit-border-bottom-right-radius: 6px !important; -} -.top_level { - margin-bottom: 10px; - position: relative; -} + .top_level { + border: 1px solid #c4dbf1; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + float: left; + width: 99%; + padding-bottom: 5px; + margin-bottom: 10px; + } -#results li a, #results li a:visited { - border: 1px solid #c9c9c9; - background-color: #f9f9f9; - -webkit-border-radius: 2px; - padding: 10px; - display: block; - color: #555; - text-decoration: none; - padding-bottom: 3px; - margin-bottom: 5px; - width: 92%; -} - -.location_name { font-weight: bold; color: #ff8070; } - -.top_level ul { padding: 5px 0 0 0; } -.top_level li { margin-bottom: 5px;} + .top_level h3 { + background-color: #ebf3fc; + background:-moz-linear-gradient(-90deg, #fafcfe, #ebf3fc) repeat scroll 0 0 transparent; + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fafcfe), to(#ebf3fc)); + border-bottom: 1px solid #c4dbf1; + margin: 0; + padding: 5px; + font-size: 13px; + color: #6C7CA1; + } + .top_level ul { padding: 5px; font-size: 12px; } + .top_level ul li { margin-bottom: 3px !important; } + .checkin_list_timestamp { color: #555; font-weight: normal; float: right; } + #results li a, #results li a:visited { text-decoration: none; } + .anchored_action { + float: right; + clear: right; + margin: 5px 5px 0 0; + width: auto !important; + color: #fff !important; + font-size: 12px !important; + font-weight: bold; + padding: 3px 9px !important; + text-align: center !important; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + min-width: 100px; + } @@ -234,11 +263,11 @@