From 7f7c8ef5d73083243e2656b5c28ebb54c2e13535 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Thu, 5 Oct 2017 15:34:15 -0700 Subject: [PATCH] Chapter 14: Ajax (v0.14) --- app/models.py | 1 + app/routes.py | 20 ++++++++- app/static/loading.gif | Bin 0 -> 673 bytes app/templates/_post.html | 12 ++++- app/templates/base.html | 14 ++++++ app/translate.py | 21 +++++++++ app/translations/es/LC_MESSAGES/messages.po | 42 ++++++++++++------ config.py | 1 + .../2b017edaa91f_add_language_to_posts.py | 28 ++++++++++++ 9 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 app/static/loading.gif create mode 100644 app/translate.py create mode 100644 migrations/versions/2b017edaa91f_add_language_to_posts.py diff --git a/app/models.py b/app/models.py index a0f68ae..a4db0be 100644 --- a/app/models.py +++ b/app/models.py @@ -86,6 +86,7 @@ class Post(db.Model): body = db.Column(db.String(140)) timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + language = db.Column(db.String(5)) def __repr__(self): return ''.format(self.body) diff --git a/app/routes.py b/app/routes.py index c4e9461..5f1c7a3 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,13 +1,16 @@ from datetime import datetime -from flask import render_template, flash, redirect, url_for, request, g +from flask import render_template, flash, redirect, url_for, request, g, \ + jsonify from flask_login import login_user, logout_user, current_user, login_required from werkzeug.urls import url_parse from flask_babel import _, get_locale +from langdetect import detect, LangDetectException from app import app, db from app.forms import LoginForm, RegistrationForm, EditProfileForm, \ EmptyForm, PostForm, ResetPasswordRequestForm, ResetPasswordForm from app.models import User, Post from app.email import send_password_reset_email +from app.translate import translate @app.before_request @@ -24,7 +27,12 @@ def before_request(): def index(): form = PostForm() if form.validate_on_submit(): - post = Post(body=form.post.data, author=current_user) + try: + language = detect(form.post.data) + except LangDetectException: + language = '' + post = Post(body=form.post.data, author=current_user, + language=language) db.session.add(post) db.session.commit() flash(_('Your post is now live!')) @@ -198,3 +206,11 @@ def unfollow(username): return redirect(url_for('user', username=username)) else: return redirect(url_for('index')) + + +@app.route('/translate', methods=['POST']) +@login_required +def translate_text(): + return jsonify({'text': translate(request.form['text'], + request.form['source_language'], + request.form['dest_language'])}) diff --git a/app/static/loading.gif b/app/static/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..d0bce1542342e912da81a2c260562df172f30d73 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{Lk&@8WQa67~pE8 zXTZz|lvDgC+Z`3#dv5h=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0kj3v921Z^em#l`(k(o@H$3ZdDRc@9NidXDNbqrumReCGv$gd8+e8WW28HVqkJ_9i zH>s*<31KtHjANIPvi2#*6BEu%3Dak5O_t&NBI)H?V$TxT}#l{vOTn5naXTfF^&~Hhq+NX@#Ccc>y7T?;vjI&jdhsDsPJyAw*m0Qz>i}K7# zL9w50Ng{fT}A5JUe8lRK1h7_Y2;BWJDd=c6f&i?Wv5(5q?6|P zQw{>maxZP<537OA37Uk}7@%_$4o$EWe_Zl>&#id|lE-BpDC#+Fn|msJ%_2h{Hg1vP z#N8WAzfWasG}yq|xqE)DrWaOofX=z|?*pgc%{ig5vl!pqDlC|q&~Z0$&Rvsft&VO- z4MZj+%-+Vx%W}v;V76hyp=;+R;x+~t^Q%*xuFTQAF2})fSfTHDAs>sO!OBw`)&)o$ c0!CNZt))x~rAZP^^P&YOFfdqy5)K#u0POD40{{R3 literal 0 HcmV?d00001 diff --git a/app/templates/_post.html b/app/templates/_post.html index 7e05990..1c13800 100644 --- a/app/templates/_post.html +++ b/app/templates/_post.html @@ -14,7 +14,17 @@ {{ _('%(username)s said %(when)s', username=user_link, when=moment(post.timestamp).fromNow()) }}
- {{ post.body }} + {{ post.body }} + {% if post.language and post.language != g.locale %} +

+ + {{ _('Translate') }} + + {% endif %} diff --git a/app/templates/base.html b/app/templates/base.html index 157e81e..9874556 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -53,4 +53,18 @@ {{ super() }} {{ moment.include_moment() }} {{ moment.lang(g.locale) }} + {% endblock %} diff --git a/app/translate.py b/app/translate.py new file mode 100644 index 0000000..d4a7998 --- /dev/null +++ b/app/translate.py @@ -0,0 +1,21 @@ +import json +import requests +from flask_babel import _ +from app import app + + +def translate(text, source_language, dest_language): + if 'MS_TRANSLATOR_KEY' not in app.config or \ + not app.config['MS_TRANSLATOR_KEY']: + return _('Error: the translation service is not configured.') + auth = { + 'Ocp-Apim-Subscription-Key': app.config['MS_TRANSLATOR_KEY'], + 'Ocp-Apim-Subscription-Region': 'westus2'} + r = requests.post( + 'https://api.cognitive.microsofttranslator.com' + '/translate?api-version=3.0&from={}&to={}'.format( + source_language, dest_language), headers=auth, json=[ + {'Text': text}]) + if r.status_code != 200: + return _('Error: the translation service failed.') + return r.json()[0]['translations'][0]['text'] diff --git a/app/translations/es/LC_MESSAGES/messages.po b/app/translations/es/LC_MESSAGES/messages.po index 2e7a00f..d468175 100644 --- a/app/translations/es/LC_MESSAGES/messages.po +++ b/app/translations/es/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2017-10-03 15:49-0700\n" +"POT-Creation-Date: 2017-10-05 15:32-0700\n" "PO-Revision-Date: 2017-09-29 23:25-0700\n" "Last-Translator: FULL NAME \n" "Language: es\n" @@ -82,57 +82,65 @@ msgstr "Dí algo" msgid "Search" msgstr "Buscar" -#: app/routes.py:30 +#: app/routes.py:37 msgid "Your post is now live!" msgstr "¡Tu artículo ha sido publicado!" -#: app/routes.py:66 +#: app/routes.py:73 msgid "Invalid username or password" msgstr "Nombre de usuario o contraseña inválidos" -#: app/routes.py:92 +#: app/routes.py:99 msgid "Congratulations, you are now a registered user!" msgstr "¡Felicitaciones, ya eres un usuario registrado!" -#: app/routes.py:107 +#: app/routes.py:114 msgid "Check your email for the instructions to reset your password" msgstr "Busca en tu email las instrucciones para crear una nueva contraseña" -#: app/routes.py:124 +#: app/routes.py:131 msgid "Your password has been reset." msgstr "Tu contraseña ha sido cambiada." -#: app/routes.py:152 +#: app/routes.py:159 msgid "Your changes have been saved." msgstr "Tus cambios han sido salvados." -#: app/routes.py:157 app/templates/edit_profile.html:5 +#: app/routes.py:164 app/templates/edit_profile.html:5 msgid "Edit Profile" msgstr "Editar Perfil" -#: app/routes.py:166 app/routes.py:182 +#: app/routes.py:173 app/routes.py:189 #, python-format msgid "User %(username)s not found." msgstr "El usuario %(username)s no ha sido encontrado." -#: app/routes.py:169 +#: app/routes.py:176 msgid "You cannot follow yourself!" msgstr "¡No te puedes seguir a tí mismo!" -#: app/routes.py:173 +#: app/routes.py:180 #, python-format msgid "You are following %(username)s!" msgstr "¡Ahora estás siguiendo a %(username)s!" -#: app/routes.py:185 +#: app/routes.py:192 msgid "You cannot unfollow yourself!" msgstr "¡No te puedes dejar de seguir a tí mismo!" -#: app/routes.py:189 +#: app/routes.py:196 #, python-format msgid "You are not following %(username)s." msgstr "No estás siguiendo a %(username)s." +#: app/translate.py:10 +msgid "Error: the translation service is not configured." +msgstr "Error: el servicio de traducciones no está configurado." + +#: app/translate.py:17 +msgid "Error: the translation service failed." +msgstr "Error el servicio de traducciones ha fallado." + #: app/templates/404.html:4 msgid "Not Found" msgstr "Página No Encontrada" @@ -154,6 +162,10 @@ msgstr "El administrador ha sido notificado. ¡Lamentamos la inconveniencia!" msgid "%(username)s said %(when)s" msgstr "%(username)s dijo %(when)s" +#: app/templates/_post.html:19 +msgid "Translate" +msgstr "Traducir" + #: app/templates/base.html:4 msgid "Welcome to Microblog" msgstr "Bienvenido a Microblog" @@ -178,6 +190,10 @@ msgstr "Perfil" msgid "Logout" msgstr "Salir" +#: app/templates/base.html:73 +msgid "Error: Could not contact server." +msgstr "Error: el servidor no pudo ser contactado." + #: app/templates/index.html:5 #, python-format msgid "Hi, %(username)s!" diff --git a/config.py b/config.py index 0e4b11b..880701f 100644 --- a/config.py +++ b/config.py @@ -14,4 +14,5 @@ class Config(object): MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') ADMINS = ['your-email@example.com'] LANGUAGES = ['en', 'es'] + MS_TRANSLATOR_KEY = os.environ.get('MS_TRANSLATOR_KEY') POSTS_PER_PAGE = 25 diff --git a/migrations/versions/2b017edaa91f_add_language_to_posts.py b/migrations/versions/2b017edaa91f_add_language_to_posts.py new file mode 100644 index 0000000..69daa02 --- /dev/null +++ b/migrations/versions/2b017edaa91f_add_language_to_posts.py @@ -0,0 +1,28 @@ +"""add language to posts + +Revision ID: 2b017edaa91f +Revises: ae346256b650 +Create Date: 2017-10-04 22:48:34.494465 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2b017edaa91f' +down_revision = 'ae346256b650' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('post', sa.Column('language', sa.String(length=5), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('post', 'language') + # ### end Alembic commands ###