python 3, pep8 and other various updates
This commit is contained in:
parent
6b193afe47
commit
02e93a9716
5
Procfile
5
Procfile
|
@ -1,4 +1,3 @@
|
|||
web: gunicorn runp-heroku:app
|
||||
init: python db_create.py && pybabel compile -d app/translations
|
||||
upgrade: python db_upgrade.py && pybabel compile -d app/translations
|
||||
|
||||
init: python db_create.py
|
||||
upgrade: python db_upgrade.py
|
||||
|
|
|
@ -6,11 +6,7 @@ A decently featured microblogging web application written in Python and Flask th
|
|||
Installation
|
||||
------------
|
||||
|
||||
The tutorial referenced above explains how to setup a virtual environment with all the required modules. As a convenience, the `setup.py` script will create this virtual environment for you. You can run this script again to refresh any missing modules. Note that to be able to run this script you have to have the following packages installed:
|
||||
|
||||
- Python 2.7
|
||||
- Python development package (`python-dev` for most Linux distributions)
|
||||
- git
|
||||
The tutorial referenced above explains how to setup a virtual environment with all the required modules.
|
||||
|
||||
The sqlite database must also be created before the application can run, and the `db_create.py` script takes care of that. See the [Database tutorial](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database) for the details.
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import os
|
||||
from flask import Flask
|
||||
from flask.json import JSONEncoder
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from flask.ext.login import LoginManager
|
||||
from flask.ext.openid import OpenID
|
||||
from flask.ext.mail import Mail
|
||||
from flask.ext.babel import Babel, lazy_gettext
|
||||
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
|
||||
from momentjs import momentjs
|
||||
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, \
|
||||
MAIL_PASSWORD
|
||||
from .momentjs import momentjs
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('config')
|
||||
|
@ -19,22 +21,41 @@ oid = OpenID(app, os.path.join(basedir, 'tmp'))
|
|||
mail = Mail(app)
|
||||
babel = Babel(app)
|
||||
|
||||
|
||||
class CustomJSONEncoder(JSONEncoder):
|
||||
"""This class adds support for lazy translation texts to Flask's
|
||||
JSON encoder. This is necessary when flashing translated texts."""
|
||||
def default(self, obj):
|
||||
from speaklater import is_lazy_string
|
||||
if is_lazy_string(obj):
|
||||
try:
|
||||
return unicode(obj) # python 2
|
||||
except NameError:
|
||||
return str(obj) # python 3
|
||||
return super(CustomJSONEncoder, self).default(obj)
|
||||
|
||||
app.json_encoder = CustomJSONEncoder
|
||||
|
||||
if not app.debug and MAIL_SERVER != '':
|
||||
import logging
|
||||
from logging.handlers import SMTPHandler
|
||||
credentials = None
|
||||
if MAIL_USERNAME or MAIL_PASSWORD:
|
||||
credentials = (MAIL_USERNAME, MAIL_PASSWORD)
|
||||
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
|
||||
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT),
|
||||
'no-reply@' + MAIL_SERVER, ADMINS,
|
||||
'microblog failure', credentials)
|
||||
mail_handler.setLevel(logging.ERROR)
|
||||
app.logger.addHandler(mail_handler)
|
||||
|
||||
if not app.debug and os.environ.get('HEROKU') is None:
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
|
||||
file_handler = RotatingFileHandler('tmp/microblog.log', 'a',
|
||||
1 * 1024 * 1024, 10)
|
||||
file_handler.setLevel(logging.INFO)
|
||||
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
||||
file_handler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('microblog startup')
|
||||
|
@ -49,4 +70,3 @@ if os.environ.get('HEROKU') is not None:
|
|||
app.jinja_env.globals['momentjs'] = momentjs
|
||||
|
||||
from app import views, models
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from threading import Thread
|
||||
|
||||
|
||||
def async(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
thr = Thread(target = f, args = args, kwargs = kwargs)
|
||||
thr = Thread(target=f, args=args, kwargs=kwargs)
|
||||
thr.start()
|
||||
return wrapper
|
||||
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
from flask import render_template
|
||||
from flask import render_template, current_app
|
||||
from flask.ext.mail import Message
|
||||
from app import mail
|
||||
from decorators import async
|
||||
from .decorators import async
|
||||
from config import ADMINS
|
||||
|
||||
|
||||
@async
|
||||
def send_async_email(msg):
|
||||
def send_async_email(app, msg):
|
||||
with app.app_context():
|
||||
mail.send(msg)
|
||||
|
||||
|
||||
def send_email(subject, sender, recipients, text_body, html_body):
|
||||
msg = Message(subject, sender = sender, recipients = recipients)
|
||||
msg = Message(subject, sender=sender, recipients=recipients)
|
||||
msg.body = text_body
|
||||
msg.html = html_body
|
||||
send_async_email(msg)
|
||||
#thr = threading.Thread(target = send_async_email, args = [msg])
|
||||
#thr.start()
|
||||
send_async_email(current_app, msg)
|
||||
|
||||
|
||||
def follower_notification(followed, follower):
|
||||
|
@ -22,7 +23,6 @@ def follower_notification(followed, follower):
|
|||
ADMINS[0],
|
||||
[followed.email],
|
||||
render_template("follower_email.txt",
|
||||
user = followed, follower = follower),
|
||||
user=followed, follower=follower),
|
||||
render_template("follower_email.html",
|
||||
user = followed, follower = follower))
|
||||
|
||||
user=followed, follower=follower))
|
||||
|
|
34
app/forms.py
34
app/forms.py
|
@ -1,16 +1,18 @@
|
|||
from flask.ext.wtf import Form
|
||||
from wtforms import TextField, BooleanField, TextAreaField
|
||||
from wtforms.validators import Required, Length
|
||||
from flask.ext.babel import gettext
|
||||
from app.models import User
|
||||
from wtforms import StringField, BooleanField, TextAreaField
|
||||
from wtforms.validators import DataRequired, Length
|
||||
from .models import User
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
openid = TextField('openid', validators = [Required()])
|
||||
remember_me = BooleanField('remember_me', default = False)
|
||||
openid = StringField('openid', validators=[DataRequired()])
|
||||
remember_me = BooleanField('remember_me', default=False)
|
||||
|
||||
|
||||
class EditForm(Form):
|
||||
nickname = TextField('nickname', validators = [Required()])
|
||||
about_me = TextAreaField('about_me', validators = [Length(min = 0, max = 140)])
|
||||
nickname = StringField('nickname', validators=[DataRequired()])
|
||||
about_me = TextAreaField('about_me', validators=[Length(min=0, max=140)])
|
||||
|
||||
def __init__(self, original_nickname, *args, **kwargs):
|
||||
Form.__init__(self, *args, **kwargs)
|
||||
|
@ -22,16 +24,22 @@ class EditForm(Form):
|
|||
if self.nickname.data == self.original_nickname:
|
||||
return True
|
||||
if self.nickname.data != User.make_valid_nickname(self.nickname.data):
|
||||
self.nickname.errors.append(gettext('This nickname has invalid characters. Please use letters, numbers, dots and underscores only.'))
|
||||
self.nickname.errors.append(gettext(
|
||||
'This nickname has invalid characters. '
|
||||
'Please use letters, numbers, dots and underscores only.'))
|
||||
return False
|
||||
user = User.query.filter_by(nickname = self.nickname.data).first()
|
||||
if user != None:
|
||||
self.nickname.errors.append(gettext('This nickname is already in use. Please choose another one.'))
|
||||
user = User.query.filter_by(nickname=self.nickname.data).first()
|
||||
if user is not None:
|
||||
self.nickname.errors.append(gettext(
|
||||
'This nickname is already in use. '
|
||||
'Please choose another one.'))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class PostForm(Form):
|
||||
post = TextField('post', validators = [Required()])
|
||||
post = StringField('post', validators=[DataRequired()])
|
||||
|
||||
|
||||
class SearchForm(Form):
|
||||
search = TextField('search', validators = [Required()])
|
||||
search = StringField('search', validators=[DataRequired()])
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
from hashlib import md5
|
||||
import re
|
||||
from app import db
|
||||
from app import app
|
||||
from config import WHOOSH_ENABLED
|
||||
import re
|
||||
|
||||
ROLE_USER = 0
|
||||
ROLE_ADMIN = 1
|
||||
import sys
|
||||
if sys.version_info >= (3, 0):
|
||||
enable_search = False
|
||||
else:
|
||||
enable_search = WHOOSH_ENABLED
|
||||
if enable_search:
|
||||
import flask.ext.whooshalchemy as whooshalchemy
|
||||
|
||||
followers = db.Table('followers',
|
||||
|
||||
followers = db.Table(
|
||||
'followers',
|
||||
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
|
||||
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
|
||||
)
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
nickname = db.Column(db.String(64), unique = True)
|
||||
email = db.Column(db.String(120), index = True, unique = True)
|
||||
role = db.Column(db.SmallInteger, default = ROLE_USER)
|
||||
posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
nickname = db.Column(db.String(64), index=True, unique=True)
|
||||
email = db.Column(db.String(120), index=True, unique=True)
|
||||
posts = db.relationship('Post', backref='author', lazy='dynamic')
|
||||
about_me = db.Column(db.String(140))
|
||||
last_seen = db.Column(db.DateTime)
|
||||
followed = db.relationship('User',
|
||||
secondary = followers,
|
||||
primaryjoin = (followers.c.follower_id == id),
|
||||
secondaryjoin = (followers.c.followed_id == id),
|
||||
backref = db.backref('followers', lazy = 'dynamic'),
|
||||
lazy = 'dynamic')
|
||||
secondary=followers,
|
||||
primaryjoin=(followers.c.follower_id == id),
|
||||
secondaryjoin=(followers.c.followed_id == id),
|
||||
backref=db.backref('followers', lazy='dynamic'),
|
||||
lazy='dynamic')
|
||||
|
||||
@staticmethod
|
||||
def make_valid_nickname(nickname):
|
||||
|
@ -33,12 +40,12 @@ class User(db.Model):
|
|||
|
||||
@staticmethod
|
||||
def make_unique_nickname(nickname):
|
||||
if User.query.filter_by(nickname = nickname).first() == None:
|
||||
if User.query.filter_by(nickname=nickname).first() is None:
|
||||
return nickname
|
||||
version = 2
|
||||
while True:
|
||||
new_nickname = nickname + str(version)
|
||||
if User.query.filter_by(nickname = new_nickname).first() == None:
|
||||
if User.query.filter_by(nickname=new_nickname).first() is None:
|
||||
break
|
||||
version += 1
|
||||
return new_nickname
|
||||
|
@ -53,10 +60,14 @@ class User(db.Model):
|
|||
return False
|
||||
|
||||
def get_id(self):
|
||||
return unicode(self.id)
|
||||
try:
|
||||
return unicode(self.id) # python 2
|
||||
except NameError:
|
||||
return str(self.id) # python 3
|
||||
|
||||
def avatar(self, size):
|
||||
return 'http://www.gravatar.com/avatar/' + md5(self.email).hexdigest() + '?d=mm&s=' + str(size)
|
||||
return 'http://www.gravatar.com/avatar/%s?d=mm&s=%d' % \
|
||||
(md5(self.email.encode('utf-8')).hexdigest(), size)
|
||||
|
||||
def follow(self, user):
|
||||
if not self.is_following(user):
|
||||
|
@ -69,18 +80,23 @@ class User(db.Model):
|
|||
return self
|
||||
|
||||
def is_following(self, user):
|
||||
return self.followed.filter(followers.c.followed_id == user.id).count() > 0
|
||||
return self.followed.filter(
|
||||
followers.c.followed_id == user.id).count() > 0
|
||||
|
||||
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): # pragma: no cover
|
||||
return '<User %r>' % (self.nickname)
|
||||
|
||||
|
||||
class Post(db.Model):
|
||||
__searchable__ = ['body']
|
||||
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
body = db.Column(db.String(140))
|
||||
timestamp = db.Column(db.DateTime)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||
|
@ -89,6 +105,6 @@ class Post(db.Model):
|
|||
def __repr__(self): # pragma: no cover
|
||||
return '<Post %r>' % (self.body)
|
||||
|
||||
if WHOOSH_ENABLED:
|
||||
import flask.ext.whooshalchemy as whooshalchemy
|
||||
|
||||
if enable_search:
|
||||
whooshalchemy.whoosh_index(app, Post)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
from jinja2 import Markup
|
||||
|
||||
|
||||
class momentjs(object):
|
||||
def __init__(self, timestamp):
|
||||
self.timestamp = timestamp
|
||||
|
||||
def render(self, format):
|
||||
return Markup("<script>\ndocument.write(moment(\"%s\").%s);\n</script>" % (self.timestamp.strftime("%Y-%m-%dT%H:%M:%S Z"), format))
|
||||
return Markup(
|
||||
"<script>\ndocument.write(moment(\"%s\").%s);\n</script>" %
|
||||
(self.timestamp.strftime("%Y-%m-%dT%H:%M:%S Z"), format))
|
||||
|
||||
def format(self, fmt):
|
||||
return self.render("format(\"%s\")" % fmt)
|
||||
|
@ -15,4 +18,3 @@ class momentjs(object):
|
|||
|
||||
def fromNow(self):
|
||||
return self.render("fromNow()")
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,6 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _('File Not Found') }}</h1>
|
||||
<p><a href="{{url_for('index')}}">{{ _('Back') }}</a></p>
|
||||
<h1>{{ _('File Not Found') }}</h1>
|
||||
<p><a href="{{ url_for('index') }}">{{ _('Back') }}</a></p>
|
||||
{% endblock %}
|
|
@ -2,7 +2,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _('An unexpected error has occurred') }}</h1>
|
||||
<p>{{ _('The administrator has been notified. Sorry for the inconvenience!') }}</p>
|
||||
<p><a href="{{url_for('index')}}">{{ _('Back') }}</a></p>
|
||||
<h1>{{ _('An unexpected error has occurred') }}</h1>
|
||||
<p>{{ _('The administrator has been notified. Sorry for the inconvenience!') }}</p>
|
||||
<p><a href="{{ url_for('index') }}">{{ _('Back') }}</a></p>
|
||||
{% endblock %}
|
|
@ -2,17 +2,17 @@
|
|||
<html>
|
||||
<head>
|
||||
{% if title %}
|
||||
<title>{{title}} - microblog</title>
|
||||
<title>{{ title }} - microblog</title>
|
||||
{% else %}
|
||||
<title>microblog</title>
|
||||
{% endif %}
|
||||
<link href="{{ url_for('.static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('.static', filename = 'css/bootstrap-responsive.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('.static', filename='css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<link href="{{ url_for('.static', filename='css/bootstrap-responsive.min.css') }}" rel="stylesheet">
|
||||
<script src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<script src="{{ url_for('.static', filename = 'js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('.static', filename = 'js/moment.min.js') }}"></script>
|
||||
<script src="{{ url_for('.static', filename='js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('.static', filename='js/moment.min.js') }}"></script>
|
||||
{% if g.locale != 'en' %}
|
||||
<script src="{{ url_for('.static', filename = 'js/moment-' + g.locale + '.min.js') }}"></script>
|
||||
<script src="{{ url_for('.static', filename='js/moment-' + g.locale + '.min.js') }}"></script>
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>
|
||||
|
@ -48,13 +48,13 @@
|
|||
<ul class="nav">
|
||||
<li><a href="{{ url_for('index') }}">{{ _('Home') }}</a></li>
|
||||
{% if g.user.is_authenticated() %}
|
||||
<li><a href="{{ url_for('user', nickname = g.user.nickname) }}">{{ _('Your Profile') }}</a></li>
|
||||
<li><a href="{{ url_for('user', nickname=g.user.nickname) }}">{{ _('Your Profile') }}</a></li>
|
||||
<li><a href="{{ url_for('logout') }}">{{ _('Logout') }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="nav-collapse collapse">
|
||||
{% if g.user.is_authenticated() and g.search_enabled %}
|
||||
<form class="navbar-search pull-right" action="{{url_for('search')}}" method="post" name="search">{{g.search_form.hidden_tag()}}{{g.search_form.search(size=20,placeholder=_('Search'),class="search-query")}}</form>
|
||||
{% if g.user.is_authenticated() %}
|
||||
<form class="navbar-search pull-right" action="{{ url_for('search') }}" method="post" name="search">{{ g.search_form.hidden_tag() }}{{ g.search_form.search(size=20, placeholder=_('Search'), class="search-query")}}</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _('Edit Your Profile') }}</h1>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
<h1>{{ _('Edit Your Profile') }}</h1>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
<form class="form-horizontal" action="" method="post" name="edit">
|
||||
{{form.hidden_tag()}}
|
||||
<div class="control-group{% if form.errors.post %} error{% endif %}">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="control-group{% if form.nickname.errors %} error{% endif %}">
|
||||
<label class="control-label" for="nickname">{{ _('Your nickname:') }}</label>
|
||||
<div class="controls">
|
||||
{{ form.nickname(maxlength = 64, class = "span4") }}
|
||||
{% for error in form.errors.nickname %}
|
||||
<span class="help-inline">[{{error}}]</span><br>
|
||||
{{ form.nickname(maxlength=64, class="span4") }}
|
||||
{% for error in form.nickname.errors %}
|
||||
<span class="help-inline">[{{ error }}]</span><br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group{% if form.errors.post %} error{% endif %}">
|
||||
<div class="control-group{% if form.about_me.errors %} error{% endif %}">
|
||||
<label class="control-label" for="about_me">{{ _('About yourself:') }}</label>
|
||||
<div class="controls">
|
||||
{{ form.about_me(cols = 64, rows = 4, class = "span4") }}
|
||||
{% for error in form.errors.about_me %}
|
||||
<span class="help-inline">[{{error}}]</span><br>
|
||||
{{ form.about_me(cols=64, rows=4, class="span4") }}
|
||||
{% for error in form.about_me.errors %}
|
||||
<span class="help-inline">[{{ error }}]</span><br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,6 +31,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<p>Dear {{user.nickname}},</p>
|
||||
<p>{{ _('%(nickname)s is now a follower.', nickname = '<a href="' + url_for("user", nickname = follower.nickname, _external = True) + '">' + follower.nickname + '</a>') }}</p>
|
||||
<p>Dear {{ user.nickname }},</p>
|
||||
<p>{{ _('%(nickname)s is now a follower.', nickname='<a href="' + url_for("user", nickname=follower.nickname, _external=True) + '">' + follower.nickname + '</a>') }}</p>
|
||||
<table>
|
||||
<tr valign="top">
|
||||
<td><img src="{{follower.avatar(50)}}"></td>
|
||||
<td><img src="{{ follower.avatar(50) }}"></td>
|
||||
<td>
|
||||
<a href="{{url_for('user', nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a><br />
|
||||
{{follower.about_me}}
|
||||
<a href="{{ url_for('user', nickname=follower.nickname, _external=True)}}">{{ follower.nickname }}</a><br />
|
||||
{{ follower.about_me }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{{ _('Dear %(nickname)s,', nickname = user.nickname) }}
|
||||
{{ _('Dear %(nickname)s,', nickname=user.nickname) }}
|
||||
|
||||
{{ _('%(nickname)s is now a follower. Click on the following link to visit %(nickname)s\'s profile page:', nickname = follower.nickname) }}
|
||||
{{ _('%(nickname)s is now a follower. Click on the following link to visit %(nickname)s\'s profile page:', nickname=follower.nickname) }}
|
||||
|
||||
{{url_for("user", nickname = follower.nickname, _external = True)}}
|
||||
{{ url_for("user", nickname=follower.nickname, _external=True) }}
|
||||
|
||||
{{ _('Regards,') }}
|
||||
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _('Hi, %(nickname)s!', nickname = g.user.nickname) }}</h1>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
<h1>{{ _('Hi, %(nickname)s!', nickname=g.user.nickname) }}</h1>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
<form class="form-horizontal" action="" method="post" name="post">
|
||||
{{form.hidden_tag()}}
|
||||
<div class="control-group{% if form.errors.post %} error{% endif %}">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="control-group{% if form.post.errors %} error{% endif %}">
|
||||
<label class="control-label" for="post">{{ _('Say something:') }}</label>
|
||||
<div class="controls">
|
||||
{{ form.post(size = 30, maxlength = 140) }}
|
||||
{% for error in form.errors.post %}
|
||||
<span class="help-inline">[{{error}}]</span><br>
|
||||
{{ form.post(size=30, maxlength=140) }}
|
||||
{% for error in form.post.errors %}
|
||||
<span class="help-inline">[{{ error }}]</span><br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,20 +22,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% for post in posts.items %}
|
||||
</div>
|
||||
{% for post in posts.items %}
|
||||
{% include 'post.html' %}
|
||||
{% endfor %}
|
||||
<ul class="pager">
|
||||
{% endfor %}
|
||||
<ul class="pager">
|
||||
{% if posts.has_prev %}
|
||||
<li class="previous"><a href="{{ url_for('index', page = posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
|
||||
<li class="previous"><a href="{{ url_for('index', page=posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
|
||||
{% else %}
|
||||
<li class="previous disabled"><a href="#">{{ _('Newer posts') }}</a></li>
|
||||
{% endif %}
|
||||
{% if posts.has_next %}
|
||||
<li class="next"><a href="{{ url_for('index', page = posts.next_num) }}">{{ _('Older posts') }}</a></li>
|
||||
<li class="next"><a href="{{ url_for('index', page=posts.next_num) }}">{{ _('Older posts') }}</a></li>
|
||||
{% else %}
|
||||
<li class="next disabled"><a href="#">{{ _('Older posts') }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<script type="text/javascript">
|
||||
function set_openid(openid, pr)
|
||||
{
|
||||
<script type="text/javascript">
|
||||
function set_openid(openid, pr)
|
||||
{
|
||||
u = openid.search('<username>');
|
||||
if (u != -1) {
|
||||
// openid requires username
|
||||
|
@ -13,25 +13,25 @@ function set_openid(openid, pr)
|
|||
}
|
||||
form = document.forms['login'];
|
||||
form.elements['openid'].value = openid;
|
||||
}
|
||||
</script>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
}
|
||||
</script>
|
||||
{% include 'flash.html' %}
|
||||
<div class="well">
|
||||
<h3>{{ _('Please Sign In') }}</h3>
|
||||
<form class="form" action="" method="post" name="login">
|
||||
{{form.hidden_tag()}}
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="help-block">{{ _('Click on your OpenID provider below:') }}</div>
|
||||
<div class="control-group">
|
||||
{% for pr in providers %}
|
||||
<a href="javascript:set_openid('{{pr.url}}', '{{pr.name}}');"><img src="/static/img/{{pr.name.lower()}}.png" class="img-polaroid" style="margin:2px;" /></a>
|
||||
<a href="javascript:set_openid('{{ pr.url }}', '{{ pr.name }}');"><img src="/static/img/{{ pr.name.lower() }}.png" class="img-polaroid" style="margin:2px;" /></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="control-group{% if form.errors.openid %} error{% endif %}">
|
||||
<div class="control-group{% if form.openid.errors %} error{% endif %}">
|
||||
<label class="control-label" for="openid">{{ _('Or enter your OpenID here:') }}</label>
|
||||
<div class="controls">
|
||||
{{ form.openid(size = 80, class = "span4") }}
|
||||
{% for error in form.errors.openid %}
|
||||
<span class="help-inline">[{{error}}]</span><br>
|
||||
{{ form.openid(size=80, class="span4") }}
|
||||
{% for error in form.openid.errors %}
|
||||
<span class="help-inline">[{{ error }}]</span><br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,5 +48,5 @@ function set_openid(openid, pr)
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<table class="table table-hover">
|
||||
<tr>
|
||||
<td width="70px"><a href="{{url_for('user', nickname = post.author.nickname)}}"><img src="{{post.author.avatar(70)}}" /></a></td>
|
||||
<td width="70px"><a href="{{ url_for('user', nickname=post.author.nickname) }}"><img src="{{ post.author.avatar(70) }}" /></a></td>
|
||||
<td>
|
||||
{% autoescape false %}
|
||||
<p>{{ _('%(nickname)s said %(when)s:', nickname = '<a href="%s">%s</a>' % (url_for('user', nickname = post.author.nickname), post.author.nickname), when = momentjs(post.timestamp).fromNow()) }}</p>
|
||||
<p>{{ _('%(nickname)s said %(when)s:', nickname='<a href="%s">%s</a>' % (url_for('user', nickname=post.author.nickname), post.author.nickname), when=momentjs(post.timestamp).fromNow()) }}</p>
|
||||
{% endautoescape %}
|
||||
<p><strong><span id="post{{post.id}}">{{post.body}}</span></strong></p>
|
||||
<p><strong><span id="post{{ post.id }}">{{ post.body }}</span></strong></p>
|
||||
{% if post.language != None and post.language != '' and post.language != g.locale %}
|
||||
<div>
|
||||
<span id="translation{{post.id}}">
|
||||
<a href="javascript:translate('{{post.language}}', '{{g.locale}}', '#post{{post.id}}', '#translation{{post.id}}', '#loading{{post.id}}');">{{ _('Translate') }}</a>
|
||||
<span id="translation{{ post.id }}">
|
||||
<a href="javascript:translate('{{ post.language }}', '{{ g.locale }}', '#post{{ post.id }}', '#translation{{ post.id }}', '#loading{{ post.id }}');">{{ _('Translate') }}</a>
|
||||
</span>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% if post.author.id == g.user.id %}
|
||||
|
|
|
@ -2,39 +2,39 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'flash.html' %}
|
||||
<div class="well well-large" style="height: 140px;">
|
||||
{% include 'flash.html' %}
|
||||
<div class="well well-large" style="height: 140px;">
|
||||
<div class="pull-right">
|
||||
<img src="{{user.avatar(128)}}" class="img-polaroid">
|
||||
<img src="{{ user.avatar(128) }}" class="img-polaroid">
|
||||
</div>
|
||||
<h1>{{user.nickname}}</h1>
|
||||
{% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
|
||||
<h1>{{ user.nickname }}</h1>
|
||||
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
|
||||
{% if user.last_seen %}
|
||||
<p><em>{{ _('Last seen:') }} {{ momentjs(user.last_seen).calendar() }}</em></p>
|
||||
{% endif %}
|
||||
<p>{{ _('Followers:') }} {{user.followers.count() - 1}} | {{ _('Following:') }} {{user.followed.count() - 1}} |
|
||||
<p>{{ _('Followers:') }} {{ user.followers.count() - 1 }} | {{ _('Following:') }} {{ user.followed.count() - 1 }} |
|
||||
{% if user.id == g.user.id %}
|
||||
<a href="{{url_for('edit')}}">{{ _('Edit your profile') }}</a>
|
||||
<a href="{{ url_for('edit') }}">{{ _('Edit your profile') }}</a>
|
||||
{% elif not g.user.is_following(user) %}
|
||||
<a href="{{url_for('follow', nickname = user.nickname)}}">{{ _('Follow') }}</a>
|
||||
<a href="{{ url_for('follow', nickname=user.nickname) }}">{{ _('Follow') }}</a>
|
||||
{% else %}
|
||||
<a href="{{url_for('unfollow', nickname = user.nickname)}}">{{ _('Unfollow') }}</a>
|
||||
<a href="{{ url_for('unfollow', nickname=user.nickname) }}">{{ _('Unfollow') }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% for post in posts.items %}
|
||||
</div>
|
||||
{% for post in posts.items %}
|
||||
{% include 'post.html' %}
|
||||
{% endfor %}
|
||||
<ul class="pager">
|
||||
{% endfor %}
|
||||
<ul class="pager">
|
||||
{% if posts.has_prev %}
|
||||
<li class="previous"><a href="{{ url_for('user', nickname = user.nickname, page = posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
|
||||
<li class="previous"><a href="{{ url_for('user', nickname=user.nickname, page=posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
|
||||
{% else %}
|
||||
<li class="previous disabled"><a href="#">{{ _('Newer posts') }}</a></li>
|
||||
{% endif %}
|
||||
{% if posts.has_next %}
|
||||
<li class="next"><a href="{{ url_for('user', nickname = user.nickname, page = posts.next_num) }}">{{ _('Older posts') }}</a></li>
|
||||
<li class="next"><a href="{{ url_for('user', nickname=user.nickname, page=posts.next_num) }}">{{ _('Older posts') }}</a></li>
|
||||
{% else %}
|
||||
<li class="next disabled"><a href="#">{{ _('Older posts') }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
import urllib, httplib
|
||||
try:
|
||||
import httplib # Python 2
|
||||
except ImportError:
|
||||
import http.client as httplib # Python 3
|
||||
try:
|
||||
from urllib import urlencode # Python 2
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode # Python 3
|
||||
import json
|
||||
from app import app
|
||||
from flask.ext.babel import gettext
|
||||
from config import MS_TRANSLATOR_CLIENT_ID, MS_TRANSLATOR_CLIENT_SECRET
|
||||
|
||||
|
||||
def microsoft_translate(text, sourceLang, destLang):
|
||||
if MS_TRANSLATOR_CLIENT_ID == "" or MS_TRANSLATOR_CLIENT_SECRET == "":
|
||||
return gettext('Error: translation service not configured.')
|
||||
try:
|
||||
# get access token
|
||||
params = urllib.urlencode({
|
||||
params = urlencode({
|
||||
'client_id': MS_TRANSLATOR_CLIENT_ID,
|
||||
'client_secret': MS_TRANSLATOR_CLIENT_SECRET,
|
||||
'scope': 'http://api.microsofttranslator.com',
|
||||
|
@ -17,39 +25,40 @@ def microsoft_translate(text, sourceLang, destLang):
|
|||
})
|
||||
conn = httplib.HTTPSConnection("datamarket.accesscontrol.windows.net")
|
||||
conn.request("POST", "/v2/OAuth2-13", params)
|
||||
response = json.loads (conn.getresponse().read())
|
||||
response = json.loads(conn.getresponse().read())
|
||||
token = response[u'access_token']
|
||||
|
||||
# translate
|
||||
conn = httplib.HTTPConnection('api.microsofttranslator.com')
|
||||
params = {
|
||||
'appId': 'Bearer ' + token,
|
||||
params = {'appId': 'Bearer ' + token,
|
||||
'from': sourceLang,
|
||||
'to': destLang,
|
||||
'text': text.encode("utf-8")
|
||||
}
|
||||
conn.request("GET", '/V2/Ajax.svc/Translate?' + urllib.urlencode(params))
|
||||
response = json.loads("{\"response\":" + conn.getresponse().read().decode('utf-8-sig') + "}")
|
||||
'text': text.encode("utf-8")}
|
||||
conn.request("GET",
|
||||
'/V2/Ajax.svc/Translate?' + urlencode(params))
|
||||
response = json.loads('{"response":' +
|
||||
conn.getresponse().read().decode('utf-8') + '}')
|
||||
return response["response"]
|
||||
except:
|
||||
#return gettext('Error: Unexpected error.')
|
||||
raise
|
||||
|
||||
|
||||
def google_translate(text, sourceLang, destLang):
|
||||
if not app.debug:
|
||||
return gettext('Error: translation service not available.')
|
||||
try:
|
||||
params = urllib.urlencode({
|
||||
params = urlencode({
|
||||
'client': 't',
|
||||
'text': text.encode("utf-8"),
|
||||
'sl': sourceLang,
|
||||
'tl': destLang,
|
||||
'ie': 'UTF-8',
|
||||
'oe': 'UTF-8'
|
||||
})
|
||||
'oe': 'UTF-8'})
|
||||
conn = httplib.HTTPSConnection("translate.google.com")
|
||||
conn.request("GET", "/translate_a/t?" + params, headers = { 'User-Agent': 'Mozilla/5.0' })
|
||||
httpresponse = conn.getresponse().read().replace(",,,", ",\"\",\"\",").replace(",,", ",\"\",")
|
||||
conn.request("GET", "/translate_a/t?" + params,
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
httpresponse = conn.getresponse().read().replace(
|
||||
",,,", ",\"\",\"\",").replace(",,", ",\"\",")
|
||||
response = json.loads("{\"response\":" + httpresponse + "}")
|
||||
return response["response"][0][0][0]
|
||||
except:
|
||||
|
|
Binary file not shown.
146
app/views.py
146
app/views.py
|
@ -1,24 +1,30 @@
|
|||
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 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.sqlalchemy import get_debug_queries
|
||||
from flask.ext.babel import gettext
|
||||
from app import app, db, lm, oid, babel
|
||||
from forms import LoginForm, EditForm, PostForm, SearchForm
|
||||
from models import User, ROLE_USER, ROLE_ADMIN, Post
|
||||
from datetime import datetime
|
||||
from emails import follower_notification
|
||||
from guess_language import guessLanguage
|
||||
from translate import microsoft_translate
|
||||
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES, DATABASE_QUERY_TIMEOUT, WHOOSH_ENABLED
|
||||
from app import app, db, lm, oid, babel
|
||||
from .forms import LoginForm, EditForm, PostForm, SearchForm
|
||||
from .models import User, Post
|
||||
from .emails import follower_notification
|
||||
from .translate import microsoft_translate
|
||||
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES, \
|
||||
DATABASE_QUERY_TIMEOUT
|
||||
|
||||
|
||||
@lm.user_loader
|
||||
def load_user(id):
|
||||
return User.query.get(int(id))
|
||||
|
||||
|
||||
@babel.localeselector
|
||||
def get_locale():
|
||||
return request.accept_languages.best_match(LANGUAGES.keys())
|
||||
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.user = current_user
|
||||
|
@ -28,49 +34,54 @@ def before_request():
|
|||
db.session.commit()
|
||||
g.search_form = SearchForm()
|
||||
g.locale = get_locale()
|
||||
g.search_enabled = WHOOSH_ENABLED
|
||||
|
||||
|
||||
@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))
|
||||
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)
|
||||
def internal_error(error):
|
||||
def not_found_error(error):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_error(error):
|
||||
db.session.rollback()
|
||||
return render_template('500.html'), 500
|
||||
|
||||
@app.route('/', methods = ['GET', 'POST'])
|
||||
@app.route('/index', methods = ['GET', 'POST'])
|
||||
@app.route('/index/<int:page>', methods = ['GET', 'POST'])
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
@app.route('/index', methods=['GET', 'POST'])
|
||||
@app.route('/index/<int:page>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def index(page = 1):
|
||||
def index(page=1):
|
||||
form = PostForm()
|
||||
if form.validate_on_submit():
|
||||
language = guessLanguage(form.post.data)
|
||||
if language == 'UNKNOWN' or len(language) > 5:
|
||||
language = ''
|
||||
post = Post(body = form.post.data,
|
||||
timestamp = datetime.utcnow(),
|
||||
author = g.user,
|
||||
language = language)
|
||||
post = Post(body=form.post.data, timestamp=datetime.utcnow(),
|
||||
author=g.user, language=language)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
flash(gettext('Your post is now live!'))
|
||||
return redirect(url_for('index'))
|
||||
posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False)
|
||||
return render_template('index.html',
|
||||
title = 'Home',
|
||||
form = form,
|
||||
posts = posts)
|
||||
title='Home',
|
||||
form=form,
|
||||
posts=posts)
|
||||
|
||||
@app.route('/login', methods = ['GET', 'POST'])
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
@oid.loginhandler
|
||||
def login():
|
||||
if g.user is not None and g.user.is_authenticated():
|
||||
|
@ -78,25 +89,26 @@ def login():
|
|||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
session['remember_me'] = form.remember_me.data
|
||||
return oid.try_login(form.openid.data, ask_for = ['nickname', 'email'])
|
||||
return oid.try_login(form.openid.data, ask_for=['nickname', 'email'])
|
||||
return render_template('login.html',
|
||||
title = 'Sign In',
|
||||
form = form,
|
||||
providers = app.config['OPENID_PROVIDERS'])
|
||||
title='Sign In',
|
||||
form=form,
|
||||
providers=app.config['OPENID_PROVIDERS'])
|
||||
|
||||
|
||||
@oid.after_login
|
||||
def after_login(resp):
|
||||
if resp.email is None or resp.email == "":
|
||||
flash(gettext('Invalid login. Please try again.'))
|
||||
return redirect(url_for('login'))
|
||||
user = User.query.filter_by(email = resp.email).first()
|
||||
user = User.query.filter_by(email=resp.email).first()
|
||||
if user is None:
|
||||
nickname = resp.nickname
|
||||
if nickname is None or nickname == "":
|
||||
nickname = resp.email.split('@')[0]
|
||||
nickname = User.make_valid_nickname(nickname)
|
||||
nickname = User.make_unique_nickname(nickname)
|
||||
user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
|
||||
user = User(nickname=nickname, email=resp.email)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
# make the user follow him/herself
|
||||
|
@ -106,28 +118,31 @@ def after_login(resp):
|
|||
if 'remember_me' in session:
|
||||
remember_me = session['remember_me']
|
||||
session.pop('remember_me', None)
|
||||
login_user(user, remember = remember_me)
|
||||
login_user(user, remember=remember_me)
|
||||
return redirect(request.args.get('next') or url_for('index'))
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/user/<nickname>')
|
||||
@app.route('/user/<nickname>/<int:page>')
|
||||
@login_required
|
||||
def user(nickname, page = 1):
|
||||
user = User.query.filter_by(nickname = nickname).first()
|
||||
if user == None:
|
||||
flash(gettext('User %(nickname)s not found.', nickname = nickname))
|
||||
def user(nickname, page=1):
|
||||
user = User.query.filter_by(nickname=nickname).first()
|
||||
if user is None:
|
||||
flash(gettext('User %(nickname)s not found.', nickname=nickname))
|
||||
return redirect(url_for('index'))
|
||||
posts = user.posts.paginate(page, POSTS_PER_PAGE, False)
|
||||
return render_template('user.html',
|
||||
user = user,
|
||||
posts = posts)
|
||||
user=user,
|
||||
posts=posts)
|
||||
|
||||
@app.route('/edit', methods = ['GET', 'POST'])
|
||||
|
||||
@app.route('/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def edit():
|
||||
form = EditForm(g.user.nickname)
|
||||
|
@ -141,53 +156,56 @@ def edit():
|
|||
elif request.method != "POST":
|
||||
form.nickname.data = g.user.nickname
|
||||
form.about_me.data = g.user.about_me
|
||||
return render_template('edit.html',
|
||||
form = form)
|
||||
return render_template('edit.html', form=form)
|
||||
|
||||
|
||||
@app.route('/follow/<nickname>')
|
||||
@login_required
|
||||
def follow(nickname):
|
||||
user = User.query.filter_by(nickname = nickname).first()
|
||||
if user == None:
|
||||
flash('User ' + nickname + ' not found.')
|
||||
user = User.query.filter_by(nickname=nickname).first()
|
||||
if user is None:
|
||||
flash('User %s not found.' % nickname)
|
||||
return redirect(url_for('index'))
|
||||
if user == g.user:
|
||||
flash(gettext('You can\'t follow yourself!'))
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
return redirect(url_for('user', nickname=nickname))
|
||||
u = g.user.follow(user)
|
||||
if u is None:
|
||||
flash(gettext('Cannot follow %(nickname)s.', nickname = nickname))
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
flash(gettext('Cannot follow %(nickname)s.', nickname=nickname))
|
||||
return redirect(url_for('user', nickname=nickname))
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
flash(gettext('You are now following %(nickname)s!', nickname = nickname))
|
||||
flash(gettext('You are now following %(nickname)s!', nickname=nickname))
|
||||
follower_notification(user, g.user)
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
return redirect(url_for('user', nickname=nickname))
|
||||
|
||||
|
||||
@app.route('/unfollow/<nickname>')
|
||||
@login_required
|
||||
def unfollow(nickname):
|
||||
user = User.query.filter_by(nickname = nickname).first()
|
||||
if user == None:
|
||||
flash('User ' + nickname + ' not found.')
|
||||
user = User.query.filter_by(nickname=nickname).first()
|
||||
if user is None:
|
||||
flash('User %s not found.' % nickname)
|
||||
return redirect(url_for('index'))
|
||||
if user == g.user:
|
||||
flash(gettext('You can\'t unfollow yourself!'))
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
return redirect(url_for('user', nickname=nickname))
|
||||
u = g.user.unfollow(user)
|
||||
if u is None:
|
||||
flash(gettext('Cannot unfollow %(nickname)s.', nickname = nickname))
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
flash(gettext('Cannot unfollow %(nickname)s.', nickname=nickname))
|
||||
return redirect(url_for('user', nickname=nickname))
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
flash(gettext('You have stopped following %(nickname)s.', nickname = nickname))
|
||||
return redirect(url_for('user', nickname = nickname))
|
||||
flash(gettext('You have stopped following %(nickname)s.',
|
||||
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:
|
||||
if post is None:
|
||||
flash('Post not found.')
|
||||
return redirect(url_for('index'))
|
||||
if post.author.id != g.user.id:
|
||||
|
@ -198,27 +216,29 @@ def delete(id):
|
|||
flash('Your post has been deleted.')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route('/search', methods = ['POST'])
|
||||
|
||||
@app.route('/search', methods=['POST'])
|
||||
@login_required
|
||||
def search():
|
||||
if not g.search_form.validate_on_submit():
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('search_results', query = g.search_form.search.data))
|
||||
return redirect(url_for('search_results', query=g.search_form.search.data))
|
||||
|
||||
|
||||
@app.route('/search_results/<query>')
|
||||
@login_required
|
||||
def search_results(query):
|
||||
results = Post.query.whoosh_search(query, MAX_SEARCH_RESULTS).all()
|
||||
return render_template('search_results.html',
|
||||
query = query,
|
||||
results = results)
|
||||
query=query,
|
||||
results=results)
|
||||
|
||||
@app.route('/translate', methods = ['POST'])
|
||||
|
||||
@app.route('/translate', methods=['POST'])
|
||||
@login_required
|
||||
def translate():
|
||||
return jsonify({
|
||||
'text': microsoft_translate(
|
||||
request.form['text'],
|
||||
request.form['sourceLang'],
|
||||
request.form['destLang']) })
|
||||
|
||||
request.form['destLang'])})
|
||||
|
|
17
config.py
17
config.py
|
@ -6,14 +6,15 @@ CSRF_ENABLED = True
|
|||
SECRET_KEY = 'you-will-never-guess'
|
||||
|
||||
OPENID_PROVIDERS = [
|
||||
{ 'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id' },
|
||||
{ 'name': 'Yahoo', 'url': 'https://me.yahoo.com' },
|
||||
{ 'name': 'AOL', 'url': 'http://openid.aol.com/<username>' },
|
||||
{ 'name': 'Flickr', 'url': 'http://www.flickr.com/<username>' },
|
||||
{ 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }]
|
||||
{'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id'},
|
||||
{'name': 'Yahoo', 'url': 'https://me.yahoo.com'},
|
||||
{'name': 'AOL', 'url': 'http://openid.aol.com/<username>'},
|
||||
{'name': 'Flickr', 'url': 'http://www.flickr.com/<username>'},
|
||||
{'name': 'MyOpenID', 'url': 'https://www.myopenid.com'}]
|
||||
|
||||
if os.environ.get('DATABASE_URL') is None:
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') + '?check_same_thread=False'
|
||||
SQLALCHEMY_DATABASE_URI = ('sqlite:///' + os.path.join(basedir, 'app.db') +
|
||||
'?check_same_thread=False')
|
||||
else:
|
||||
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
|
||||
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
||||
|
@ -31,8 +32,8 @@ MAIL_SERVER = '' # your mailserver
|
|||
MAIL_PORT = 25
|
||||
MAIL_USE_TLS = False
|
||||
MAIL_USE_SSL = False
|
||||
MAIL_USERNAME = 'you'
|
||||
MAIL_PASSWORD = 'your-password'
|
||||
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
||||
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
||||
|
||||
# available languages
|
||||
LANGUAGES = {
|
||||
|
|
|
@ -9,4 +9,5 @@ if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
|
|||
api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
|
||||
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
else:
|
||||
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))
|
||||
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO,
|
||||
api.version(SQLALCHEMY_MIGRATE_REPO))
|
||||
|
|
|
@ -4,4 +4,5 @@ from config import SQLALCHEMY_DATABASE_URI
|
|||
from config import SQLALCHEMY_MIGRATE_REPO
|
||||
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1)
|
||||
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
|
||||
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
print('Current database version: ' + str(v))
|
||||
|
|
|
@ -4,12 +4,16 @@ from migrate.versioning import api
|
|||
from app import db
|
||||
from config import SQLALCHEMY_DATABASE_URI
|
||||
from config import SQLALCHEMY_MIGRATE_REPO
|
||||
migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1)
|
||||
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
migration = SQLALCHEMY_MIGRATE_REPO + ('/versions/%03d_migration.py' % (v+1))
|
||||
tmp_module = imp.new_module('old_model')
|
||||
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
exec old_model in tmp_module.__dict__
|
||||
script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata)
|
||||
script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI,
|
||||
SQLALCHEMY_MIGRATE_REPO,
|
||||
tmp_module.meta, db.metadata)
|
||||
open(migration, "wt").write(script)
|
||||
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
print 'New migration saved as ' + migration
|
||||
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
|
||||
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
print('New migration saved as ' + migration)
|
||||
print('Current database version: ' + str(v))
|
||||
|
|
|
@ -9,7 +9,6 @@ user = Table('user', post_meta,
|
|||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('nickname', String(length=64)),
|
||||
Column('email', String(length=120)),
|
||||
Column('role', SmallInteger, default=ColumnDefault(0)),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ user = Table('user', post_meta,
|
|||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('nickname', String(length=64)),
|
||||
Column('email', String(length=120)),
|
||||
Column('role', SmallInteger, default=ColumnDefault(0)),
|
||||
Column('about_me', String(length=140)),
|
||||
Column('last_seen', DateTime),
|
||||
)
|
||||
|
|
|
@ -3,4 +3,5 @@ from migrate.versioning import api
|
|||
from config import SQLALCHEMY_DATABASE_URI
|
||||
from config import SQLALCHEMY_MIGRATE_REPO
|
||||
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
|
||||
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
|
||||
print('Current database version: ' + str(v))
|
||||
|
|
|
@ -3,6 +3,5 @@ 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)
|
||||
|
||||
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
|
||||
app.run(debug=True)
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
Babel==0.9.6
|
||||
Flask==0.9
|
||||
Flask-Babel==0.8
|
||||
Flask-Login==0.1.3
|
||||
Flask-Mail==0.8.2
|
||||
Flask-OpenID==1.1.1
|
||||
Flask-SQLAlchemy==0.16
|
||||
Flask-WTF==0.8.3
|
||||
git+git://github.com/miguelgrinberg/Flask-WhooshAlchemy
|
||||
Jinja2==2.6
|
||||
MySQL-python==1.2.4
|
||||
SQLAlchemy==0.7.9
|
||||
Tempita==0.5.1
|
||||
WTForms==1.0.3
|
||||
Werkzeug==0.8.3
|
||||
Whoosh==2.4.1
|
||||
blinker==1.2
|
||||
coverage==3.6
|
||||
Babel==1.3
|
||||
Flask==0.10.1
|
||||
Flask-Babel==0.9
|
||||
Flask-Login==0.2.11
|
||||
Flask-Mail==0.9.0
|
||||
Flask-OpenID==1.2.1
|
||||
Flask-SQLAlchemy==2.0
|
||||
Flask-WTF==0.10.2
|
||||
Flask-WhooshAlchemy==0.56
|
||||
Jinja2==2.7.3
|
||||
MarkupSafe==0.23
|
||||
SQLAlchemy==0.9.7
|
||||
Tempita==0.5.2
|
||||
WTForms==2.0.1
|
||||
Werkzeug==0.9.6
|
||||
Whoosh==2.6.0
|
||||
blinker==1.3
|
||||
coverage==3.7.1
|
||||
decorator==3.4.0
|
||||
flup==1.0.3.dev-20110405
|
||||
flipflop==1.0
|
||||
guess-language==0.2
|
||||
gunicorn==0.17.2
|
||||
psycopg2==2.5
|
||||
gunicorn==19.1.1
|
||||
itsdangerous==0.24
|
||||
pbr==0.10.0
|
||||
psycopg2==2.5.4
|
||||
python-openid==2.2.5
|
||||
pytz==2013b
|
||||
pytz==2014.7
|
||||
six==1.8.0
|
||||
speaklater==1.3
|
||||
sqlalchemy-migrate==0.7.2
|
||||
|
||||
sqlalchemy-migrate==0.9.2
|
||||
sqlparse==0.1.11
|
||||
|
|
2
run.py
2
run.py
|
@ -1,3 +1,3 @@
|
|||
#!flask/bin/python
|
||||
from app import app
|
||||
app.run(debug = True)
|
||||
app.run(debug=True)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#!flask/bin/python
|
||||
from app import app
|
|
@ -1,9 +1,10 @@
|
|||
#!flask/bin/python
|
||||
import os
|
||||
|
||||
# use mysql
|
||||
os.environ['DATABASE_URL'] = 'mysql://apps:apps@localhost/apps'
|
||||
|
||||
from flup.server.fcgi import WSGIServer
|
||||
from flipflop import WSGIServer
|
||||
from app import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!flask/bin/python
|
||||
from flup.server.fcgi import WSGIServer
|
||||
from flipflop import WSGIServer
|
||||
from app import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
24
setup.py
24
setup.py
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import os, subprocess, sys
|
||||
subprocess.call(['python', 'virtualenv.py', 'flask'])
|
||||
if sys.platform == 'win32':
|
||||
bin = 'Scripts'
|
||||
else:
|
||||
bin = 'bin'
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask<0.10'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-login'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-openid'])
|
||||
if sys.platform == 'win32':
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', '--no-deps', 'lamson', 'chardet', 'flask-mail'])
|
||||
else:
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-mail'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'sqlalchemy==0.7.9'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-sqlalchemy'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'sqlalchemy-migrate'])
|
||||
#subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-whooshalchemy'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'git+git://github.com/miguelgrinberg/Flask-WhooshAlchemy'])
|
||||
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', 'guess-language'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flup'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'coverage'])
|
64
tests.py
64
tests.py
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf8 -*-
|
||||
|
||||
from coverage import coverage
|
||||
cov = coverage(branch = True, omit = ['flask/*', 'tests.py'])
|
||||
cov = coverage(branch=True, omit=['flask/*', 'tests.py'])
|
||||
cov.start()
|
||||
|
||||
import os
|
||||
|
@ -14,11 +14,13 @@ from app import app, db
|
|||
from app.models import User, Post
|
||||
from app.translate import microsoft_translate
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
app.config['TESTING'] = True
|
||||
app.config['CSRF_ENABLED'] = False
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'test.db')
|
||||
app.config['WTF_CSRF_ENABLED'] = False
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \
|
||||
os.path.join(basedir, 'test.db')
|
||||
db.create_all()
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -32,24 +34,25 @@ class TestCase(unittest.TestCase):
|
|||
n = User.make_valid_nickname('John_[123]\n')
|
||||
assert n == 'John_123'
|
||||
# create a user
|
||||
u = User(nickname = 'john', email = 'john@example.com')
|
||||
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.is_authenticated() is True
|
||||
assert u.is_active() is True
|
||||
assert u.is_anonymous() is False
|
||||
assert u.id == int(u.get_id())
|
||||
|
||||
def test_avatar(self):
|
||||
# create a user
|
||||
u = User(nickname = 'john', email = 'john@example.com')
|
||||
u = User(nickname='john', email='john@example.com')
|
||||
avatar = u.avatar(128)
|
||||
expected = 'http://www.gravatar.com/avatar/d4c74594d841139328695756648b6bd6'
|
||||
expected = 'http://www.gravatar.com/avatar/' + \
|
||||
'd4c74594d841139328695756648b6bd6'
|
||||
assert avatar[0:len(expected)] == expected
|
||||
|
||||
def test_make_unique_nickname(self):
|
||||
# create a user and write it to the database
|
||||
u = User(nickname = 'john', email = 'john@example.com')
|
||||
u = User(nickname='john', email='john@example.com')
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
nickname = User.make_unique_nickname('susan')
|
||||
|
@ -57,7 +60,7 @@ class TestCase(unittest.TestCase):
|
|||
nickname = User.make_unique_nickname('john')
|
||||
assert nickname != 'john'
|
||||
# make another user with the new nickname
|
||||
u = User(nickname = nickname, email = 'susan@example.com')
|
||||
u = User(nickname=nickname, email='susan@example.com')
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
nickname2 = User.make_unique_nickname('john')
|
||||
|
@ -65,45 +68,49 @@ class TestCase(unittest.TestCase):
|
|||
assert nickname2 != nickname
|
||||
|
||||
def test_follow(self):
|
||||
u1 = User(nickname = 'john', email = 'john@example.com')
|
||||
u2 = User(nickname = 'susan', email = 'susan@example.com')
|
||||
u1 = User(nickname='john', email='john@example.com')
|
||||
u2 = User(nickname='susan', email='susan@example.com')
|
||||
db.session.add(u1)
|
||||
db.session.add(u2)
|
||||
db.session.commit()
|
||||
assert u1.unfollow(u2) == None
|
||||
assert u1.unfollow(u2) is None
|
||||
u = u1.follow(u2)
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
assert u1.follow(u2) == None
|
||||
assert u1.follow(u2) is None
|
||||
assert u1.is_following(u2)
|
||||
assert u1.followed.count() == 1
|
||||
assert u1.followed.first().nickname == 'susan'
|
||||
assert u2.followers.count() == 1
|
||||
assert u2.followers.first().nickname == 'john'
|
||||
u = u1.unfollow(u2)
|
||||
assert u != None
|
||||
assert u is not None
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
assert u1.is_following(u2) == False
|
||||
assert not u1.is_following(u2)
|
||||
assert u1.followed.count() == 0
|
||||
assert u2.followers.count() == 0
|
||||
|
||||
def test_follow_posts(self):
|
||||
# make four users
|
||||
u1 = User(nickname = 'john', email = 'john@example.com')
|
||||
u2 = User(nickname = 'susan', email = 'susan@example.com')
|
||||
u3 = User(nickname = 'mary', email = 'mary@example.com')
|
||||
u4 = User(nickname = 'david', email = 'david@example.com')
|
||||
u1 = User(nickname='john', email='john@example.com')
|
||||
u2 = User(nickname='susan', email='susan@example.com')
|
||||
u3 = User(nickname='mary', email='mary@example.com')
|
||||
u4 = User(nickname='david', email='david@example.com')
|
||||
db.session.add(u1)
|
||||
db.session.add(u2)
|
||||
db.session.add(u3)
|
||||
db.session.add(u4)
|
||||
# make four posts
|
||||
utcnow = datetime.utcnow()
|
||||
p1 = Post(body = "post from john", author = u1, timestamp = utcnow + timedelta(seconds = 1))
|
||||
p2 = Post(body = "post from susan", author = u2, timestamp = utcnow + timedelta(seconds = 2))
|
||||
p3 = Post(body = "post from mary", author = u3, timestamp = utcnow + timedelta(seconds = 3))
|
||||
p4 = Post(body = "post from david", author = u4, timestamp = utcnow + timedelta(seconds = 4))
|
||||
p1 = Post(body="post from john", author=u1,
|
||||
timestamp=utcnow + timedelta(seconds=1))
|
||||
p2 = Post(body="post from susan", author=u2,
|
||||
timestamp=utcnow + timedelta(seconds=2))
|
||||
p3 = Post(body="post from mary", author=u3,
|
||||
timestamp=utcnow + timedelta(seconds=3))
|
||||
p4 = Post(body="post from david", author=u4,
|
||||
timestamp=utcnow + timedelta(seconds=4))
|
||||
db.session.add(p1)
|
||||
db.session.add(p2)
|
||||
db.session.add(p3)
|
||||
|
@ -143,8 +150,8 @@ class TestCase(unittest.TestCase):
|
|||
|
||||
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())
|
||||
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()
|
||||
|
@ -160,6 +167,7 @@ class TestCase(unittest.TestCase):
|
|||
assert microsoft_translate(u'English', 'en', 'es') == u'Inglés'
|
||||
assert microsoft_translate(u'Español', 'es', 'en') == u'Spanish'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
unittest.main()
|
||||
|
@ -170,5 +178,5 @@ if __name__ == '__main__':
|
|||
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.html_report(directory='tmp/coverage')
|
||||
cov.erase()
|
||||
|
|
|
@ -6,4 +6,3 @@ if sys.platform == 'win32':
|
|||
else:
|
||||
pybabel = 'flask/bin/pybabel'
|
||||
os.system(pybabel + ' compile -d app/translations')
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ else:
|
|||
if len(sys.argv) != 2:
|
||||
print "usage: tr_init <language-code>"
|
||||
sys.exit(1)
|
||||
os.system(pybabel + ' extract -F babel.cfg -k lazy_gettext -o messages.pot app')
|
||||
os.system(pybabel + ' init -i messages.pot -d app/translations -l ' + sys.argv[1])
|
||||
os.system(pybabel +
|
||||
' extract -F babel.cfg -k lazy_gettext -o messages.pot app')
|
||||
os.system(pybabel +
|
||||
' init -i messages.pot -d app/translations -l ' + sys.argv[1])
|
||||
os.unlink('messages.pot')
|
||||
|
||||
|
|
|
@ -8,4 +8,3 @@ else:
|
|||
os.system(pybabel + ' extract -F babel.cfg -k lazy_gettext -o messages.pot app')
|
||||
os.system(pybabel + ' update -i messages.pot -d app/translations')
|
||||
os.unlink('messages.pot')
|
||||
|
||||
|
|
2475
virtualenv.py
2475
virtualenv.py
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue