Chapter 14: Ajax (v0.14)
This commit is contained in:
		
							parent
							
								
									1b3a9a0338
								
							
						
					
					
						commit
						4fc04b6688
					
				| 
						 | 
				
			
			@ -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 '<Post {}>'.format(self.body)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'])})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 673 B  | 
| 
						 | 
				
			
			@ -14,7 +14,17 @@
 | 
			
		|||
                {{ _('%(username)s said %(when)s',
 | 
			
		||||
                    username=user_link, when=moment(post.timestamp).fromNow()) }}
 | 
			
		||||
                <br>
 | 
			
		||||
                {{ post.body }}
 | 
			
		||||
                <span id="post{{ post.id }}">{{ post.body }}</span>
 | 
			
		||||
                {% if post.language and post.language != g.locale %}
 | 
			
		||||
                <br><br>
 | 
			
		||||
                <span id="translation{{ post.id }}">
 | 
			
		||||
                    <a href="javascript:translate(
 | 
			
		||||
                                '#post{{ post.id }}',
 | 
			
		||||
                                '#translation{{ post.id }}',
 | 
			
		||||
                                '{{ post.language }}',
 | 
			
		||||
                                '{{ g.locale }}');">{{ _('Translate') }}</a>
 | 
			
		||||
                </span>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,4 +53,18 @@
 | 
			
		|||
    {{ super() }}
 | 
			
		||||
    {{ moment.include_moment() }}
 | 
			
		||||
    {{ moment.lang(g.locale) }}
 | 
			
		||||
    <script>
 | 
			
		||||
        function translate(sourceElem, destElem, sourceLang, destLang) {
 | 
			
		||||
            $(destElem).html('<img src="{{ url_for('static', filename='loading.gif') }}">');
 | 
			
		||||
            $.post('/translate', {
 | 
			
		||||
                text: $(sourceElem).text(),
 | 
			
		||||
                source_language: sourceLang,
 | 
			
		||||
                dest_language: destLang
 | 
			
		||||
            }).done(function(response) {
 | 
			
		||||
                $(destElem).text(response['text'])
 | 
			
		||||
            }).fail(function() {
 | 
			
		||||
                $(destElem).text("{{ _('Error: Could not contact server.') }}");
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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']
 | 
			
		||||
| 
						 | 
				
			
			@ -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 <EMAIL@ADDRESS>\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!"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 ###
 | 
			
		||||
		Loading…
	
		Reference in New Issue