Chapter 21: User Notifications (v0.21)
This commit is contained in:
parent
93b3770418
commit
9d67b1ab7f
|
@ -41,3 +41,9 @@ class SearchForm(FlaskForm):
|
||||||
if 'meta' not in kwargs:
|
if 'meta' not in kwargs:
|
||||||
kwargs['meta'] = {'csrf': False}
|
kwargs['meta'] = {'csrf': False}
|
||||||
super(SearchForm, self).__init__(*args, **kwargs)
|
super(SearchForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageForm(FlaskForm):
|
||||||
|
message = TextAreaField(_l('Message'), validators=[
|
||||||
|
DataRequired(), Length(min=1, max=140)])
|
||||||
|
submit = SubmitField(_l('Submit'))
|
||||||
|
|
|
@ -5,8 +5,9 @@ from flask_login import current_user, login_required
|
||||||
from flask_babel import _, get_locale
|
from flask_babel import _, get_locale
|
||||||
from langdetect import detect, LangDetectException
|
from langdetect import detect, LangDetectException
|
||||||
from app import db
|
from app import db
|
||||||
from app.main.forms import EditProfileForm, EmptyForm, PostForm, SearchForm
|
from app.main.forms import EditProfileForm, EmptyForm, PostForm, SearchForm, \
|
||||||
from app.models import User, Post
|
MessageForm
|
||||||
|
from app.models import User, Post, Message, Notification
|
||||||
from app.translate import translate
|
from app.translate import translate
|
||||||
from app.main import bp
|
from app.main import bp
|
||||||
|
|
||||||
|
@ -170,3 +171,52 @@ def search():
|
||||||
if page > 1 else None
|
if page > 1 else None
|
||||||
return render_template('search.html', title=_('Search'), posts=posts,
|
return render_template('search.html', title=_('Search'), posts=posts,
|
||||||
next_url=next_url, prev_url=prev_url)
|
next_url=next_url, prev_url=prev_url)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/send_message/<recipient>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def send_message(recipient):
|
||||||
|
user = User.query.filter_by(username=recipient).first_or_404()
|
||||||
|
form = MessageForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
msg = Message(author=current_user, recipient=user,
|
||||||
|
body=form.message.data)
|
||||||
|
db.session.add(msg)
|
||||||
|
user.add_notification('unread_message_count', user.new_messages())
|
||||||
|
db.session.commit()
|
||||||
|
flash(_('Your message has been sent.'))
|
||||||
|
return redirect(url_for('main.user', username=recipient))
|
||||||
|
return render_template('send_message.html', title=_('Send Message'),
|
||||||
|
form=form, recipient=recipient)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/messages')
|
||||||
|
@login_required
|
||||||
|
def messages():
|
||||||
|
current_user.last_message_read_time = datetime.utcnow()
|
||||||
|
current_user.add_notification('unread_message_count', 0)
|
||||||
|
db.session.commit()
|
||||||
|
page = request.args.get('page', 1, type=int)
|
||||||
|
messages = current_user.messages_received.order_by(
|
||||||
|
Message.timestamp.desc()).paginate(
|
||||||
|
page=page, per_page=current_app.config['POSTS_PER_PAGE'],
|
||||||
|
error_out=False)
|
||||||
|
next_url = url_for('main.messages', page=messages.next_num) \
|
||||||
|
if messages.has_next else None
|
||||||
|
prev_url = url_for('main.messages', page=messages.prev_num) \
|
||||||
|
if messages.has_prev else None
|
||||||
|
return render_template('messages.html', messages=messages.items,
|
||||||
|
next_url=next_url, prev_url=prev_url)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/notifications')
|
||||||
|
@login_required
|
||||||
|
def notifications():
|
||||||
|
since = request.args.get('since', 0.0, type=float)
|
||||||
|
notifications = current_user.notifications.filter(
|
||||||
|
Notification.timestamp > since).order_by(Notification.timestamp.asc())
|
||||||
|
return jsonify([{
|
||||||
|
'name': n.name,
|
||||||
|
'data': n.get_data(),
|
||||||
|
'timestamp': n.timestamp
|
||||||
|
} for n in notifications])
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
import json
|
||||||
from time import time
|
from time import time
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
|
@ -79,6 +80,16 @@ class User(UserMixin, db.Model):
|
||||||
primaryjoin=(followers.c.followed_id == id),
|
primaryjoin=(followers.c.followed_id == id),
|
||||||
secondaryjoin=(followers.c.follower_id == id),
|
secondaryjoin=(followers.c.follower_id == id),
|
||||||
lazy='dynamic', back_populates='following')
|
lazy='dynamic', back_populates='following')
|
||||||
|
messages_sent = db.relationship('Message',
|
||||||
|
foreign_keys='Message.sender_id',
|
||||||
|
lazy='dynamic', back_populates='author')
|
||||||
|
messages_received = db.relationship('Message',
|
||||||
|
foreign_keys='Message.recipient_id',
|
||||||
|
lazy='dynamic',
|
||||||
|
back_populates='recipient')
|
||||||
|
last_message_read_time = db.Column(db.DateTime)
|
||||||
|
notifications = db.relationship('Notification', lazy='dynamic',
|
||||||
|
back_populates='user')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User {}>'.format(self.username)
|
return '<User {}>'.format(self.username)
|
||||||
|
@ -125,6 +136,17 @@ class User(UserMixin, db.Model):
|
||||||
return
|
return
|
||||||
return User.query.get(id)
|
return User.query.get(id)
|
||||||
|
|
||||||
|
def new_messages(self):
|
||||||
|
last_read_time = self.last_message_read_time or datetime(1900, 1, 1)
|
||||||
|
return Message.query.filter_by(recipient=self).filter(
|
||||||
|
Message.timestamp > last_read_time).count()
|
||||||
|
|
||||||
|
def add_notification(self, name, data):
|
||||||
|
self.notifications.filter_by(name=name).delete()
|
||||||
|
n = Notification(name=name, payload_json=json.dumps(data), user=self)
|
||||||
|
db.session.add(n)
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
@login.user_loader
|
@login.user_loader
|
||||||
def load_user(id):
|
def load_user(id):
|
||||||
|
@ -142,3 +164,30 @@ class Post(SearchableMixin, db.Model):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Post {}>'.format(self.body)
|
return '<Post {}>'.format(self.body)
|
||||||
|
|
||||||
|
|
||||||
|
class Message(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
sender_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
|
||||||
|
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
|
||||||
|
body = db.Column(db.String(140))
|
||||||
|
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||||
|
author = db.relationship('User', foreign_keys='Message.sender_id',
|
||||||
|
back_populates='messages_sent')
|
||||||
|
recipient = db.relationship('User', foreign_keys='Message.recipient_id',
|
||||||
|
back_populates='messages_received')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Message {}>'.format(self.body)
|
||||||
|
|
||||||
|
|
||||||
|
class Notification(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String(128), index=True)
|
||||||
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
|
||||||
|
timestamp = db.Column(db.Float, index=True, default=time)
|
||||||
|
payload_json = db.Column(db.Text)
|
||||||
|
user = db.relationship('User', back_populates='notifications')
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return json.loads(str(self.payload_json))
|
||||||
|
|
|
@ -39,6 +39,16 @@
|
||||||
<a class="nav-link" aria-current="page" href="{{ url_for('auth.login') }}">{{ _('Login') }}</a>
|
<a class="nav-link" aria-current="page" href="{{ url_for('auth.login') }}">{{ _('Login') }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" aria-current="page" href="{{ url_for('main.messages') }}">{{ _('Messages') }}
|
||||||
|
{% set new_messages = current_user.new_messages() %}
|
||||||
|
<span id="message_count" class="badge text-bg-danger"
|
||||||
|
style="visibility: {% if new_messages %}visible
|
||||||
|
{% else %}hidden{% endif %};">
|
||||||
|
{{ new_messages }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" aria-current="page" href="{{ url_for('main.user', username=current_user.username) }}">{{ _('Profile') }}</a>
|
<a class="nav-link" aria-current="page" href="{{ url_for('main.user', username=current_user.username) }}">{{ _('Profile') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -109,6 +119,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('DOMContentLoaded', initialize_popovers);
|
document.addEventListener('DOMContentLoaded', initialize_popovers);
|
||||||
|
|
||||||
|
function set_message_count(n) {
|
||||||
|
const count = document.getElementById('message_count');
|
||||||
|
count.innerText = n;
|
||||||
|
count.style.visibility = n ? 'visible' : 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
function initialize_notifications() {
|
||||||
|
let since = 0;
|
||||||
|
setInterval(async function() {
|
||||||
|
const response = await fetch('{{ url_for('main.notifications') }}?since=' + since);
|
||||||
|
const notifications = await response.json();
|
||||||
|
for (let i = 0; i < notifications.length; i++) {
|
||||||
|
if (notifications[i].name == 'unread_message_count')
|
||||||
|
set_message_count(notifications[i].data);
|
||||||
|
since = notifications[i].timestamp;
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', initialize_notifications);
|
||||||
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{ _('Messages') }}</h1>
|
||||||
|
{% for post in messages %}
|
||||||
|
{% include '_post.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
<nav aria-label="Post navigation">
|
||||||
|
<ul class="pagination">
|
||||||
|
<li class="page-item{% if not prev_url %} disabled{% endif %}">
|
||||||
|
<a class="page-link" href="#">
|
||||||
|
<span aria-hidden="true">←</span> {{ _('Newer messages') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="page-item{% if not next_url %} disabled{% endif %}">
|
||||||
|
<a class="page-link" href="#">
|
||||||
|
{{ _('Older messages') }} <span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% import "bootstrap_wtf.html" as wtf %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{ _('Send Message to %(recipient)s', recipient=recipient) }}</h1>
|
||||||
|
{{ wtf.quick_form(form) }}
|
||||||
|
{% endblock %}
|
|
@ -28,6 +28,9 @@
|
||||||
</form>
|
</form>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if user != current_user %}
|
||||||
|
<p><a href="{{ url_for('main.send_message', recipient=user.username) }}">{{ _('Send private message') }}</a></p>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -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-11-25 18:23-0800\n"
|
"POT-Creation-Date: 2017-11-25 18:26-0800\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"
|
||||||
|
@ -94,7 +94,7 @@ msgstr "Tu contraseña ha sido cambiada."
|
||||||
msgid "About me"
|
msgid "About me"
|
||||||
msgstr "Acerca de mí"
|
msgstr "Acerca de mí"
|
||||||
|
|
||||||
#: app/main/forms.py:13 app/main/forms.py:28
|
#: app/main/forms.py:13 app/main/forms.py:28 app/main/forms.py:44
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Enviar"
|
msgstr "Enviar"
|
||||||
|
|
||||||
|
@ -106,47 +106,59 @@ msgstr "Dí algo"
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Buscar"
|
msgstr "Buscar"
|
||||||
|
|
||||||
|
#: app/main/forms.py:43
|
||||||
|
msgid "Message"
|
||||||
|
msgstr "Mensaje"
|
||||||
|
|
||||||
#: app/main/routes.py:36
|
#: app/main/routes.py:36
|
||||||
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/main/routes.py:87
|
#: app/main/routes.py:94
|
||||||
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/main/routes.py:92 app/templates/edit_profile.html:5
|
#: app/main/routes.py:99 app/templates/edit_profile.html:5
|
||||||
msgid "Edit Profile"
|
msgid "Edit Profile"
|
||||||
msgstr "Editar Perfil"
|
msgstr "Editar Perfil"
|
||||||
|
|
||||||
#: app/main/routes.py:101 app/main/routes.py:117
|
#: app/main/routes.py:108 app/main/routes.py:124
|
||||||
#, 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/main/routes.py:104
|
#: app/main/routes.py:111
|
||||||
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/main/routes.py:108
|
#: app/main/routes.py:115
|
||||||
#, 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/main/routes.py:120
|
#: app/main/routes.py:127
|
||||||
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/main/routes.py:124
|
#: app/main/routes.py:131
|
||||||
#, 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/templates/_post.html:14
|
#: app/main/routes.py:170
|
||||||
|
msgid "Your message has been sent."
|
||||||
|
msgstr "Tu mensaje ha sido enviado."
|
||||||
|
|
||||||
|
#: app/main/routes.py:172
|
||||||
|
msgid "Send Message"
|
||||||
|
msgstr "Enviar Mensaje"
|
||||||
|
|
||||||
|
#: app/templates/_post.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
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:25
|
#: app/templates/_post.html:27
|
||||||
msgid "Translate"
|
msgid "Translate"
|
||||||
msgstr "Traducir"
|
msgstr "Traducir"
|
||||||
|
|
||||||
|
@ -166,15 +178,19 @@ msgstr "Explorar"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Ingresar"
|
msgstr "Ingresar"
|
||||||
|
|
||||||
#: app/templates/base.html:35
|
#: app/templates/base.html:36 app/templates/messages.html:4
|
||||||
|
msgid "Messages"
|
||||||
|
msgstr "Mensajes"
|
||||||
|
|
||||||
|
#: app/templates/base.html:45
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Perfil"
|
msgstr "Perfil"
|
||||||
|
|
||||||
#: app/templates/base.html:36
|
#: app/templates/base.html:46
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Salir"
|
msgstr "Salir"
|
||||||
|
|
||||||
#: app/templates/base.html:73
|
#: app/templates/base.html:83
|
||||||
msgid "Error: Could not contact server."
|
msgid "Error: Could not contact server."
|
||||||
msgstr "Error: el servidor no pudo ser contactado."
|
msgstr "Error: el servidor no pudo ser contactado."
|
||||||
|
|
||||||
|
@ -183,40 +199,53 @@ msgstr "Error: el servidor no pudo ser contactado."
|
||||||
msgid "Hi, %(username)s!"
|
msgid "Hi, %(username)s!"
|
||||||
msgstr "¡Hola, %(username)s!"
|
msgstr "¡Hola, %(username)s!"
|
||||||
|
|
||||||
#: app/templates/index.html:17 app/templates/user.html:31
|
#: app/templates/index.html:17 app/templates/user.html:34
|
||||||
msgid "Newer posts"
|
msgid "Newer posts"
|
||||||
msgstr "Artículos siguientes"
|
msgstr "Artículos siguientes"
|
||||||
|
|
||||||
#: app/templates/index.html:22 app/templates/user.html:36
|
#: app/templates/index.html:22 app/templates/user.html:39
|
||||||
msgid "Older posts"
|
msgid "Older posts"
|
||||||
msgstr "Artículos previos"
|
msgstr "Artículos previos"
|
||||||
|
|
||||||
|
#: app/templates/messages.html:12
|
||||||
|
msgid "Newer messages"
|
||||||
|
msgstr "Mensajes siguientes"
|
||||||
|
|
||||||
|
#: app/templates/messages.html:17
|
||||||
|
msgid "Older messages"
|
||||||
|
msgstr "Mensajes previos"
|
||||||
|
|
||||||
#: app/templates/search.html:4
|
#: app/templates/search.html:4
|
||||||
msgid "Search Results"
|
msgid "Search Results"
|
||||||
msgstr "Resultados de Búsqueda"
|
msgstr ""
|
||||||
|
|
||||||
#: app/templates/search.html:12
|
#: app/templates/search.html:12
|
||||||
msgid "Previous results"
|
msgid "Previous results"
|
||||||
msgstr "Resultados previos"
|
msgstr ""
|
||||||
|
|
||||||
#: app/templates/search.html:17
|
#: app/templates/search.html:17
|
||||||
msgid "Next results"
|
msgid "Next results"
|
||||||
msgstr "Resultados próximos"
|
msgstr ""
|
||||||
|
|
||||||
|
#: app/templates/send_message.html:5
|
||||||
|
#, python-format
|
||||||
|
msgid "Send Message to %(recipient)s"
|
||||||
|
msgstr "Enviar Mensaje a %(recipient)s"
|
||||||
|
|
||||||
#: app/templates/user.html:8
|
#: app/templates/user.html:8
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Usuario"
|
msgstr "Usuario"
|
||||||
|
|
||||||
#: app/templates/user.html:11
|
#: app/templates/user.html:11 app/templates/user_popup.html:9
|
||||||
msgid "Last seen on"
|
msgid "Last seen on"
|
||||||
msgstr "Última visita"
|
msgstr "Última visita"
|
||||||
|
|
||||||
#: app/templates/user.html:13
|
#: app/templates/user.html:13 app/templates/user_popup.html:11
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(count)d followers"
|
msgid "%(count)d followers"
|
||||||
msgstr "%(count)d seguidores"
|
msgstr "%(count)d seguidores"
|
||||||
|
|
||||||
#: app/templates/user.html:13
|
#: app/templates/user.html:13 app/templates/user_popup.html:11
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(count)d following"
|
msgid "%(count)d following"
|
||||||
msgstr "siguiendo a %(count)d"
|
msgstr "siguiendo a %(count)d"
|
||||||
|
@ -225,14 +254,18 @@ msgstr "siguiendo a %(count)d"
|
||||||
msgid "Edit your profile"
|
msgid "Edit your profile"
|
||||||
msgstr "Editar tu perfil"
|
msgstr "Editar tu perfil"
|
||||||
|
|
||||||
#: app/templates/user.html:17
|
#: app/templates/user.html:17 app/templates/user_popup.html:14
|
||||||
msgid "Follow"
|
msgid "Follow"
|
||||||
msgstr "Seguir"
|
msgstr "Seguir"
|
||||||
|
|
||||||
#: app/templates/user.html:19
|
#: app/templates/user.html:19 app/templates/user_popup.html:16
|
||||||
msgid "Unfollow"
|
msgid "Unfollow"
|
||||||
msgstr "Dejar de seguir"
|
msgstr "Dejar de seguir"
|
||||||
|
|
||||||
|
#: app/templates/user.html:22
|
||||||
|
msgid "Send private message"
|
||||||
|
msgstr "Enviar mensaje privado"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:12
|
#: app/templates/auth/login.html:12
|
||||||
msgid "New User?"
|
msgid "New User?"
|
||||||
msgstr "¿Usuario Nuevo?"
|
msgstr "¿Usuario Nuevo?"
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from app import create_app, db
|
from app import create_app, db
|
||||||
from app.models import User, Post
|
from app.models import User, Post, Message, Notification
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
|
|
||||||
@app.shell_context_processor
|
@app.shell_context_processor
|
||||||
def make_shell_context():
|
def make_shell_context():
|
||||||
return {'db': db, 'User': User, 'Post': Post}
|
return {'db': db, 'User': User, 'Post': Post, 'Message': Message,
|
||||||
|
'Notification': Notification}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
"""private messages
|
||||||
|
|
||||||
|
Revision ID: d049de007ccf
|
||||||
|
Revises: 834b1a697901
|
||||||
|
Create Date: 2017-11-12 23:30:28.571784
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'd049de007ccf'
|
||||||
|
down_revision = '2b017edaa91f'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('message',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('sender_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('recipient_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('body', sa.String(length=140), nullable=True),
|
||||||
|
sa.Column('timestamp', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['recipient_id'], ['user.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sender_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
with op.batch_alter_table('message', schema=None) as batch_op:
|
||||||
|
batch_op.create_index(batch_op.f('ix_message_recipient_id'), ['recipient_id'], unique=False)
|
||||||
|
batch_op.create_index(batch_op.f('ix_message_sender_id'), ['sender_id'], unique=False)
|
||||||
|
batch_op.create_index(batch_op.f('ix_message_timestamp'), ['timestamp'], unique=False)
|
||||||
|
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('last_message_read_time', sa.DateTime(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('last_message_read_time')
|
||||||
|
|
||||||
|
with op.batch_alter_table('notification', schema=None) as batch_op:
|
||||||
|
batch_op.drop_index(batch_op.f('ix_notification_timestamp'))
|
||||||
|
batch_op.drop_index(batch_op.f('ix_message_sender_id'))
|
||||||
|
batch_op.drop_index(batch_op.f('ix_message_recipient_id'))
|
||||||
|
|
||||||
|
op.drop_table('message')
|
||||||
|
# ### end Alembic commands ###
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""notifications
|
||||||
|
|
||||||
|
Revision ID: f7ac3d27bb1d
|
||||||
|
Revises: d049de007ccf
|
||||||
|
Create Date: 2017-11-22 19:48:39.945858
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'f7ac3d27bb1d'
|
||||||
|
down_revision = 'd049de007ccf'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('notification',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=True),
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('timestamp', sa.Float(), nullable=True),
|
||||||
|
sa.Column('payload_json', sa.Text(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
with op.batch_alter_table('notification', schema=None) as batch_op:
|
||||||
|
batch_op.create_index(batch_op.f('ix_notification_name'), ['name'], unique=False)
|
||||||
|
batch_op.create_index(batch_op.f('ix_notification_timestamp'), ['timestamp'], unique=False)
|
||||||
|
batch_op.create_index(batch_op.f('ix_notification_user_id'), ['user_id'], unique=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('notification', schema=None) as batch_op:
|
||||||
|
batch_op.drop_index(batch_op.f('ix_notification_user_id'))
|
||||||
|
batch_op.drop_index(batch_op.f('ix_notification_timestamp'))
|
||||||
|
batch_op.drop_index(batch_op.f('ix_notification_name'))
|
||||||
|
|
||||||
|
op.drop_table('notification')
|
||||||
|
# ### end Alembic commands ###
|
Loading…
Reference in New Issue