coverage and profiling
This commit is contained in:
parent
c99c1d52b5
commit
f43818315e
|
@ -74,7 +74,7 @@ class User(db.Model):
|
||||||
def followed_posts(self):
|
def followed_posts(self):
|
||||||
return Post.query.join(followers, (followers.c.followed_id == Post.user_id)).filter(followers.c.follower_id == self.id).order_by(Post.timestamp.desc())
|
return Post.query.join(followers, (followers.c.followed_id == Post.user_id)).filter(followers.c.follower_id == self.id).order_by(Post.timestamp.desc())
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self): # pragma: no cover
|
||||||
return '<User %r>' % (self.nickname)
|
return '<User %r>' % (self.nickname)
|
||||||
|
|
||||||
class Post(db.Model):
|
class Post(db.Model):
|
||||||
|
@ -86,7 +86,7 @@ class Post(db.Model):
|
||||||
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))
|
language = db.Column(db.String(5))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self): # pragma: no cover
|
||||||
return '<Post %r>' % (self.body)
|
return '<Post %r>' % (self.body)
|
||||||
|
|
||||||
whooshalchemy.whoosh_index(app, Post)
|
whooshalchemy.whoosh_index(app, Post)
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
<img id="loading{{post.id}}" style="display: none" src="/static/img/loading.gif">
|
<img id="loading{{post.id}}" style="display: none" src="/static/img/loading.gif">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if post.author.id == g.user.id %}
|
||||||
|
<div><a href="{{ url_for('delete', id = post.id) }}">{{ _('Delete') }}</a></div>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -7,8 +7,8 @@ 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: 2013-02-19 22:11-0800\n"
|
"POT-Creation-Date: 2013-03-09 20:29-0800\n"
|
||||||
"PO-Revision-Date: 2013-02-19 22:12-0800\n"
|
"PO-Revision-Date: 2013-03-09 20:29-0800\n"
|
||||||
"Last-Translator: Miguel Grinberg <miguel.grinberg@gmail.com>\n"
|
"Last-Translator: Miguel Grinberg <miguel.grinberg@gmail.com>\n"
|
||||||
"Language-Team: es <LL@li.org>\n"
|
"Language-Team: es <LL@li.org>\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
@ -38,55 +38,55 @@ msgstr "Este nombre de usuario ya esta usado. Por favor elije otro."
|
||||||
msgid "Error: translation service not configured."
|
msgid "Error: translation service not configured."
|
||||||
msgstr "Error: el servicio de traducción no está configurado."
|
msgstr "Error: el servicio de traducción no está configurado."
|
||||||
|
|
||||||
#: app/translate.py:35 app/translate.py:55
|
#: app/translate.py:40
|
||||||
msgid "Error: Unexpected error."
|
|
||||||
msgstr "Error: Un error inesperado ha ocurrido."
|
|
||||||
|
|
||||||
#: app/translate.py:39
|
|
||||||
msgid "Error: translation service not available."
|
msgid "Error: translation service not available."
|
||||||
msgstr "Error: servicio de traducción no disponible."
|
msgstr "Error: servicio de traducción no disponible."
|
||||||
|
|
||||||
#: app/views.py:56
|
#: app/translate.py:56
|
||||||
|
msgid "Error: Unexpected error."
|
||||||
|
msgstr "Error: Un error inesperado ha ocurrido."
|
||||||
|
|
||||||
|
#: app/views.py:64
|
||||||
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/views.py:81
|
#: app/views.py:89
|
||||||
msgid "Invalid login. Please try again."
|
msgid "Invalid login. Please try again."
|
||||||
msgstr "Credenciales inválidas. Por favor intenta de nuevo."
|
msgstr "Credenciales inválidas. Por favor intenta de nuevo."
|
||||||
|
|
||||||
#: app/views.py:114
|
#: app/views.py:122
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User %(nickname)s not found."
|
msgid "User %(nickname)s not found."
|
||||||
msgstr "El usuario %(nickname)s no existe."
|
msgstr "El usuario %(nickname)s no existe."
|
||||||
|
|
||||||
#: app/views.py:130
|
#: app/views.py:138
|
||||||
msgid "Your changes have been saved."
|
msgid "Your changes have been saved."
|
||||||
msgstr "Tus cambios han sido guardados."
|
msgstr "Tus cambios han sido guardados."
|
||||||
|
|
||||||
#: app/views.py:146
|
#: app/views.py:154
|
||||||
msgid "You can't follow yourself!"
|
msgid "You can't follow yourself!"
|
||||||
msgstr "¡No te puedes seguir a tí mismo!"
|
msgstr "¡No te puedes seguir a tí mismo!"
|
||||||
|
|
||||||
#: app/views.py:150
|
#: app/views.py:158
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Cannot follow %(nickname)s."
|
msgid "Cannot follow %(nickname)s."
|
||||||
msgstr "No se pudo seguir a %(nickname)s."
|
msgstr "No se pudo seguir a %(nickname)s."
|
||||||
|
|
||||||
#: app/views.py:154
|
#: app/views.py:162
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You are now following %(nickname)s!"
|
msgid "You are now following %(nickname)s!"
|
||||||
msgstr "¡Ya estás siguiendo a %(nickname)s!"
|
msgstr "¡Ya estás siguiendo a %(nickname)s!"
|
||||||
|
|
||||||
#: app/views.py:166
|
#: app/views.py:174
|
||||||
msgid "You can't unfollow yourself!"
|
msgid "You can't unfollow yourself!"
|
||||||
msgstr "¡No te puedes dejar de seguir a tí mismo!"
|
msgstr "¡No te puedes dejar de seguir a tí mismo!"
|
||||||
|
|
||||||
#: app/views.py:170
|
#: app/views.py:178
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Cannot unfollow %(nickname)s."
|
msgid "Cannot unfollow %(nickname)s."
|
||||||
msgstr "No se pudo dejar de seguir a %(nickname)s."
|
msgstr "No se pudo dejar de seguir a %(nickname)s."
|
||||||
|
|
||||||
#: app/views.py:174
|
#: app/views.py:182
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You have stopped following %(nickname)s."
|
msgid "You have stopped following %(nickname)s."
|
||||||
msgstr "Ya no sigues más a %(nickname)s."
|
msgstr "Ya no sigues más a %(nickname)s."
|
||||||
|
@ -208,6 +208,10 @@ msgstr "%(nickname)s dijo %(when)s:"
|
||||||
msgid "Translate"
|
msgid "Translate"
|
||||||
msgstr "Traducir"
|
msgstr "Traducir"
|
||||||
|
|
||||||
|
#: app/templates/post.html:18
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Borrar"
|
||||||
|
|
||||||
#: app/templates/search_results.html:5
|
#: app/templates/search_results.html:5
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Search results for \"%(query)s\":"
|
msgid "Search results for \"%(query)s\":"
|
||||||
|
|
25
app/views.py
25
app/views.py
|
@ -1,5 +1,6 @@
|
||||||
from flask import render_template, flash, redirect, session, url_for, request, g, jsonify
|
from flask import render_template, flash, redirect, session, url_for, request, g, jsonify
|
||||||
from flask.ext.login import login_user, logout_user, current_user, login_required
|
from flask.ext.login import login_user, logout_user, current_user, login_required
|
||||||
|
from flask.ext.sqlalchemy import get_debug_queries
|
||||||
from flask.ext.babel import gettext
|
from flask.ext.babel import gettext
|
||||||
from app import app, db, lm, oid, babel
|
from app import app, db, lm, oid, babel
|
||||||
from forms import LoginForm, EditForm, PostForm, SearchForm
|
from forms import LoginForm, EditForm, PostForm, SearchForm
|
||||||
|
@ -8,7 +9,7 @@ from datetime import datetime
|
||||||
from emails import follower_notification
|
from emails import follower_notification
|
||||||
from guess_language import guessLanguage
|
from guess_language import guessLanguage
|
||||||
from translate import microsoft_translate
|
from translate import microsoft_translate
|
||||||
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES
|
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES, DATABASE_QUERY_TIMEOUT
|
||||||
|
|
||||||
@lm.user_loader
|
@lm.user_loader
|
||||||
def load_user(id):
|
def load_user(id):
|
||||||
|
@ -28,6 +29,13 @@ def before_request():
|
||||||
g.search_form = SearchForm()
|
g.search_form = SearchForm()
|
||||||
g.locale = get_locale()
|
g.locale = get_locale()
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def after_request(response):
|
||||||
|
for query in get_debug_queries():
|
||||||
|
if query.duration >= DATABASE_QUERY_TIMEOUT:
|
||||||
|
app.logger.warning("SLOW QUERY: %s\nParameters: %s\nDuration: %fs\nContext: %s\n" % (query.statement, query.parameters, query.duration, query.context))
|
||||||
|
return response
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def internal_error(error):
|
def internal_error(error):
|
||||||
return render_template('404.html'), 404
|
return render_template('404.html'), 404
|
||||||
|
@ -174,6 +182,21 @@ def unfollow(nickname):
|
||||||
flash(gettext('You have stopped following %(nickname)s.', nickname = nickname))
|
flash(gettext('You have stopped following %(nickname)s.', nickname = nickname))
|
||||||
return redirect(url_for('user', nickname = nickname))
|
return redirect(url_for('user', nickname = nickname))
|
||||||
|
|
||||||
|
@app.route('/delete/<int:id>')
|
||||||
|
@login_required
|
||||||
|
def delete(id):
|
||||||
|
post = Post.query.get(id)
|
||||||
|
if post == None:
|
||||||
|
flash('Post not found.')
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
if post.author.id != g.user.id:
|
||||||
|
flash('You cannot delete this post.')
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
db.session.delete(post)
|
||||||
|
db.session.commit()
|
||||||
|
flash('Your post has been deleted.')
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
@app.route('/search', methods = ['POST'])
|
@app.route('/search', methods = ['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def search():
|
def search():
|
||||||
|
|
|
@ -14,8 +14,12 @@ OPENID_PROVIDERS = [
|
||||||
|
|
||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
|
||||||
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
||||||
|
SQLALCHEMY_RECORD_QUERIES = True
|
||||||
WHOOSH_BASE = os.path.join(basedir, 'search.db')
|
WHOOSH_BASE = os.path.join(basedir, 'search.db')
|
||||||
|
|
||||||
|
# slow database query threshold (in seconds)
|
||||||
|
DATABASE_QUERY_TIMEOUT = 0.5
|
||||||
|
|
||||||
# email server
|
# email server
|
||||||
MAIL_SERVER = 'your.mailserver.com'
|
MAIL_SERVER = 'your.mailserver.com'
|
||||||
MAIL_PORT = 25
|
MAIL_PORT = 25
|
||||||
|
@ -38,5 +42,5 @@ MS_TRANSLATOR_CLIENT_SECRET = '' # enter your MS translator app secret here
|
||||||
ADMINS = ['you@example.com']
|
ADMINS = ['you@example.com']
|
||||||
|
|
||||||
# pagination
|
# pagination
|
||||||
POSTS_PER_PAGE = 3
|
POSTS_PER_PAGE = 50
|
||||||
MAX_SEARCH_RESULTS = 50
|
MAX_SEARCH_RESULTS = 50
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!flask/bin/python
|
||||||
|
from werkzeug.contrib.profiler import ProfilerMiddleware
|
||||||
|
from app import app
|
||||||
|
|
||||||
|
app.config['PROFILE'] = True
|
||||||
|
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [30])
|
||||||
|
app.run(debug = True)
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -20,3 +20,4 @@ subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-wtf'])
|
||||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-babel'])
|
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-babel'])
|
||||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'guess-language'])
|
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'guess-language'])
|
||||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flup'])
|
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flup'])
|
||||||
|
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'coverage'])
|
||||||
|
|
60
tests.py
60
tests.py
|
@ -1,6 +1,10 @@
|
||||||
#!flask/bin/python
|
#!flask/bin/python
|
||||||
# -*- coding: utf8 -*-
|
# -*- coding: utf8 -*-
|
||||||
|
|
||||||
|
from coverage import coverage
|
||||||
|
cov = coverage(branch = True, omit = ['flask/*', 'tests.py'])
|
||||||
|
cov.start()
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
@ -21,6 +25,21 @@ class TestCase(unittest.TestCase):
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
|
|
||||||
|
def test_user(self):
|
||||||
|
# make valid nicknames
|
||||||
|
n = User.make_valid_nickname('John_123')
|
||||||
|
assert n == 'John_123'
|
||||||
|
n = User.make_valid_nickname('John_[123]\n')
|
||||||
|
assert n == 'John_123'
|
||||||
|
# create a user
|
||||||
|
u = User(nickname = 'john', email = 'john@example.com')
|
||||||
|
db.session.add(u)
|
||||||
|
db.session.commit()
|
||||||
|
assert u.is_authenticated() == True
|
||||||
|
assert u.is_active() == True
|
||||||
|
assert u.is_anonymous() == False
|
||||||
|
assert u.id == int(u.get_id())
|
||||||
|
|
||||||
def test_avatar(self):
|
def test_avatar(self):
|
||||||
# create a user
|
# create a user
|
||||||
u = User(nickname = 'john', email = 'john@example.com')
|
u = User(nickname = 'john', email = 'john@example.com')
|
||||||
|
@ -33,6 +52,8 @@ class TestCase(unittest.TestCase):
|
||||||
u = User(nickname = 'john', email = 'john@example.com')
|
u = User(nickname = 'john', email = 'john@example.com')
|
||||||
db.session.add(u)
|
db.session.add(u)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
nickname = User.make_unique_nickname('susan')
|
||||||
|
assert nickname == 'susan'
|
||||||
nickname = User.make_unique_nickname('john')
|
nickname = User.make_unique_nickname('john')
|
||||||
assert nickname != 'john'
|
assert nickname != 'john'
|
||||||
# make another user with the new nickname
|
# make another user with the new nickname
|
||||||
|
@ -111,14 +132,43 @@ class TestCase(unittest.TestCase):
|
||||||
assert len(f2) == 2
|
assert len(f2) == 2
|
||||||
assert len(f3) == 2
|
assert len(f3) == 2
|
||||||
assert len(f4) == 1
|
assert len(f4) == 1
|
||||||
assert f1 == [p4, p2, p1]
|
assert f1[0].id == p4.id
|
||||||
assert f2 == [p3, p2]
|
assert f1[1].id == p2.id
|
||||||
assert f3 == [p4, p3]
|
assert f1[2].id == p1.id
|
||||||
assert f4 == [p4]
|
assert f2[0].id == p3.id
|
||||||
|
assert f2[1].id == p2.id
|
||||||
|
assert f3[0].id == p4.id
|
||||||
|
assert f3[1].id == p3.id
|
||||||
|
assert f4[0].id == p4.id
|
||||||
|
|
||||||
|
def test_delete_post(self):
|
||||||
|
# create a user and a post
|
||||||
|
u = User(nickname = 'john', email = 'john@example.com')
|
||||||
|
p = Post(body = 'test post', author = u, timestamp = datetime.utcnow())
|
||||||
|
db.session.add(u)
|
||||||
|
db.session.add(p)
|
||||||
|
db.session.commit()
|
||||||
|
# query the post and destroy the session
|
||||||
|
p = Post.query.get(1)
|
||||||
|
db.session.remove()
|
||||||
|
# delete the post using a new session
|
||||||
|
db.session = db.create_scoped_session()
|
||||||
|
db.session.delete(p)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
def test_translation(self):
|
def test_translation(self):
|
||||||
assert microsoft_translate(u'English', 'en', 'es') == u'Inglés'
|
assert microsoft_translate(u'English', 'en', 'es') == u'Inglés'
|
||||||
assert microsoft_translate(u'Español', 'es', 'en') == u'Spanish'
|
assert microsoft_translate(u'Español', 'es', 'en') == u'Spanish'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
try:
|
||||||
|
unittest.main()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
cov.stop()
|
||||||
|
cov.save()
|
||||||
|
print "\n\nCoverage Report:\n"
|
||||||
|
cov.report()
|
||||||
|
print "\nHTML version: " + os.path.join(basedir, "tmp/coverage/index.html")
|
||||||
|
cov.html_report(directory = 'tmp/coverage')
|
||||||
|
cov.erase()
|
||||||
|
|
Loading…
Reference in New Issue