Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate chapters, languages and years #712

Merged
merged 12 commits into from
Apr 6, 2020
86 changes: 74 additions & 12 deletions src/config.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,93 @@
import hashlib
import json
import urllib.parse
import os

from language import Language, DEFAULT_LANGUAGE

SUPPORTED_YEARS = []
DEFAULT_YEAR = '2019'

config_json = {}

DEFAULT_AVATAR_FOLDER_PATH = '/static/images/avatars/'
AVATAR_SIZE = 200
AVATARS_NUMBER = 15

SUPPORTED_CHAPTERS = {}
SUPPORTED_LANGUAGES = {}

def get_config(year):
global config_json
return config_json[year] if year in config_json else None


def get_entries_from_json(json_config, p_key, s_key):
entries = []
if p_key in json_config:
for values in json_config.get(p_key):
entries.append(values.get(s_key))
return entries


def get_chapters(json_config):
chapters = []
data = get_entries_from_json(json_config,'outline','chapters')
for list in data:
for entry in list:
chapters.append(entry.get('slug'))
return chapters


def get_languages(json_config):
languages = []
data = get_entries_from_json(json_config,'settings','supported_languages')
for list in data:
for entry in list:
languages.append(getattr(Language,entry.upper()))
return languages


def get_live(json_config):
is_live = False
data = get_entries_from_json(json_config,'settings','isLive')
for list in data:
if list == True:
is_live = True
return is_live


def update_config():
global SUPPORTED_YEARS
global SUPPORTED_CHAPTERS
global SUPPORTED_LANGUAGES
global config_json

# TODO(rviscomi): Dynamically handle multiple annual configs.
with open('config/2019.json', 'r') as config_file:
config_json['2019'] = json.load(config_file)

for contributor_id, contributor in config_json['2019']['contributors'].items():
if 'avatar_url' not in contributor:
if 'gravatar' in contributor:
gravatar_url = 'https://www.gravatar.com/avatar/' + hashlib.md5(contributor['gravatar'].lower().encode()).hexdigest() + '.jpg?'
gravatar_url += urllib.parse.urlencode({'d': 'mp','s':str(AVATAR_SIZE)})
contributor['avatar_url'] = gravatar_url
else:
contributor['avatar_url'] = DEFAULT_AVATAR_FOLDER_PATH + str(hash(contributor_id) % AVATARS_NUMBER) + '.jpg'
config_files = []

for root, directories, files in os.walk('config'):
for file in files:
if '.json' in file:
config_files.append(file[0:4])

for year in config_files:
config_filename = 'config/%s.json' % year
with open(config_filename, 'r') as config_file:
json_config = json.load(config_file)
config_json[year] = json_config

if get_live(json_config):
SUPPORTED_YEARS.append(year)
SUPPORTED_LANGUAGES.update({year : get_languages(json_config)})
SUPPORTED_CHAPTERS.update({year : set(get_chapters(json_config))})

for contributor_id, contributor in json_config['contributors'].items():
if 'avatar_url' not in contributor:
if 'gravatar' in contributor:
gravatar_url = 'https://www.gravatar.com/avatar/' + hashlib.md5(contributor['gravatar'].lower().encode()).hexdigest() + '.jpg?'
gravatar_url += urllib.parse.urlencode({'d': 'mp','s':str(AVATAR_SIZE)})
contributor['avatar_url'] = gravatar_url
else:
contributor['avatar_url'] = DEFAULT_AVATAR_FOLDER_PATH + str(hash(contributor_id) % AVATARS_NUMBER) + '.jpg'

update_config()
6 changes: 6 additions & 0 deletions src/config/2019.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"settings": [
{
"isLive": true,
"supported_languages": ["en","es","fr","ja"]
}
],
"outline": [
{
"part": "I. Page Content",
Expand Down
10 changes: 5 additions & 5 deletions src/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ def lang_code(self):

# Currently we are only supporting languages and not regions
class Language(object):
JAPANESE = _Language('日本語', 'ja', 'JP')
ENGLISH = _Language('English', 'en', 'US')
SPANISH = _Language('Español', 'es', 'ES')
FRENCH = _Language('Français', 'fr', 'FR')
JA = _Language('日本語', 'ja', 'JP')
EN = _Language('English', 'en', 'US')
ES = _Language('Español', 'es', 'ES')
FR = _Language('Français', 'fr', 'FR')

DEFAULT_LANGUAGE = Language.ENGLISH
DEFAULT_LANGUAGE = Language.EN

# Maps language codes to _Language objects.
language_map = {v.lang_code: v for k,v in Language.__dict__.items() if k[:1] != '_'}
Expand Down
16 changes: 8 additions & 8 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import config as config_util
from config import get_config, SUPPORTED_YEARS, SUPPORTED_LANGUAGES, DEFAULT_YEAR
from csp import csp
from flask import Flask, abort, redirect, render_template as flask_render_template, request, send_from_directory, url_for
from flask_talisman import Talisman
from language import DEFAULT_LANGUAGE, get_language
import logging
import random
from werkzeug.routing import BaseConverter
from validate import validate, SUPPORTED_YEARS, DEFAULT_YEAR
from validate import validate
import os.path

# Set WOFF and WOFF2 caching to return 1 year as they should never change
Expand Down Expand Up @@ -41,7 +41,7 @@ def add_header(response):

def render_template(template, *args, **kwargs):
year = request.view_args.get('year', DEFAULT_YEAR)
supported_languages = SUPPORTED_YEARS.get(year, (DEFAULT_LANGUAGE,))
supported_languages = SUPPORTED_LANGUAGES.get(year, (DEFAULT_LANGUAGE,))

lang = request.view_args.get('lang') or ''
language = get_language(lang)
Expand All @@ -59,7 +59,7 @@ def render_template(template, *args, **kwargs):
if (os.path.isfile(langTemplate)):
template_supported_languages.append(l)

kwargs.update(supported_languages=template_supported_languages, year=year, lang=lang, language=language, supported_years=list(SUPPORTED_YEARS.keys()))
kwargs.update(supported_languages=template_supported_languages, year=year, lang=lang, language=language, supported_years=SUPPORTED_YEARS)
return flask_render_template(template, *args, **kwargs)


Expand Down Expand Up @@ -94,7 +94,7 @@ def convertOldImagePath(folder):
@app.route('/<lang>/<year>/')
@validate
def home(lang, year):
config = config_util.get_config(year)
config = get_config(year)
return render_template('%s/%s/index.html' % (lang, year), config=config)


Expand All @@ -113,14 +113,14 @@ def root(lang):
@app.route('/<lang>/<year>/table-of-contents')
@validate
def table_of_contents(lang, year):
config = config_util.get_config(year)
config = get_config(year)
return render_template('%s/%s/table_of_contents.html' % (lang, year), config=config)


@app.route('/<lang>/<year>/contributors')
@validate
def contributors(lang, year):
config = config_util.get_config(year)
config = get_config(year)
contributors = list(config["contributors"].items())
random.shuffle(contributors)
config["contributors"] = dict(contributors)
Expand Down Expand Up @@ -151,7 +151,7 @@ def sitemap():
@app.route('/<lang>/<year>/<chapter>')
@validate
def chapter(lang, year, chapter):
config = config_util.get_config(year)
config = get_config(year)
(prev_chapter, next_chapter) = get_chapter_nextprev(config, chapter)
return render_template('%s/%s/chapters/%s.html' % (lang, year, chapter), config=config, prev_chapter=prev_chapter, next_chapter=next_chapter)

Expand Down
42 changes: 6 additions & 36 deletions src/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,7 @@
from functools import wraps
from language import Language, DEFAULT_LANGUAGE

import config as config_util

DEFAULT_YEAR = '2019'
SUPPORTED_YEARS = {
# When there is one supported language, it must have a trailing comma.
'2019': (Language.ENGLISH,Language.FRENCH,Language.JAPANESE,Language.SPANISH)
}

#TO DO - Stop Hardcoding these
CHAPTERS = {
'accessibility',
'caching',
'cdn',
'cms',
'compression',
'css',
'ecommerce',
'fonts',
'javascript',
'http2',
'markup',
'media',
'page-weight',
'performance',
'pwa',
'resource-hints',
'seo',
'security',
'third-parties',
'media',
'mobile-web'
}
from config import SUPPORTED_YEARS, DEFAULT_YEAR, SUPPORTED_CHAPTERS, SUPPORTED_LANGUAGES

TYPO_CHAPTERS = {
'http-2': 'http2',
Expand All @@ -59,7 +28,7 @@ def decorated_function(*args, **kwargs):

if chapter:

validated_chapter = validate_chapter(chapter)
validated_chapter = validate_chapter(chapter,year)

if chapter != validated_chapter:
return redirect('/%s/%s/%s' % (lang, year, validated_chapter), code=301)
Expand All @@ -77,9 +46,10 @@ def decorated_function(*args, **kwargs):
return decorated_function


def validate_chapter(chapter):
def validate_chapter(chapter,year):

if chapter not in CHAPTERS:
chapters_for_year = SUPPORTED_CHAPTERS.get(year)
if chapter not in chapters_for_year:
if chapter in TYPO_CHAPTERS:
logging.debug('Typo chapter requested: %s, redirecting to %s' % (chapter, TYPO_CHAPTERS.get(chapter)))
return TYPO_CHAPTERS.get(chapter)
Expand All @@ -101,7 +71,7 @@ def validate_lang_and_year(lang, year):
logging.debug('Unsupported year requested: %s' % year)
abort(404, 'Unsupported year requested')

supported_langs = [l.lang_code for l in SUPPORTED_YEARS.get(year)]
supported_langs = [l.lang_code for l in SUPPORTED_LANGUAGES.get(year)]
logging.debug('Languages supported for %s: %s.' % (year, supported_langs))

# If an unsupported language code is passed in, abort.
Expand Down
7 changes: 4 additions & 3 deletions src/validate_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from config import DEFAULT_YEAR, SUPPORTED_YEARS
from language import Language, DEFAULT_LANGUAGE
from validate import parse_accept_language, DEFAULT_YEAR, SUPPORTED_YEARS
from validate import parse_accept_language

SUPPORTED_LANGUAGES = (Language.ENGLISH.lang_code, Language.JAPANESE.lang_code)
SUPPORTED_LANGUAGES = (Language.en.lang_code, Language.ja.lang_code)
DEFAULT_LANGUAGE_CODE = DEFAULT_LANGUAGE.lang_code
JAPANESE_LANGUAGE_CODE = Language.JAPANESE.lang_code
JAPANESE_LANGUAGE_CODE = Language.ja.lang_code


def assert_language(accept_language_header, expected_lang):
Expand Down