Browse Source

Доделал личные сообщения

devel
bashmak 9 years ago
parent
commit
17e33af1bb
  1. 7
      accounts_app/models.py
  2. 2
      msg_app/forms.py
  3. 161
      msg_app/locale/ru/LC_MESSAGES/django.po
  4. 103
      msg_app/migrations/0001_initial.py
  5. 37
      msg_app/models.py
  6. 15
      msg_app/templates/msg_app/chat.html
  7. 8
      msg_app/templates/msg_app/conversations.html
  8. 3
      msg_app/urls.py
  9. 28
      msg_app/views.py
  10. 1
      requirements.txt
  11. 6
      templates/base.html

7
accounts_app/models.py

@ -90,9 +90,10 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
def get_min_ava(self):
if self.avatar:
path = self.avatar.min()
if os.path.exists(path):
return path
url_path = self.avatar.min()
real_path = url_path[1:]
if os.path.exists(real_path):
return url_path
else:
return DEFAULT_PICTURE
else:

2
msg_app/forms.py

@ -17,7 +17,7 @@ class ConversationForm(forms.ModelForm):
class Meta:
model = Conversation
exclude = ['date_create']
exclude = ['date_create', 'author']
def create(self, author):
participants = self.cleaned_data['participants']

161
msg_app/locale/ru/LC_MESSAGES/django.po

@ -0,0 +1,161 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Dmitry Novikov nerosketch@gmail.com, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-04 17:52+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: forms.py:25
msgid "you must choose at least 1 participants"
msgstr "вы должны выбрать как минимум одного участника"
#: models.py:14 templates/msg_app/conversations.html.py:54
msgid "New"
msgstr "Новая"
#: models.py:15
msgid "Seen"
msgstr "Просмотренное"
#: models.py:16
msgid "Deleted"
msgstr "Удалённое"
#: models.py:28
msgid "Message status"
msgstr "Статус сообщения"
#: models.py:29
msgid "Messages statuses"
msgstr "Статусы сообщений"
#: models.py:33
msgid "Body"
msgstr "Тело"
#: models.py:34
msgid "sent at"
msgstr "отправлено"
#: models.py:36 models.py:234
msgid "Conversation"
msgstr "Беседа"
#: models.py:66
msgid "Message"
msgstr "Сообщение"
#: models.py:67
msgid "Messages"
msgstr "Сообщения"
#: models.py:69
msgid "Can view messages"
msgstr "может просматривать сообщения"
#: models.py:77
msgid "Admin"
msgstr "Админ"
#: models.py:78
msgid "Guest"
msgstr "Гость"
#: models.py:79
msgid "Banned user"
msgstr "Запрещённый пользователь"
#: models.py:80
msgid "Inviter"
msgstr "Создатель"
#: models.py:90
msgid "Conversation membership"
msgstr "Членство в беседе"
#: models.py:91
msgid "Conversation memberships"
msgstr "Членства в беседах"
#: models.py:100
msgid "Participant profile does not found"
msgstr "Учётная запись участника не найдена"
#: models.py:110
msgid "No name"
msgstr "Без имени"
#: models.py:235 templates/msg_app/conversations.html.py:14
msgid "Conversations"
msgstr "Беседы"
#: models.py:237
msgid "Can view conversation"
msgstr "Может просматривать беседы"
#: templates/msg_app/chat.html:7 templates/msg_app/conversations.html.py:7
msgid "Private messages"
msgstr "Личные сообщения"
#: templates/msg_app/chat.html:16
msgid "peoples"
msgstr "участников"
#: templates/msg_app/chat.html:26
msgid "Delete"
msgstr "Удалить"
#: templates/msg_app/chat.html:58
msgid "Message history is empty"
msgstr "История сообщений пуста"
#: templates/msg_app/chat.html:75
msgid "Send"
msgstr "Отправить"
#: templates/msg_app/conversations.html:32
#, python-format
msgid "%(participants_count)s participants, %(msg_count)s messages"
msgstr "%(participants_count)s участников, %(msg_count)s сообщений"
#: templates/msg_app/conversations.html:38
msgid "No messages found"
msgstr "Сообщения не найдены"
#: templates/msg_app/conversations.html:47
msgid "Any conversations not found"
msgstr "Не найдено ни одной беседы"
#: templates/msg_app/modal_new_conversation.html:5
msgid "Create conversation"
msgstr "Создать беседу"
#: templates/msg_app/modal_new_conversation.html:19
msgid "for select multiple press ctrl and click on field"
msgstr "Для выбора нескольких ывриантов зажмите ctrl и кликните вариант"
#: templates/msg_app/modal_new_conversation.html:28
msgid "Add"
msgstr "Добавить"
#: views.py:26
msgid "Conversation has been created"
msgstr "Беседа создана"
#: views.py:29 views.py:48
msgid "fix form errors"
msgstr "заполните все поля в соответствии с их форматом"

103
msg_app/migrations/0001_initial.py

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-10-04 17:44
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Conversation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=32)),
('date_create', models.DateTimeField(auto_now_add=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Conversation',
'verbose_name_plural': 'Conversations',
'db_table': 'conversations',
'permissions': (('can_view_conversation', 'Can view conversation'),),
},
),
migrations.CreateModel(
name='ConversationMembership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('adm', 'Admin'), ('gst', 'Guest'), ('ban', 'Banned user'), ('inv', 'Inviter')], default='gst', max_length=3)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to=settings.AUTH_USER_MODEL)),
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='msg_app.Conversation')),
('who_invite_that_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='self_conversations', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Conversation membership',
'verbose_name_plural': 'Conversation memberships',
'db_table': 'conversation_memberships',
},
),
migrations.CreateModel(
name='Message',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField(verbose_name='Body')),
('sent_at', models.DateTimeField(auto_now_add=True, verbose_name='sent at')),
('attachment', models.FileField(blank=True, null=True, upload_to='messages_attachments/%Y_%m_%d')),
],
options={
'verbose_name': 'Message',
'verbose_name_plural': 'Messages',
'db_table': 'messages',
'ordering': ['-sent_at'],
'permissions': (('can_view_messages', 'Can view messages'),),
},
),
migrations.CreateModel(
name='MessageStatus',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state', models.CharField(choices=[('new', 'New'), ('old', 'Seen'), ('del', 'Deleted')], default='new', max_length=3)),
('msg', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='msg_statuses', to='msg_app.Message')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usr_msg_status', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Message status',
'verbose_name_plural': 'Messages statuses',
'db_table': 'message_status',
},
),
migrations.AddField(
model_name='message',
name='account_status',
field=models.ManyToManyField(through='msg_app.MessageStatus', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='message',
name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='message',
name='conversation',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='msg_app.Conversation', verbose_name='Conversation'),
),
migrations.AddField(
model_name='conversation',
name='participants',
field=models.ManyToManyField(related_name='conversations', through='msg_app.ConversationMembership', to=settings.AUTH_USER_MODEL),
),
migrations.AlterUniqueTogether(
name='messagestatus',
unique_together=set([('msg', 'user', 'state')]),
),
]

37
msg_app/models.py

@ -8,7 +8,7 @@ class MessageError(Exception):
class MessageStatus(models.Model):
msg = models.ForeignKey('Message')
msg = models.ForeignKey('Message', related_name='msg_statuses')
user = models.ForeignKey(UserProfile, related_name='usr_msg_status')
MESSAGE_STATES = (
('new', _('New')),
@ -17,6 +17,9 @@ class MessageStatus(models.Model):
)
state = models.CharField(max_length=3, choices=MESSAGE_STATES, default='new')
def __str__(self):
return "%s for %s (%s)" % (self.get_state_display(), self.user, self.msg)
class Meta:
db_table = 'message_status'
unique_together = (
@ -30,12 +33,12 @@ class Message(models.Model):
text = models.TextField(_("Body"))
sent_at = models.DateTimeField(_("sent at"), auto_now_add=True)
author = models.ForeignKey(UserProfile, related_name='messages')
conversation = models.ForeignKey('Conversation', verbose_name=_('Dialog'))
conversation = models.ForeignKey('Conversation', verbose_name=_('Conversation'))
attachment = models.FileField(upload_to='messages_attachments/%Y_%m_%d', blank=True, null=True)
account_status = models.ManyToManyField(UserProfile, through=MessageStatus, through_fields=('msg', 'user'))
def __str__(self):
return self.text[9:]
return self.text[:9]
def _set_status(self, account, code):
try:
@ -107,7 +110,7 @@ class ConversationManager(models.Manager):
title = _('No name')
else:
title = ', '.join(usernames)
conversation = self.create(title=title)
conversation = self.create(title=title, author=author)
for acc in other_participants:
ConversationMembership.objects.create(
account=acc, conversation=conversation, status='adm', who_invite_that_user=author
@ -122,12 +125,19 @@ class ConversationManager(models.Manager):
ms_count = MessageStatus.objects.filter(user=account, state='new').count()
return ms_count
def fetch(self, account):
conversations = self.filter(models.Q(author=account) | models.Q(participants__in=[account])).annotate(
msg_count=models.Count('message', distinct=True)
)
return conversations
class Conversation(models.Model):
title = models.CharField(max_length=32)
participants = models.ManyToManyField(UserProfile, related_name='conversations',
through='ConversationMembership',
through_fields=('conversation', 'account'))
author = models.ForeignKey(UserProfile)
date_create = models.DateTimeField(auto_now_add=True)
def __str__(self):
@ -158,13 +168,6 @@ class Conversation(models.Model):
MessageStatus.objects.create(msg=msg, user=participant)
return msg
def get_author(self):
try:
accs = ConversationMembership.objects.filter(status='inv', conversation=self)
return accs[0].account
except IndexError:
pass
def remove_message(self, msg):
if isinstance(msg, Message):
m = msg
@ -213,6 +216,18 @@ class Conversation(models.Model):
def find_messages_by_text(self, text):
return Message.objects.filter(text__icontains=text, conversation=self)
def _make_messages_status(self, account, status):
qs = MessageStatus.objects.filter(msg__conversation=self, user=account).exclude(state='del')
if status != 'del':
qs = qs.exclude(state=status)
return qs.update(state=status)
def make_messages_status_new(self, account):
return self._make_messages_status(account, 'new')
def make_messages_status_old(self, account):
return self._make_messages_status(account, 'old')
class Meta:
db_table = 'conversations'
verbose_name = _("Conversation")

15
msg_app/templates/msg_app/chat.html

@ -13,7 +13,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ conv.title }}
<small>2 {% trans 'peoples' %}</small>
<small>{{ conv.participants.count }} {% trans 'peoples' %}</small>
</h3>
</div>
<div class="list-group">
@ -21,7 +21,12 @@
{% with can_view_profile=perms.accounts_app.can_view_userprofile %}
{% for msg in msg_list %}
{% with author=msg.author %}
<div class="list-group-item">
<div class="list-group-item clearfix">
{% if msg.author == request.user %}
<a href="{% url 'msg_app:remove_msg' conv.pk msg.pk %}" class="btn btn-link pull-right" title="{% trans 'Delete' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
<div class="row">
<div class="col-sm-1">
{% if can_view_profile %}
@ -66,12 +71,12 @@
</div>
<div class="btn-group">
<button class="btn btn-default">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-send"></span> {% trans 'Send' %}
</button>
<button class="btn btn-default">
<!--<button class="btn btn-default" type="button">
<span class="glyphicon glyphicon-tag"></span> { % trans 'Attach' % }
</button>
</button>-->
</div>
<small>Если кто-то будет пользоваться то усовершенствую личные сообщения</small>

8
msg_app/templates/msg_app/conversations.html

@ -15,11 +15,11 @@
</div>
<div class="list-group">
{% for conv in conversations %}
{% with author=conv.get_author new_count=conv.get_messages_new_count last_msg=conv.last_message %}
{% with last_msg=conv.last_message %}
<a href="{% url 'msg_app:to_conversation' conv.pk %}" class="list-group-item">
<div class="row">
<div class="col-sm-1">
<img src="{{ author.get_min_ava }}" class="img-thumbnail">
<img src="{{ conv.author.get_min_ava }}" class="img-thumbnail">
</div>
<div class="col-sm-11">
@ -29,7 +29,7 @@
</div>
{% endif %}
<h4>{{ conv.title }}
<small>{{ conv.participants.count }} participants{% if new_count > 0 %}, <span class="label label-primary">{{ new_count }} new</span>{% endif %}</small>
<small>{% blocktrans with participants_count=conv.participants.count msg_count=conv.msg_count %}{{ participants_count }} participants, {{ msg_count }} messages{% endblocktrans %}</small>
</h4>
{% if last_msg %}
@ -57,4 +57,6 @@
</div>
</div>
{% include 'toolbar_page.html' with pag=conversations %}
{% endblock %}

3
msg_app/urls.py

@ -5,5 +5,6 @@ from . import views
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^new$', views.new_conversation, name='new_conversation'),
url(r'^(?P<conv_id>\d+)/$', views.to_conversation, name='to_conversation')
url(r'^(?P<conv_id>\d+)/$', views.to_conversation, name='to_conversation'),
url(r'^(?P<conv_id>\d+)/(?P<msg_id>\d+)/del$', views.remove_msg, name='remove_msg')
]

28
msg_app/views.py

@ -1,15 +1,20 @@
from django.contrib.auth.decorators import login_required
from django.contrib.gis.shortcuts import render_to_text
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404
from .models import Conversation, MessageError
from mydefs import pag_mn
from .models import Conversation, MessageError, Message
from .forms import ConversationForm, MessageForm
@login_required
def home(request):
conversations = Conversation.objects.filter(participants__in=[request.user])
# TODO: optimise queries
conversations = Conversation.objects.fetch(request.user)
conversations = pag_mn(request, conversations, 8)
return render(request, 'msg_app/conversations.html', {
'conversations': conversations
})
@ -19,10 +24,14 @@ def home(request):
def new_conversation(request):
try:
frm = ConversationForm(request.POST or None)
if request.method == 'POST' and frm.is_valid():
if request.method == 'POST':
if frm.is_valid():
conv = frm.create(request.user)
messages.success(request, _('Conversation has been created'))
return redirect('msg_app:to_conversation', conv.pk)
else:
messages.error(request, _('fix form errors'))
else:
return render_to_text('msg_app/modal_new_conversation.html', {
'form': frm
}, request=request)
@ -40,8 +49,9 @@ def to_conversation(request, conv_id):
if frm.is_valid():
frm.create(conv, request.user)
else:
print(frm)
messages.error(request, _('fix form errors'))
else:
conv.make_messages_status_old(request.user)
msg_list = conv.get_messages()
return render(request, 'msg_app/chat.html', {
'conv': conv,
@ -50,3 +60,13 @@ def to_conversation(request, conv_id):
except MessageError as e:
messages.error(request, e)
return redirect('msg_app:home')
@login_required
def remove_msg(request, conv_id, msg_id):
msg = get_object_or_404(Message, pk=msg_id)
if msg.author != request.user:
raise PermissionDenied
conversation_id = msg.conversation.pk
msg.delete()
return redirect('msg_app:to_conversation', conversation_id)

1
requirements.txt

@ -15,4 +15,3 @@ easysnmp
rq
pid
django-guardian
jsonfield

6
templates/base.html

@ -8,11 +8,7 @@
<div class="col-sm-5">
<a href="{% url 'acc_app:profile' %}">
{% if user.avatar %}
<img alt="авка" class="img-circle img-responsive" src="{{ user.avatar.min }}"/>
{% else %}
<img src="/static/img/user_ava_min.gif" class="img-circle img-responsive" alt="авка"/>
{% endif %}
<img alt="авка" class="img-circle img-responsive" src="{{ user.get_min_ava }}"/>
</a>
</div>

Loading…
Cancel
Save