Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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).

Tzu-ping Chung

June 03, 2016
Tweet

More Decks by Tzu-ping Chung

Other Decks in Programming

Transcript

  1. We Made the
    PyCon TW 2016 Website

    View full-size slide

  2. Not Web Lead

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. http://macdown.uranusjr.com

    View full-size slide

  6. PyCon APAC 2014
    PyCon APAC 2015

    View full-size slide

  7. PyCon TW 2016

    View full-size slide

  8. No More CMS
    • We are all programmers here
    • Redundant content separation
    • RDBS
    • Flat pages
    • Git is our content database

    View full-size slide

  9. 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

    View full-size slide

  10. Built to Last

    View full-size slide

  11. The web framework for perfectionists with deadlines.

    View full-size slide

  12. Third-party Apps
    • As few as possible
    • Django has more than you think
    • Easy to make simple solutions

    View full-size slide

  13. 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

    View full-size slide

  14. Test Coverage
    • I do hope it could be higher
    • Numbers can be misleading
    • Django is too declarative
    • Test the important parts first

    View full-size slide

  15. Testing
    • pytest-django
    • unittest.mock + pytest-mock
    • Travis CI
    • Codecov.io
    • Staging server (manual inspection)

    View full-size slide

  16. PYTEST IS AWESOME
    BECAUSE

    View full-size slide

  17. Content Management
    With Templates!

    View full-size slide

  18. # url(r'^(?P.*)/$', content_page)
    def content_page(request, path):
    template_path = '/'.join([
    CONTENT_TEMPLATE_PREFIX,
    request.LANGUAGE_CODE,
    path + '.html',
    ]
    return render(request, template_path)

    View full-size slide

  19. GET /events/keynotes/
    path = 'events/keynotes'
    template_path =
    'contents/events/keynotes.html'
    HttpResponse

    View full-size slide

  20. class TemplateExistanceStatusResponse(
    TemplateResponse):
    def resolve_template(self, template):
    try:
    return super().resolve_template(
    template=template,
    )
    except TemplateDoesNotExist:
    raise Http404

    View full-size slide

  21. Do It Yourself

    View full-size slide

  22. Markdown Support
    • You don’t need server-side rendering
    • One custom form widget is enough
    • Render with JavaScript

    View full-size slide

  23. 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(
    '\r\n'
    '{content}',
    attrs=flatatt(attrs),
    content=force_text(value),
    )

    View full-size slide

  24. 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.

    View full-size slide

  25. var nodeList = document.querySelector(
    'textarea[data-simplemde]');
    Array.prototype.forEach.call(nodeList, (e) => {
    new SimpleMDE({
    'element': e,
    // ... Whatever options you want.
    });
    });

    View full-size slide

  26. class EventInfo(models.Model):
    # ...
    detailed_description = models.TextField(
    verbose_name=_('detailed description'),
    blank=True,
    help_text=_(...),
    )
    # ...

    View full-size slide

  27. {% if object.detailed_description %}
    {% trans 'Description' %}


    {{ object.detailed_description }}


    {% endif %}

    View full-size slide

  28. 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);
    });

    View full-size slide

  29. Character counter
    • Why not CharField(max_length=...)
    • Character-word ratio
    • Front-end user experience

    View full-size slide

  30. East Asian Width
    • Unicode Standard Annex #11
    • “Wide” and “narrow” characters
    • And ambiguous (not going there)

    View full-size slide

  31. Ken Lunde
    㼭卌⶛
    https://www.linkedin.com/in/kenlunde

    View full-size slide

  32. _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
    )

    View full-size slide

  33. http://d.hatena.ne.jp/takenspc/20111126

    View full-size slide

  34. 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);
    }

    View full-size slide

  35. Email-Based User
    • No idea where this came from
    • A lot copied from Symposion
    • Some of our own touch

    View full-size slide

  36. Internalisation

    View full-size slide

  37. 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)),
    ]

    View full-size slide

  38. https://tw.pycon.org/2016/zh-hant/events/talks/
    Chinese (Traditional)
    Matched URL path

    View full-size slide

  39. # This is in i18n_patterns(...).
    # url(r'^(?P.*)/$', content_page)
    def content_page(request, path):
    template_path = '/'.join([
    CONTENT_TEMPLATE_PREFIX,
    request.LANGUAGE_CODE,
    path + '.html',
    ])
    return render(request, template_path)

    View full-size slide

  40. Does English need
    translation?

    View full-size slide

  41. Translation
    • en or en-us?
    • Non-ASCII characters
    • English is not the base language

    View full-size slide

  42. 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)',
    }

    View full-size slide

  43. Translations
    locale/
    !"" _src/
    # !"" LC_MESSAGES/
    # $"" README.txt
    !"" en_US/
    # $"" LC_MESSAGES
    $"" zh_Hant/
    $"" LC_MESSAGES

    View full-size slide

  44. https://www.transifex.com/pycon-taiwan/pycon-tw-2016/

    View full-size slide

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

    View full-size slide

  46. GitHub
    git push
    Travis CI
    Trigger
    master?
    Transifex
    tx push
    Manual
    Translation
    tx pull
    Local
    Repository
    Some Remote
    Repository
    git push [no-ci]

    View full-size slide

  47. U+1F937 SHRUG (Unicode 9.0)

    View full-size slide

  48. 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]/

    View full-size slide

  49. 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)

    View full-size slide

  50. # Settings.
    FALLBACK_LANS = {
    'zh': 'zh-hant',
    'en': 'en-us',
    }
    # For middleware.
    FALLBACK_PREFIX_PATTERN = re.compile(
    r'^/(?P{})(?:/?|/.+)$'.format(
    '|'.join(settings.FALLBACK_LANS.keys()),
    ),
    re.UNICODE,
    )

    View full-size slide

  51. Lessons Learnt

    View full-size slide

  52. If I Do This Again
    • Better code
    • Better dev-op
    • Better management

    View full-size slide

  53. 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!

    View full-size slide

  54. Better Dev-op
    • Transifex integration
    • Staging server for each branch
    • R/O production database mirroring?
    • Tighter production cycle

    View full-size slide

  55. 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?

    View full-size slide

  56. So… 2017?
    • Start as early as possible
    • Do things upfront
    • Repeat as little as possible

    View full-size slide

  57. Sponsorship?

    View full-size slide

  58. pip install conferencekit
    * Does not work yet

    View full-size slide

  59. ⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻
    ⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻⸈Ⰵ㉻

    View full-size slide