Chapter 14: Ajax (v0.14)
This commit is contained in:
		
							parent
							
								
									2001337714
								
							
						
					
					
						commit
						bf464e18b1
					
				| 
						 | 
					@ -86,6 +86,7 @@ class Post(db.Model):
 | 
				
			||||||
    body = db.Column(db.String(140))
 | 
					    body = db.Column(db.String(140))
 | 
				
			||||||
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
 | 
					    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
 | 
				
			||||||
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
 | 
					    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
 | 
				
			||||||
 | 
					    language = db.Column(db.String(5))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return '<Post {}>'.format(self.body)
 | 
					        return '<Post {}>'.format(self.body)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,16 @@
 | 
				
			||||||
from datetime import datetime
 | 
					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 flask_login import login_user, logout_user, current_user, login_required
 | 
				
			||||||
from werkzeug.urls import url_parse
 | 
					from werkzeug.urls import url_parse
 | 
				
			||||||
from flask_babel import _, get_locale
 | 
					from flask_babel import _, get_locale
 | 
				
			||||||
 | 
					from guess_language import guess_language
 | 
				
			||||||
from app import app, db
 | 
					from app import app, db
 | 
				
			||||||
from app.forms import LoginForm, RegistrationForm, EditProfileForm, PostForm, \
 | 
					from app.forms import LoginForm, RegistrationForm, EditProfileForm, PostForm, \
 | 
				
			||||||
    ResetPasswordRequestForm, ResetPasswordForm
 | 
					    ResetPasswordRequestForm, ResetPasswordForm
 | 
				
			||||||
from app.models import User, Post
 | 
					from app.models import User, Post
 | 
				
			||||||
from app.email import send_password_reset_email
 | 
					from app.email import send_password_reset_email
 | 
				
			||||||
 | 
					from app.translate import translate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.before_request
 | 
					@app.before_request
 | 
				
			||||||
| 
						 | 
					@ -24,7 +27,11 @@ def before_request():
 | 
				
			||||||
def index():
 | 
					def index():
 | 
				
			||||||
    form = PostForm()
 | 
					    form = PostForm()
 | 
				
			||||||
    if form.validate_on_submit():
 | 
					    if form.validate_on_submit():
 | 
				
			||||||
        post = Post(body=form.post.data, author=current_user)
 | 
					        language = guess_language(form.post.data)
 | 
				
			||||||
 | 
					        if language == 'UNKNOWN' or len(language) > 5:
 | 
				
			||||||
 | 
					            language = ''
 | 
				
			||||||
 | 
					        post = Post(body=form.post.data, author=current_user,
 | 
				
			||||||
 | 
					                    language=language)
 | 
				
			||||||
        db.session.add(post)
 | 
					        db.session.add(post)
 | 
				
			||||||
        db.session.commit()
 | 
					        db.session.commit()
 | 
				
			||||||
        flash(_('Your post is now live!'))
 | 
					        flash(_('Your post is now live!'))
 | 
				
			||||||
| 
						 | 
					@ -189,3 +196,11 @@ def unfollow(username):
 | 
				
			||||||
    db.session.commit()
 | 
					    db.session.commit()
 | 
				
			||||||
    flash(_('You are not following %(username)s.', username=username))
 | 
					    flash(_('You are not following %(username)s.', username=username))
 | 
				
			||||||
    return redirect(url_for('user', username=username))
 | 
					    return redirect(url_for('user', username=username))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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)s said %(when)s',
 | 
				
			||||||
                    username=user_link, when=moment(post.timestamp).fromNow()) }}
 | 
					                    username=user_link, when=moment(post.timestamp).fromNow()) }}
 | 
				
			||||||
                <br>
 | 
					                <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>
 | 
					            </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
    </table>
 | 
					    </table>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,4 +53,18 @@
 | 
				
			||||||
    {{ super() }}
 | 
					    {{ super() }}
 | 
				
			||||||
    {{ moment.include_moment() }}
 | 
					    {{ moment.include_moment() }}
 | 
				
			||||||
    {{ moment.lang(g.locale) }}
 | 
					    {{ 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 %}
 | 
					{% endblock %}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					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']}
 | 
				
			||||||
 | 
					    r = requests.get('https://api.microsofttranslator.com/v2/Ajax.svc'
 | 
				
			||||||
 | 
					                     '/Translate?text={}&from={}&to={}'.format(
 | 
				
			||||||
 | 
					                         text, source_language, dest_language),
 | 
				
			||||||
 | 
					                     headers=auth)
 | 
				
			||||||
 | 
					    if r.status_code != 200:
 | 
				
			||||||
 | 
					        return _('Error: the translation service failed.')
 | 
				
			||||||
 | 
					    return json.loads(r.content.decode('utf-8-sig'))
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ msgid ""
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PROJECT VERSION\n"
 | 
					"Project-Id-Version: PROJECT VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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"
 | 
					"PO-Revision-Date: 2017-09-29 23:25-0700\n"
 | 
				
			||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
					"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
				
			||||||
"Language: es\n"
 | 
					"Language: es\n"
 | 
				
			||||||
| 
						 | 
					@ -82,57 +82,65 @@ msgstr "Dí algo"
 | 
				
			||||||
msgid "Search"
 | 
					msgid "Search"
 | 
				
			||||||
msgstr "Buscar"
 | 
					msgstr "Buscar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:30
 | 
					#: app/routes.py:37
 | 
				
			||||||
msgid "Your post is now live!"
 | 
					msgid "Your post is now live!"
 | 
				
			||||||
msgstr "¡Tu artículo ha sido publicado!"
 | 
					msgstr "¡Tu artículo ha sido publicado!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:66
 | 
					#: app/routes.py:73
 | 
				
			||||||
msgid "Invalid username or password"
 | 
					msgid "Invalid username or password"
 | 
				
			||||||
msgstr "Nombre de usuario o contraseña inválidos"
 | 
					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!"
 | 
					msgid "Congratulations, you are now a registered user!"
 | 
				
			||||||
msgstr "¡Felicitaciones, ya eres un usuario registrado!"
 | 
					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"
 | 
					msgid "Check your email for the instructions to reset your password"
 | 
				
			||||||
msgstr "Busca en tu email las instrucciones para crear una nueva contraseña"
 | 
					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."
 | 
					msgid "Your password has been reset."
 | 
				
			||||||
msgstr "Tu contraseña ha sido cambiada."
 | 
					msgstr "Tu contraseña ha sido cambiada."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:152
 | 
					#: app/routes.py:159
 | 
				
			||||||
msgid "Your changes have been saved."
 | 
					msgid "Your changes have been saved."
 | 
				
			||||||
msgstr "Tus cambios han sido salvados."
 | 
					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"
 | 
					msgid "Edit Profile"
 | 
				
			||||||
msgstr "Editar Perfil"
 | 
					msgstr "Editar Perfil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:166 app/routes.py:182
 | 
					#: app/routes.py:173 app/routes.py:189
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "User %(username)s not found."
 | 
					msgid "User %(username)s not found."
 | 
				
			||||||
msgstr "El usuario %(username)s no ha sido encontrado."
 | 
					msgstr "El usuario %(username)s no ha sido encontrado."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:169
 | 
					#: app/routes.py:176
 | 
				
			||||||
msgid "You cannot follow yourself!"
 | 
					msgid "You cannot follow yourself!"
 | 
				
			||||||
msgstr "¡No te puedes seguir a tí mismo!"
 | 
					msgstr "¡No te puedes seguir a tí mismo!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:173
 | 
					#: app/routes.py:180
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "You are following %(username)s!"
 | 
					msgid "You are following %(username)s!"
 | 
				
			||||||
msgstr "¡Ahora estás siguiendo a %(username)s!"
 | 
					msgstr "¡Ahora estás siguiendo a %(username)s!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:185
 | 
					#: app/routes.py:192
 | 
				
			||||||
msgid "You cannot unfollow yourself!"
 | 
					msgid "You cannot unfollow yourself!"
 | 
				
			||||||
msgstr "¡No te puedes dejar de seguir a tí mismo!"
 | 
					msgstr "¡No te puedes dejar de seguir a tí mismo!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/routes.py:189
 | 
					#: app/routes.py:196
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "You are not following %(username)s."
 | 
					msgid "You are not following %(username)s."
 | 
				
			||||||
msgstr "No estás siguiendo a %(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
 | 
					#: app/templates/404.html:4
 | 
				
			||||||
msgid "Not Found"
 | 
					msgid "Not Found"
 | 
				
			||||||
msgstr "Página No Encontrada"
 | 
					msgstr "Página No Encontrada"
 | 
				
			||||||
| 
						 | 
					@ -154,6 +162,10 @@ msgstr "El administrador ha sido notificado. ¡Lamentamos la inconveniencia!"
 | 
				
			||||||
msgid "%(username)s said %(when)s"
 | 
					msgid "%(username)s said %(when)s"
 | 
				
			||||||
msgstr "%(username)s dijo %(when)s"
 | 
					msgstr "%(username)s dijo %(when)s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: app/templates/_post.html:19
 | 
				
			||||||
 | 
					msgid "Translate"
 | 
				
			||||||
 | 
					msgstr "Traducir"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: app/templates/base.html:4
 | 
					#: app/templates/base.html:4
 | 
				
			||||||
msgid "Welcome to Microblog"
 | 
					msgid "Welcome to Microblog"
 | 
				
			||||||
msgstr "Bienvenido a Microblog"
 | 
					msgstr "Bienvenido a Microblog"
 | 
				
			||||||
| 
						 | 
					@ -178,6 +190,10 @@ msgstr "Perfil"
 | 
				
			||||||
msgid "Logout"
 | 
					msgid "Logout"
 | 
				
			||||||
msgstr "Salir"
 | 
					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
 | 
					#: app/templates/index.html:5
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "Hi, %(username)s!"
 | 
					msgid "Hi, %(username)s!"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,4 +14,5 @@ class Config(object):
 | 
				
			||||||
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
 | 
					    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
 | 
				
			||||||
    ADMINS = ['your-email@example.com']
 | 
					    ADMINS = ['your-email@example.com']
 | 
				
			||||||
    LANGUAGES = ['en', 'es']
 | 
					    LANGUAGES = ['en', 'es']
 | 
				
			||||||
 | 
					    MS_TRANSLATOR_KEY = os.environ.get('MS_TRANSLATOR_KEY')
 | 
				
			||||||
    POSTS_PER_PAGE = 25
 | 
					    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