Improve Lookup field to be more intuitive and show initial value. Move css out of base.html and into new static get_together.css. Fixes #57

This commit is contained in:
Michael Hall 2018-03-25 14:40:29 -04:00
parent 80b6374f4b
commit 3d5046e0c4
8 changed files with 101 additions and 73 deletions

View file

@ -4,29 +4,13 @@ from django.forms.widgets import TextInput, Media
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from .models.locale import Country, SPR, City
from .models.profiles import Team, UserProfile from .models.profiles import Team, UserProfile
from .models.events import Event, EventComment ,CommonEvent, Place, EventPhoto from .models.events import Event, EventComment ,CommonEvent, Place, EventPhoto
from datetime import time from datetime import time
from time import strptime, strftime from time import strptime, strftime
class LookupMedia(Media):
def render(self):
return mark_safe('''<script type="text/javascript"><script>
$(document).ready(function(){
$("#{{ widget.name }}_search").keyup(function() {
var searchText = this.value;
$.getJSON("{{ widget.source }}?q="+searchText, function(data) {
var selectField = $("#{{ widget.name }}_select");
selectField.empty();
$.each(data, function(){
selectField.append('<option value="'+ this.{{ widget.key }} +'">'+ this.{{ widget.label }} + '</option>')
});
});
});
});
</script>''')
class Lookup(TextInput): class Lookup(TextInput):
input_type = 'text' input_type = 'text'
template_name = 'forms/widgets/lookup.html' template_name = 'forms/widgets/lookup.html'
@ -34,7 +18,7 @@ class Lookup(TextInput):
checked_attribute = {'selected': True} checked_attribute = {'selected': True}
option_inherits_attrs = False option_inherits_attrs = False
def __init__(self, source='#', key="id", label="name", attrs=None): def __init__(self, source, key="id", label='__str__', attrs=None):
super().__init__(attrs) super().__init__(attrs)
self.source = source self.source = source
self.key = key self.key = key
@ -42,14 +26,18 @@ class Lookup(TextInput):
def get_context(self, name, value, attrs): def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs) context = super().get_context(name, value, attrs)
context['widget']['source'] = self.source
context['widget']['key'] = self.key
context['widget']['label'] = self.label
return context return context
def format_value(self, value): def format_value(self, value):
if value is not None: if value is not None:
return mark_safe('<option value="%s">%s</option>' % (value, _('No change'))) lookup_query = {self.key: value}
lookup_object = self.source.objects.get(**lookup_query)
lookup_field = getattr(lookup_object, self.label)
if callable(lookup_field):
lookup_value = lookup_field()
else:
lookup_value = lookup_field
return mark_safe('<option value="%s">%s</option>' % (value, lookup_value))
else: else:
return mark_safe('<option value="">--------</option>') return mark_safe('<option value="">--------</option>')
@ -152,7 +140,7 @@ class TeamForm(forms.ModelForm):
model = Team model = Team
fields = ['name', 'description', 'category', 'city', 'web_url', 'tz'] fields = ['name', 'description', 'category', 'city', 'web_url', 'tz']
widgets = { widgets = {
'city': Lookup(source='/api/cities/', label='name'), 'city': Lookup(source=City),
} }
raw_id_fields = ('city') raw_id_fields = ('city')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -164,7 +152,7 @@ class NewTeamForm(forms.ModelForm):
model = Team model = Team
fields = ['name', 'description', 'category', 'city', 'web_url', 'tz'] fields = ['name', 'description', 'category', 'city', 'web_url', 'tz']
widgets = { widgets = {
'city': Lookup(source='/api/cities/', label='name'), 'city': Lookup(source=City),
} }
raw_id_fields = ('city') raw_id_fields = ('city')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -179,7 +167,7 @@ class TeamEventForm(forms.ModelForm):
model = Event model = Event
fields = ['name', 'start_time', 'end_time', 'summary', 'place', 'web_url', 'announce_url', 'tags'] fields = ['name', 'start_time', 'end_time', 'summary', 'place', 'web_url', 'announce_url', 'tags']
widgets = { widgets = {
'place': Lookup(source='/api/places/', label='name'), 'place': Lookup(source=Place),
'start_time': DateTimeWidget, 'start_time': DateTimeWidget,
'end_time': DateTimeWidget 'end_time': DateTimeWidget
} }
@ -211,7 +199,7 @@ class NewPlaceForm(forms.ModelForm):
model = Place model = Place
fields = ['name', 'address', 'city', 'longitude', 'latitude', 'place_url', 'tz'] fields = ['name', 'address', 'city', 'longitude', 'latitude', 'place_url', 'tz']
widgets = { widgets = {
'city': Lookup(source='/api/cities/', label='name'), 'city': Lookup(source=City),
} }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -244,11 +232,11 @@ class SendNotificationsForm(forms.ModelForm):
} }
class SearchForm(forms.Form): class SearchForm(forms.Form):
city = forms.IntegerField(required=False, widget=Lookup(source='/api/cities/', label='name')) city = forms.IntegerField(required=False, widget=Lookup(source=City, label='name'))
distance = forms.IntegerField(label=_("Distance(km)"), required=True) distance = forms.IntegerField(label=_("Distance(km)"), required=True)
class Meta: class Meta:
widgets ={ widgets ={
'city': Lookup(source='/api/cities/', label='name'), 'city': Lookup(source=City, label='name'),
} }
class NewCommonEventForm(forms.ModelForm): class NewCommonEventForm(forms.ModelForm):
@ -272,10 +260,10 @@ class NewCommonEventForm(forms.ModelForm):
'tags', 'tags',
] ]
widgets ={ widgets ={
'country': Lookup(source='/api/countries/', label='name'), 'country': Lookup(source=Country, label='name'),
'spr': Lookup(source='/api/spr/', label='name'), 'spr': Lookup(source=SPR, label='name'),
'city': Lookup(source='/api/cities/', label='name'), 'city': Lookup(source=City, label='name'),
'place': Lookup(source='/api/places/', label='name'), 'place': Lookup(source=Place, label='name'),
'start_time': DateTimeWidget, 'start_time': DateTimeWidget,
'end_time': DateTimeWidget 'end_time': DateTimeWidget
} }

View file

@ -44,12 +44,14 @@ class Country(models.Model):
return 'no_country' return 'no_country'
class CountrySerializer(serializers.ModelSerializer): class CountrySerializer(serializers.ModelSerializer):
display = serializers.CharField(source='__str__', read_only=True)
class Meta: class Meta:
model = Country model = Country
fields = ( fields = (
'id', 'id',
'name', 'name',
'code' 'code',
'display',
) )
class SPR(models.Model): class SPR(models.Model):
@ -94,6 +96,13 @@ class City(models.Model):
longitude = models.FloatField(help_text=_('Longitude in Degrees East'), null=True, blank=True) longitude = models.FloatField(help_text=_('Longitude in Degrees East'), null=True, blank=True)
latitude = models.FloatField(help_text=_('Latitude in Degrees North'), null=True, blank=True) latitude = models.FloatField(help_text=_('Latitude in Degrees North'), null=True, blank=True)
@property
def short_name(self):
if self.spr.country.name == 'United States':
return u'%s, %s' % (self.name, self.spr.name)
else:
return u'%s, %s' % (self.name, self.spr.country.name)
def __str__(self): def __str__(self):
return u'%s, %s, %s' % (self.name, self.spr.name, self.spr.country.name) return u'%s, %s, %s' % (self.name, self.spr.name, self.spr.country.name)
@ -112,6 +121,7 @@ class CitySerializer(serializers.ModelSerializer):
fields = ( fields = (
'id', 'id',
'name', 'name',
'short_name',
'spr', 'spr',
'tz', 'tz',
'slug', 'slug',

View file

@ -1,4 +1,6 @@
<select autocomplete="false" id="{{ widget.name }}_select" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} style="width: 200px"> <span class="gt_lookup">
{{ widget.value }} <select autocomplete="false" id="{{ widget.name }}_select" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} >
{{ widget.value }}
</select> </select>
</span>

View file

@ -0,0 +1,40 @@
body {
padding-top: 5rem;
}
.footer {
padding-top: 1em;
padding-bottom: 1em;
border-top: #cfcfcf 1px solid;
background-color: #f5f5f5;
color: #aaaaaa;
}
.footer ul {
display: inline;
list-style-type: none;
margin-left: 0.5em;
}
.footer ul li {
display: inline;
margin-left: 0.5em;
}
.footer a {
text-decoration: none;
color: inherit;
}
.starter-template {
padding: 3rem 1.5rem;
text-align: center;
}
form {
display: inline;
}
.ui-selectmenu-menu .ui-menu {
height: 200px;
}
.gt_lookup .ui-selectmenu-icon.ui-icon {
background-position: 0px 0px;
background-image: url(/static/img/search_icon.png);
width: 16px;
height: 16px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

View file

@ -22,7 +22,7 @@
_drawSearch: function() { _drawSearch: function() {
var self = this; var self = this;
var selectField = this.element[0]; var selectField = this.element[0];
this.searchField = $('<input type="text" placeholder="Search" style="width: 100%;">'); this.searchField = $('<input type="text" class="ui-lookup-search border border-primary" placeholder="Search" style="width: 100%;">');
//this._addClass( this.searchField, "ui-selectmenu-menu", "ui-front" ); //this._addClass( this.searchField, "ui-selectmenu-menu", "ui-front" );
this.searchField.keyup(function() { this.searchField.keyup(function() {
self.options.search(this.value, function(searchText, results){ self.options.search(this.value, function(searchText, results){
@ -39,8 +39,11 @@
}); });
}); });
this.menuWrap.prepend(this.searchField); this.menuWrap.prepend(this.searchField);
},
open: function(event) {
this._super()
this.searchField.focus()
} }
}); });
}(jQuery)); }(jQuery));

View file

@ -28,43 +28,11 @@
<link rel="stylesheet" href="{% static 'css/bootstrap/css/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'css/bootstrap/css/bootstrap.min.css' %}">
<link href="{% static 'js/tether/css/tether.min.css' %}" rel="stylesheet"> <link href="{% static 'js/tether/css/tether.min.css' %}" rel="stylesheet">
<link href="{% static 'js/jquery-ui/jquery-ui.min.css' %}" rel="stylesheet"> <link href="{% static 'js/jquery-ui/jquery-ui.min.css' %}" rel="stylesheet">
<link href="{% static 'css/get_together.css' %}" rel="stylesheet">
<!-- style overrides -->
{%block styles %}{% endblock %} {%block styles %}{% endblock %}
<!-- Bootstrap style overrides -->
<style> <style>
body { </style>
padding-top: 5rem;
}
.footer {
padding-top: 1em;
padding-bottom: 1em;
border-top: #cfcfcf 1px solid;
background-color: #f5f5f5;
color: #aaaaaa;
}
.footer ul {
display: inline;
list-style-type: none;
margin-left: 0.5em;
}
.footer ul li {
display: inline;
margin-left: 0.5em;
}
.footer a {
text-decoration: none;
color: inherit;
}
.starter-template {
padding: 3rem 1.5rem;
text-align: center;
}
form {
display: inline;
}
.ui-selectmenu-menu .ui-menu {
height: 200px;
}
</style>
</head> </head>
<body> <body>

View file

@ -43,6 +43,7 @@ def home(request, *args, **kwards):
near_distance = int(request.GET.get("distance", DEFAULT_NEAR_DISTANCE)) near_distance = int(request.GET.get("distance", DEFAULT_NEAR_DISTANCE))
context['distance'] = near_distance context['distance'] = near_distance
city=None
ll = None ll = None
if "city" in request.GET and request.GET.get("city"): if "city" in request.GET and request.GET.get("city"):
context['city_search'] = True context['city_search'] = True
@ -56,6 +57,22 @@ def home(request, *args, **kwards):
if g.latlng is not None and g.latlng[0] is not None and g.latlng[1] is not None: if g.latlng is not None and g.latlng[0] is not None and g.latlng[1] is not None:
ll = g.latlng ll = g.latlng
context['geoip_lookup'] = True context['geoip_lookup'] = True
try:
city_distance = 1 #km
while city is None and city_distance < 100:
minlat = ll[0]-(city_distance/KM_PER_DEGREE_LAT)
maxlat = ll[0]+(city_distance/KM_PER_DEGREE_LAT)
minlng = ll[1]-(city_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(ll[0]))))
maxlng = ll[1]+(city_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(ll[0]))))
nearby_cities = City.objects.filter(latitude__gte=minlat, latitude__lte=maxlat, longitude__gte=minlng, longitude__lte=maxlng)
if len(nearby_cities) == 0:
city_distance += 1
else:
city = nearby_cities[0]
except:
pass # City lookup failed
except Exception as err: except Exception as err:
context['geoip_lookup'] = False context['geoip_lookup'] = False
print("Geocoder lookup failed for %s" % client_ip, err) print("Geocoder lookup failed for %s" % client_ip, err)
@ -84,7 +101,7 @@ def home(request, *args, **kwards):
print("Error looking up nearby teams and events", err) print("Error looking up nearby teams and events", err)
traceback.print_exc() traceback.print_exc()
search_form = SearchForm(initial={'distance': near_distance}) search_form = SearchForm(initial={'city': city.id, 'distance': near_distance})
context['search_form'] = search_form context['search_form'] = search_form
return render(request, 'get_together/index.html', context) return render(request, 'get_together/index.html', context)