├── __init__.py ├── mobile ├── __init__.py ├── templatetags │ ├── __init__.py │ ├── mobile_extras.py │ └── whitespaceoptimize.py ├── templates │ ├── 404.html │ ├── 500.html │ ├── club_class_day_map.html │ ├── home2.html │ ├── calendar.html │ ├── search.html │ ├── instructor.html │ ├── geomap.html │ ├── home.html │ ├── club_class_day.html │ ├── base.html │ └── club_page.html ├── context_processors.py ├── middleware.py ├── tests.py ├── geo.py ├── urls.py ├── test_views.py ├── feeds.py ├── models.py ├── ukpostcode.py └── views.py ├── pagination ├── __init__.py ├── models.py ├── templatetags │ ├── __init__.py │ └── pagination_tags.py ├── middleware.py ├── locale │ └── de │ │ └── LC_MESSAGES │ │ └── django.po ├── templates │ └── pagination │ │ └── pagination.html └── tests.py ├── README ├── .gitignore ├── media └── images │ └── favicon.ico ├── default_googlemaps_api.key ├── default_yahoo_api.key ├── manage.py ├── urls.py ├── profilingmiddleware.py ├── settings.py ├── _models.py └── slimmer.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mobile/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pagination/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pagination/models.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mobile/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /pagination/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | The Django code for http://m.fwckungfu.com 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | settings_local.py 2 | default_googlemaps_api.key 3 | tags 4 | .venv 5 | 6 | -------------------------------------------------------------------------------- /media/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbe/fwc_mobile/master/media/images/favicon.ico -------------------------------------------------------------------------------- /default_googlemaps_api.key: -------------------------------------------------------------------------------- 1 | ABQIAAAAnfs7bKE82qgb3Zc2YyS-oBT2yXp_ZAY8_ufC3CFXhHIE1NvwkxSySz_REpPq-4WZA27OwgbtyR3VcA -------------------------------------------------------------------------------- /default_yahoo_api.key: -------------------------------------------------------------------------------- 1 | u1SsEsLV34H1noY4L8gFtZ0yGHjjBkbmlELZfViUoPLwGqIUkHRbQ2dvsMDZTCOUN3J5cMiKfLdt18SGzjX3wg-- -------------------------------------------------------------------------------- /mobile/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block title %}Page not found{% endblock %} 5 | 6 | {% block main %} 7 | 8 |

Page not found :( 9 | 10 |

Here's a link to the homepage. You know, just in 11 | case.

12 | 13 | {% endblock %} -------------------------------------------------------------------------------- /mobile/context_processors.py: -------------------------------------------------------------------------------- 1 | def context(request): 2 | data = dict() 3 | 4 | user_agent = request.META.get('HTTP_USER_AGENT', '') 5 | 6 | data['iphone_version'] = False 7 | if user_agent.count('iPhone') and user_agent.count('AppleWebKit'): 8 | data['iphone_version'] = True 9 | 10 | 11 | return data -------------------------------------------------------------------------------- /mobile/middleware.py: -------------------------------------------------------------------------------- 1 | from django.utils.html import strip_spaces_between_tags as short 2 | 3 | 4 | ## http://www.davidcramer.net/code/369/spaceless-html-in-django.html 5 | class SpacelessMiddleware(object): 6 | def process_response(self, request, response): 7 | if 'text/html' in response['Content-Type']: 8 | response.content = short(response.content) 9 | return response -------------------------------------------------------------------------------- /mobile/tests.py: -------------------------------------------------------------------------------- 1 | # http://www.peterbe.com/plog/nasty-surprise-of-django-cache#c081210hpm1 2 | # By using locmem instead of whatever is in settings we can be certain that 3 | # the cache is reset when the test run because when you start the testrunner 4 | # you're starting a new python process and that's how locmem cache is reset 5 | from django.conf import settings 6 | settings.CACHE_BACKEND = 'locmem:///' 7 | 8 | from test_views import ViewsTestCase -------------------------------------------------------------------------------- /pagination/middleware.py: -------------------------------------------------------------------------------- 1 | class PaginationMiddleware(object): 2 | """ 3 | Inserts a variable representing the current page onto the request object if 4 | it exists in either **GET** or **POST** portions of the request. 5 | """ 6 | def process_request(self, request): 7 | try: 8 | request.page = int(request.REQUEST['page']) 9 | except (KeyError, ValueError, TypeError): 10 | request.page = 1 -------------------------------------------------------------------------------- /mobile/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page unavailable{% endblock %} 4 | 5 | {% block main %} 6 | 7 |

Page unavailable 8 | 9 |

We're sorry, but the requested page is currently unavailable.

10 | 11 |

We're messing around with things internally, and the server had 12 | a bit of a hiccup.

13 | 14 |

Please try again later.

15 | 16 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templatetags/mobile_extras.py: -------------------------------------------------------------------------------- 1 | # python 2 | import re 3 | 4 | # django 5 | from django import template 6 | from django.template.defaultfilters import stringfilter 7 | from django.utils.safestring import mark_safe 8 | 9 | # app 10 | 11 | # mandatory thing to have for magic to work 12 | register = template.Library() 13 | 14 | @register.filter() 15 | @stringfilter 16 | def tellink(value): 17 | value = value.strip().replace(' ','') 18 | if value.startswith('07'): 19 | value = '0044' + value[1:] 20 | return 'tel:%s' % value 21 | 22 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | try: 4 | import settings # Assumed to be in the same directory. 5 | except ImportError: 6 | import sys 7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 8 | sys.exit(1) 9 | 10 | if __name__ == "__main__": 11 | execute_manager(settings) 12 | -------------------------------------------------------------------------------- /mobile/geo.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | from geopy import geocoders 4 | 5 | class AddressNotFound(Exception): 6 | pass 7 | 8 | def geopy_geocode(address, google_key=settings.GOOGLEMAPS_API_KEY, 9 | domain='maps.google.co.uk', exactly_one=True): 10 | g = geocoders.Google(google_key, domain=domain) 11 | return g.geocode(address, exactly_one=exactly_one) 12 | 13 | def geopy_geocode_yahoo(address, yahoo_key=settings.YAHOO_API_KEY, 14 | exactly_one=True): 15 | g = geocoders.Yahoo(yahoo_key) 16 | return g.geocode(address, exactly_one=exactly_one) 17 | 18 | 19 | -------------------------------------------------------------------------------- /mobile/templates/club_class_day_map.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{{ club.name }} {{ day }} - {{ block.super }}{% endblock %} 3 | {% block header_title_outer %}

{{ club.name }} - {{ day }}

{% endblock %} 4 | 5 | {% block main %} 6 | 7 | 8 | {% if not first_class %} 9 |

Sorry, no class on this day.

10 | {% endif %} 11 | 12 | {% if google_maps_url %} 13 | 14 |
15 | {% if zoom_in_url %} 16 | zoom in +
17 | {% endif %} 18 | {% if zoom_out_url %} 19 | zoom out -
20 | {% endif %} 21 | {% else %} 22 |

Sorry, unable to connect to Google Maps at the moment

23 | {% endif %} 24 | 25 |

26 | Back to class and venue page
27 | Back to {{ club.name }} club page 28 |

29 | {% endblock %} -------------------------------------------------------------------------------- /pagination/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2008-07-28 13:26+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: templates/pagination/pagination.html:5 20 | #: templates/pagination/pagination.html:7 21 | msgid "previous" 22 | msgstr "zurück" 23 | 24 | #: templates/pagination/pagination.html:21 25 | #: templates/pagination/pagination.html:23 26 | msgid "next" 27 | msgstr "weiter" 28 | -------------------------------------------------------------------------------- /mobile/templates/home2.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load pagination_tags %} 3 | 4 | {% block main %} 5 | 6 | {% if last_visited_club %} 7 |
8 |
Last visited club:
9 |
{{ last_visited_club.name }}
10 | 11 | {% if last_visited_club_classes_today %} 12 |
Classes {{ tonight_or_today}}:
13 | {% for class in last_visited_club_classes_today %} 14 |
{{ class.style }}, {{ class.start_time }} - {{ class.end_time }}
15 | {% endfor %} 16 | {% endif %} 17 | 18 |
19 | {% endif %} 20 | 21 | 22 |

Clubs

23 | 24 |
25 | {% autopaginate clubs 5 %} 26 | {% for club in clubs %} 27 |
{{ club.name }}
28 |
{{ club.head_instructor.full_name }}
29 | {% endfor %} 30 |
31 | {% paginate %} 32 | 33 | 34 | {% endblock %} -------------------------------------------------------------------------------- /urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | import django.views.static 3 | 4 | from settings import MEDIA_ROOT 5 | 6 | # Uncomment the next two lines to enable the admin: 7 | # from django.contrib import admin 8 | # admin.autodiscover() 9 | 10 | urlpatterns = patterns('', 11 | # Example: 12 | 13 | # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 14 | # to INSTALLED_APPS to enable admin documentation: 15 | # (r'^admin/doc/', include('django.contrib.admindocs.urls')), 16 | 17 | # Uncomment the next line to enable the admin: 18 | # (r'^admin/(.*)', admin.site.root), 19 | 20 | (r'^images/(?P.*)$', django.views.static.serve, 21 | {'document_root': MEDIA_ROOT + '/images', 22 | 'show_indexes': True}), 23 | 24 | (r'^(?Pfavicon.ico)$', django.views.static.serve, 25 | {'document_root': MEDIA_ROOT + '/images', 26 | 'show_indexes': True}), 27 | 28 | (r'', include('fwc_mobile.mobile.urls')), 29 | 30 | ) 31 | -------------------------------------------------------------------------------- /pagination/templates/pagination/pagination.html: -------------------------------------------------------------------------------- 1 | {% if is_paginated %} 2 | {% load i18n %} 3 | 26 | {% endif %} 27 | -------------------------------------------------------------------------------- /mobile/templates/calendar.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block header_title_outer %}

{{ calendar_year }} Calendar

{% endblock %} 4 | 5 | {% block extrainlinecss %} 6 | div.event h4 { 7 | border-bottom:1px solid #F7A900; 8 | } 9 | {% endblock %} 10 | 11 | {% block main %} 12 | 13 | {% for eventmonth in eventmonths %} 14 |
15 |

{{ eventmonth.month }}

16 | {% for event in eventmonth.events %} 17 |      18 | 19 | {% if event.one_day %} 20 | {{ event.start_date|date:"d" }}      21 | {% else %} 22 | {{ event.start_date|date:"d" }}-{{ event.end_date|date:"d" }} 23 | {% endif %} 24 | 25 | {{ event.event }}
26 | {% endfor %} 27 |
28 | {% endfor %} 29 | 30 | {% if whole_year %} 31 |

Calendar for the rest of this year only

32 | {% else %} 33 |

34 | Calendar for whole of 35 | {% for year in whole_year_options %} 36 | {{ year }} 37 | {% endfor %} 38 |

39 | {% endif %} 40 | 41 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templates/search.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block header_title_outer %}

Search {% if searchresults %}results{% endif %}

{% endblock %} 4 | 5 | {% block extrainlinecss %} 6 | p.result a.title { font-weight:bold; } 7 | {% endblock %} 8 | 9 | {% block main %} 10 | 11 |
12 | Search: 13 | 14 |
15 | 16 | {% if searchresults %} 17 |

{{ count_results }} result{{ count_results|pluralize }}

18 | 19 | {% for result in searchresults %} 20 |

21 | {{ result.title }} ({{ result.type }})
22 | {% if result.description %} 23 | {{ result.description|safe }}
24 | {% endif %} 25 |

26 | {% endfor %} 27 | 28 | {% else %} 29 | {% if q %} 30 |

Sorry, nothing found.

31 | {% endif %} 32 | 33 | {% endif %} 34 | 35 | {% endblock %} 36 | 37 | {% block extrajs %} 38 | {% if q %} 39 | {% else %} 40 | 45 | {% endif %} 46 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templates/instructor.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load whitespaceoptimize %} 3 | {% block title %}Instructor {{ instructor.full_name }} - {{ block.super }}{% endblock %} 4 | {% block header_title_outer %}

Instructor {{ instructor.full_name }}

{% endblock %} 5 | {% load mobile_extras %} 6 | {% block extracss %} 7 | 11 | {% endblock %} 12 | 13 | 14 | {% block main %} 15 | 16 | {% whitespaceoptimize "html" %} 17 |

{{ instructor.full_name }}{#, {{ instructor.type }}#}

18 | 19 |
20 | 21 | {% if club %} 22 |
Club
23 |
{{ club.name }}
24 | {% else %} 25 |
Clubs
26 | {% for club in clubs %} 27 |
{{ club.name }}
28 | {% endfor %} 29 | 30 | {% endif %} 31 | 32 | {% if instructor.phone %} 33 |
Phone
34 |
{{ instructor.phone }}
35 | {#
{{ phone_formatted }}
#} 36 | {% endif %} 37 | 38 |
39 | {% endwhitespaceoptimize %} 40 | 41 |
{{ instructor.profile|safe }}
42 | 43 | 44 | 45 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templates/geomap.html: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | FWC Kung classes map 8 | 10 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /mobile/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block main %} 4 | {% if last_visited_club %} 5 |
6 |
Last visited club:
7 |
{{ last_visited_club.name }}
8 | 9 | {% if classes_today %} 10 | {% for each in classes_today %} 11 | 12 |
Classes {{ each.tonight_or_today}} at 13 | {% now "l" %} - {{ each.classes_today_venue }}:
14 | {% for class in each.classes_today %} 15 |
{{ class.style }}, 16 | {% if class.too_late_today %} 17 | {{ class.start_time }} - {{ class.end_time }} 18 | (already started) 19 | {% else %} 20 | {{ class.start_time }} - {{ class.end_time }} 21 | {% endif %} 22 | 23 |
24 | {% endfor %} 25 | 26 | {% endfor %} 27 | {% endif %} 28 | 29 |
30 | {% endif %} 31 | 32 | 33 | {% load cache %} 34 | {% cache 3600 template_home_clubs %} 35 | 36 |

Clubs

37 | 38 |
39 | {% for club in clubs %} 40 |
{{ club.name }}
41 |
{{ club.head_instructor.full_name }}
42 | {% endfor %} 43 |
44 | 45 | {% endcache %} 46 | 47 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templates/club_class_day.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{{ club.name }} {{ day }} - {{ block.super }}{% endblock %} 3 | {% block header_title_outer %}

{{ club.name }} - {{ day }}

{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 | {% if instructor %} 9 |
Instructor
10 |
{{ instructor.full_name }}
11 | {% endif %} 12 | 13 |
Classes
14 |
15 |
16 | {% for class in classes %} 17 |
{{class.style }}
18 | 19 |
{{ class.start_time }} - {{ class.end_time }}
20 | {% endfor %} 21 |
22 |
23 | 24 | {% if first_class %} 25 | 26 |
Address
27 |
28 | {{ first_class.address1 }}
29 | {{ first_class.address2 }}
30 | {{ first_class.address3 }}
31 | {{ first_class.address4 }} {{ first_class.address5 }}
32 | {% if map_link %} 33 | View on map
34 | {% endif %} 35 |
36 | 37 | {% if first_class.directions %} 38 |
Directions
39 |
{{ first_class.directions }}
40 | {% endif %} 41 | {% endif %} 42 | 43 |
44 | 45 | {% if not first_class %} 46 |

Sorry, no class on this day.

47 | {% endif %} 48 | 49 | 50 |

Back to {{ club.name }} club page

51 | {% endblock %} -------------------------------------------------------------------------------- /mobile/templatetags/whitespaceoptimize.py: -------------------------------------------------------------------------------- 1 | # python 2 | from slimmer import css_slimmer, guessSyntax, html_slimmer, js_slimmer 3 | 4 | # django 5 | from django import template 6 | 7 | class WhitespaceOptimizeNode(template.Node): 8 | def __init__(self, nodelist, format=None): 9 | self.nodelist = nodelist 10 | self.format = format 11 | def render(self, context): 12 | code = self.nodelist.render(context) 13 | if self.format == 'css': 14 | return css_slimmer(code) 15 | elif self.format in ('js', 'javascript'): 16 | return js_slimmer(code) 17 | elif self.format == 'html': 18 | return html_slimmer(code) 19 | else: 20 | format = guessSyntax(code) 21 | if format: 22 | self.format = format 23 | return self.render(context) 24 | 25 | return code 26 | 27 | 28 | register = template.Library() 29 | @register.tag(name='whitespaceoptimize') 30 | def do_whitespaceoptimize(parser, token): 31 | nodelist = parser.parse(('endwhitespaceoptimize',)) 32 | parser.delete_first_token() 33 | 34 | _split = token.split_contents() 35 | format = '' 36 | if len(_split) > 1: 37 | tag_name, format = _split 38 | if not (format[0] == format[-1] and format[0] in ('"', "'")): 39 | raise template.TemplateSyntaxError, \ 40 | "%r tag's argument should be in quotes" % tag_name 41 | 42 | return WhitespaceOptimizeNode(nodelist, format[1:-1]) 43 | 44 | -------------------------------------------------------------------------------- /pagination/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> from django.core.paginator import Paginator 3 | >>> from pagination.templatetags.pagination_tags import paginate 4 | >>> from django.template import Template, Context 5 | 6 | >>> p = Paginator(range(15), 2) 7 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 8 | [1, 2, 3, 4, 5, 6, 7, 8] 9 | 10 | >>> p = Paginator(range(17), 2) 11 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 12 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 13 | 14 | >>> p = Paginator(range(19), 2) 15 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 16 | [1, 2, 3, 4, None, 7, 8, 9, 10] 17 | 18 | >>> p = Paginator(range(21), 2) 19 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 20 | [1, 2, 3, 4, None, 8, 9, 10, 11] 21 | 22 | # Testing orphans 23 | >>> p = Paginator(range(5), 2, 1) 24 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 25 | [1, 2] 26 | 27 | >>> p = Paginator(range(21), 2, 1) 28 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 29 | [1, 2, 3, 4, None, 7, 8, 9, 10] 30 | 31 | >>> t = Template("{% load pagination_tags %}{% autopaginate var 2 %}{% paginate %}") 32 | 33 | # WARNING: Please, please nobody read this portion of the code! 34 | >>> class GetProxy(object): 35 | ... def __iter__(self): yield self.__dict__.__iter__ 36 | ... def copy(self): return self 37 | ... def urlencode(self): return u'' 38 | ... def keys(self): return [] 39 | >>> class RequestProxy(object): 40 | ... page = 1 41 | ... GET = GetProxy() 42 | >>> 43 | # ENDWARNING 44 | 45 | >>> t.render(Context({'var': range(21), 'request': RequestProxy()})) 46 | u'\\n\\n