From 17e33af1bbfff21c330b9256a81d37887246fb88 Mon Sep 17 00:00:00 2001 From: bashmak Date: Wed, 4 Oct 2017 18:07:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=87=D0=BD=D1=8B=D0=B5=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accounts_app/models.py | 7 +- msg_app/forms.py | 2 +- msg_app/locale/ru/LC_MESSAGES/django.po | 161 +++++++++++++++++++ msg_app/migrations/0001_initial.py | 103 ++++++++++++ msg_app/models.py | 37 +++-- msg_app/templates/msg_app/chat.html | 17 +- msg_app/templates/msg_app/conversations.html | 8 +- msg_app/urls.py | 3 +- msg_app/views.py | 40 +++-- requirements.txt | 1 - templates/base.html | 6 +- 11 files changed, 344 insertions(+), 41 deletions(-) create mode 100644 msg_app/locale/ru/LC_MESSAGES/django.po create mode 100644 msg_app/migrations/0001_initial.py diff --git a/accounts_app/models.py b/accounts_app/models.py index 3c7c9ad..731b432 100644 --- a/accounts_app/models.py +++ b/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: diff --git a/msg_app/forms.py b/msg_app/forms.py index c489cd9..fd99831 100644 --- a/msg_app/forms.py +++ b/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'] diff --git a/msg_app/locale/ru/LC_MESSAGES/django.po b/msg_app/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..de5da31 --- /dev/null +++ b/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 "заполните все поля в соответствии с их форматом" diff --git a/msg_app/migrations/0001_initial.py b/msg_app/migrations/0001_initial.py new file mode 100644 index 0000000..c47429f --- /dev/null +++ b/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')]), + ), + ] diff --git a/msg_app/models.py b/msg_app/models.py index f12b29f..a1d0eaa 100644 --- a/msg_app/models.py +++ b/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") diff --git a/msg_app/templates/msg_app/chat.html b/msg_app/templates/msg_app/chat.html index 57e86b8..5ee0394 100644 --- a/msg_app/templates/msg_app/chat.html +++ b/msg_app/templates/msg_app/chat.html @@ -13,7 +13,7 @@

{{ conv.title }} - 2 {% trans 'peoples' %} + {{ conv.participants.count }} {% trans 'peoples' %}

@@ -21,7 +21,12 @@ {% with can_view_profile=perms.accounts_app.can_view_userprofile %} {% for msg in msg_list %} {% with author=msg.author %} -
+
+ {% if msg.author == request.user %} + + + + {% endif %}
{% if can_view_profile %} @@ -66,12 +71,12 @@
- - +
Если кто-то будет пользоваться то усовершенствую личные сообщения diff --git a/msg_app/templates/msg_app/conversations.html b/msg_app/templates/msg_app/conversations.html index 077bf76..b39e2e0 100644 --- a/msg_app/templates/msg_app/conversations.html +++ b/msg_app/templates/msg_app/conversations.html @@ -15,11 +15,11 @@
+ {% include 'toolbar_page.html' with pag=conversations %} + {% endblock %} diff --git a/msg_app/urls.py b/msg_app/urls.py index 68d19c8..ff34b05 100644 --- a/msg_app/urls.py +++ b/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\d+)/$', views.to_conversation, name='to_conversation') + url(r'^(?P\d+)/$', views.to_conversation, name='to_conversation'), + url(r'^(?P\d+)/(?P\d+)/del$', views.remove_msg, name='remove_msg') ] diff --git a/msg_app/views.py b/msg_app/views.py index f456faf..5e6d62c 100644 --- a/msg_app/views.py +++ b/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,13 +24,17 @@ def home(request): def new_conversation(request): try: frm = ConversationForm(request.POST or None) - if request.method == 'POST' and frm.is_valid(): - conv = frm.create(request.user) - messages.success(request, _('Conversation has been created')) - return redirect('msg_app:to_conversation', conv.pk) - return render_to_text('msg_app/modal_new_conversation.html', { - 'form': frm - }, request=request) + 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) except MessageError as e: messages.error(request, e) return redirect('msg_app:home') @@ -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) diff --git a/requirements.txt b/requirements.txt index 447c20d..9dcb617 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,3 @@ easysnmp rq pid django-guardian -jsonfield diff --git a/templates/base.html b/templates/base.html index b5a456f..813983d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -8,11 +8,7 @@