We Made the PyCon TW 2016 Website

We Made the PyCon TW 2016 Website

We are the PyCon Taiwan 2016 Web Team. All your PyCon Taiwan website are belong to us.

You probably have seen our website. In this talk, I will outline how it’s built—with Django, and how we collaborated with other teams in the PyCon Taiwan staff.

The PyCon Taiwan 2016 website is made from countless little gems. I will talk about how we approach this task, from the ground up: How we chose from existing solutions, customised components in it, and built additional functionalities on top of it. The talk will include topics on registration handling, running common tasks with Django utilities, adding generic Python modules, and using tools to simplify collaboration.

Oh, and did you submit a proposal? I hope so—Our proposal system is awesome! I’ll also tell you how we implemented the proposal-review process (sharing some secrets in the process).

9dafad54b5b4f360b7aae5f482bc1c91?s=128

Tzu-ping Chung

June 03, 2016
Tweet

Transcript

  1. We Made the PyCon TW 2016 Website

  2. None
  3. None
  4. None
  5. None
  6. Web Lead

  7. Not Web Lead

  8. େਓతݪҼʁ (ʉ㦝 ʉ )

  9. Me • Call me TP • Follow @uranusjr • https://uranusjr.com

  10. None
  11. None
  12. http://macdown.uranusjr.com

  13. www. .com

  14. None
  15. PyCon APAC 2014 PyCon APAC 2015

  16. PyCon TW 2016

  17. No More CMS • We are all programmers here •

    Redundant content separation • RDBS • Flat pages • Git is our content database
  18. Make This the Last • Do we need a new

    site every year? • Less dependency = More time-proof • Did not make it — design mistakes :( • More on this later
  19. Built to Last

  20. Dependency

  21. The web framework for perfectionists with deadlines.

  22. Third-party Apps • As few as possible • Django has

    more than you think • Easy to make simple solutions
  23. Tests • We don’t have very good tests • Not

    enough team discipline • Unit tests on important components • Make sure we don’t serve errors • Important for maintenance
  24. Test Coverage • I do hope it could be higher

    • Numbers can be misleading • Django is too declarative • Test the important parts first
  25. Testing • pytest-django • unittest.mock + pytest-mock • Travis CI

    • Codecov.io • Staging server (manual inspection)
  26. PYTEST IS AWESOME BECAUSE

  27. None
  28. Content Management With Templates!

  29. # url(r'^(?P<path>.*)/$', content_page) def content_page(request, path): template_path = '/'.join([ CONTENT_TEMPLATE_PREFIX,

    request.LANGUAGE_CODE, path + '.html', ] return render(request, template_path)
  30. GET /events/keynotes/ path = 'events/keynotes' template_path = 'contents/events/keynotes.html' HttpResponse

  31. class TemplateExistanceStatusResponse( TemplateResponse): def resolve_template(self, template): try: return super().resolve_template( template=template,

    ) except TemplateDoesNotExist: raise Http404
  32. Do It Yourself

  33. Markdown Support • You don’t need server-side rendering • One

    custom form widget is enough • Render with JavaScript
  34. None
  35. class SimpleMDEWidget(forms.Textarea): class Media: css = {'all': ['simplemde.min.css']} js =

    ['simplemde.js', 'simplemde-setup.js'] def render(self, name, value, attrs=None): attrs = self.build_attrs(attrs, **{ 'name': name, 'data-simplemde': True, }) return format_html( '<textarea{attrs}>\r\n' '{content}</textarea>', attrs=flatatt(attrs), content=force_text(value), )
  36. <textarea cols="40" rows="10" id="id_xxx" name="xxx" data-simplemde> Rain artisanal range-rover girl

    wonton soup A.I. silent hacker sensory. Boy media advert systemic realism silent rain shoes range-rover receding plastic j-pop. Cartel Chiba tanto construct receding systemic saturation point market wristwatch stimulate pen jeans motion rifle. Car silent grenade tube meta-claymore mine tank- traps neural sign bicycle modem Shibuya media long-chain hydrocarbons.</textarea>
  37. var nodeList = document.querySelector( 'textarea[data-simplemde]'); Array.prototype.forEach.call(nodeList, (e) => { new

    SimpleMDE({ 'element': e, // ... Whatever options you want. }); });
  38. None
  39. class EventInfo(models.Model): # ... detailed_description = models.TextField( verbose_name=_('detailed description'), blank=True,

    help_text=_(...), ) # ...
  40. {% if object.detailed_description %} <h2>{% trans 'Description' %}</h2> <div class="editor-readonly">

    <div class="editor-preview"> {{ object.detailed_description }} </div> </div> {% endif %}
  41. var render = SimpleMDE.prototype.markdown; var els = document.querySelectorAll('.markdown'); Array.prototype.forEach.call(els, function

    (e) { var source = e.textContent || e.innerText; e.innerHTML = render(source); });
  42. Character counter • Why not CharField(max_length=...) • Character-word ratio •

    Front-end user experience
  43. East Asian Width • Unicode Standard Annex #11 • “Wide”

    and “narrow” characters • And ambiguous (not going there)
  44. Ken Lunde 㼭卌⶛ https://www.linkedin.com/in/kenlunde

  45. _EAW_LENS = { 'Na': 1, # Narrow - 1 'H':

    1, # Halfwidth - 1 'W': 2, # Wide - 2 'F': 2, # Fullwidth - 2 'N': 1, # Neutral - 1 'A': 2, # Ambiguous - 2 } from unicodedata import east_asian_width as eaw class EAWMaxLengthValidator(MaxLengthValidator): def clean(self, value): return sum( _EAW_LENS[eaw(c)] for c in value )
  46. http://d.hatena.ne.jp/takenspc/20111126

  47. function getCharacterCount(e) { var text = e.textContent || e.innerText; if

    (!text) { return 0; } text = text .replace(/^\s+|\s+$/g, '') .replace(/\r?\n/g, '\r\n')); return eastasianwidth.length(text); }
  48. Email-Based User • No idea where this came from •

    A lot copied from Symposion • Some of our own touch
  49. Internalisation

  50. from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( url(r'^$', index, name='index'),

    # ... more URL patterns. ) urlpatterns += [ url(r'^set-language/$', set_language), url(r'^admin/', include(admin.site.urls)), ]
  51. https://tw.pycon.org/2016/zh-hant/events/talks/ Chinese (Traditional) Matched URL path

  52. # This is in i18n_patterns(...). # url(r'^(?P<path>.*)/$', content_page) def content_page(request,

    path): template_path = '/'.join([ CONTENT_TEMPLATE_PREFIX, request.LANGUAGE_CODE, path + '.html', ]) return render(request, template_path)
  53. Does English need translation?

  54. Yes!

  55. Translation • en or en-us? • Non-ASCII characters • English

    is not the base language
  56. Patching Django from django.conf import locale if 'en-us' not in

    locale.LANG_INFO: locale.LANG_INFO['en-us'] = { 'bidi': False, 'code': 'en-us', 'name': 'English (US)', 'name_local': 'English (US)', }
  57. Translations locale/ !"" _src/ # !"" LC_MESSAGES/ # $"" README.txt

    !"" en_US/ # $"" LC_MESSAGES $"" zh_Hant/ $"" LC_MESSAGES
  58. https://www.transifex.com/pycon-taiwan/pycon-tw-2016/

  59. GitHub git push Travis CI Trigger master? Transifex tx push

    Manual Translation tx pull Local Repository
  60. GitHub git push Travis CI Trigger master? Transifex tx push

    Manual Translation tx pull Local Repository Some Remote Repository git push [no-ci]
  61. U+1F937 SHRUG (Unicode 9.0)

  62. Compatibility https://tw.pycon.org/2013/[en|ja|zh]/ https://tw.pycon.org/2014apac/[en|ja|zh]/ https://tw.pycon.org/2015apac/[en|ja|zh]/ https://tw.pycon.org/2016/[en-us|zh-hant]/

  63. class LocaleFallbackMiddleware: def process_request(self, request): if not settings.USE_I18N: return match

    = FALLBACK_PAT.match(request.path_info) if not match: return lang = match.group('lang') fallback = settings.FALLBACK_LANS[lang] prefix = get_script_prefix() path = request.get_full_path().replace( prefix + lang, prefix + fallback, 1, ) return HttpResponsePermanentRedirect(path)
  64. # Settings. FALLBACK_LANS = { 'zh': 'zh-hant', 'en': 'en-us', }

    # For middleware. FALLBACK_PREFIX_PATTERN = re.compile( r'^/(?P<lang>{})(?:/?|/.+)$'.format( '|'.join(settings.FALLBACK_LANS.keys()), ), re.UNICODE, )
  65. Lessons Learnt

  66. If I Do This Again • Better code • Better

    dev-op • Better management
  67. Better code • Plan to throw (at least) one away

    • Migration can be messy • Can’t revert design decisions • Coding styles • Indentation (size and style) • Translation unit • Tests! More! Better!
  68. Better Dev-op • Transifex integration • Staging server for each

    branch • R/O production database mirroring? • Tighter production cycle
  69. Better Management • Time is a big problem • Need

    better cohesion • Who is available? • What can I do now? • Tight development cycle without too much managing — How?
  70. Next Steps

  71. So… 2017? • Start as early as possible • Do

    things upfront • Repeat as little as possible
  72. Sponsorship?

  73. pip install conferencekit * Does not work yet

  74. ⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻ ⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻