From e390d8c250d34960d332610da2c85313e670177a Mon Sep 17 00:00:00 2001 From: Mukhtar Date: Wed, 14 Sep 2016 15:23:58 +0300 Subject: [PATCH] #ARC-18 Fixes --- api/urls.py | 2 +- api/views.py | 20 +- archilance/settings/base.py | 2 +- assets/css/jquery.jgrowl.min.css | 1 + assets/css/main.css | 5 +- assets/js/chat.js | 171 +++++++++++------- assets/js/chat_contractor.js | 28 ++- chat/admin.py | 3 +- chat/chat.py | 20 +- chat/filters.py | 2 + chat/migrations/0014_newmessage.py | 30 +++ chat/models.py | 12 ++ chat/templates/chat_contractor.html | 18 +- chat/templates/chat_customer.html | 83 ++++++--- chat/templates/review_add_modal.html | 4 +- .../migrations/0030_auto_20160912_1912.py | 35 ++++ projects/models.py | 7 + templates/partials/base.html | 3 +- templates/partials/header.html | 4 +- users/serializers.py | 7 +- .../templatetags/user_new_count_orders.html | 1 + users/templatetags/user_tags.py | 18 +- wallets/views.py | 30 ++- .../migrations/0013_auto_20160912_1912.py | 20 ++ 24 files changed, 391 insertions(+), 135 deletions(-) create mode 100644 assets/css/jquery.jgrowl.min.css create mode 100644 chat/migrations/0014_newmessage.py create mode 100644 projects/migrations/0030_auto_20160912_1912.py create mode 100644 users/templates/templatetags/user_new_count_orders.html create mode 100644 work_sell/migrations/0013_auto_20160912_1912.py diff --git a/api/urls.py b/api/urls.py index e8b90c2..79287ed 100755 --- a/api/urls.py +++ b/api/urls.py @@ -30,7 +30,7 @@ router.register(r'contractorresume', ContractorResumeViewSet) router.register(r'contractorresumefiles', ContractorResumeFilesViewSet) router.register(r'documents', DocumentViewSet) router.register(r'locations', LocationViewSet) -router.register(r'message', MessageViewSet) +router.register(r'message', MessageViewSet, base_name='Message') router.register(r'note', NoteViewSet) router.register(r'orders', OrderViewSet) router.register(r'portfolio-photos', PortfolioPhotoViewSet) diff --git a/api/views.py b/api/views.py index a05e91a..27d998f 100755 --- a/api/views.py +++ b/api/views.py @@ -28,7 +28,7 @@ from common.models import Location from common.serializers import LocationSerializer from common.filters import LocationFilterSet -from chat.models import Message, Notes, Documents +from chat.models import Message, Notes, Documents, NewMessage from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet @@ -112,12 +112,12 @@ class NoteViewSet(ModelViewSet): class MessageViewSet(ModelViewSet): - queryset = Message.objects.all() + # queryset = Message.objects.all() serializer_class = MessageSerializer filter_class = MessageFilterSet def get_queryset(self): - queryset = Message.objects.filter(is_delete=False) + queryset = Message.objects.all() search_param = self.request.query_params.get('operand', None) recipent_id = self.request.query_params.get('recipent_id', None) sender_id = self.request.query_params.get('sender_id', None) @@ -126,7 +126,15 @@ class MessageViewSet(ModelViewSet): if search_param == 'in': queryset = queryset.filter(Q(sender__in=[sender_id,recipent_id]),Q(recipent__in=[sender_id,recipent_id])).\ filter(order__isnull=True).\ - filter(~Q(sender=F('recipent'))).order_by('created') + filter(~Q(sender=F('recipent'))) + + + queryset = queryset.order_by('created') + return queryset + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + qs = NewMessage.objects.filter(message__in=queryset, user=self.request.user).delete() return queryset @@ -167,13 +175,12 @@ class LocationViewSet(ModelViewSet): filter_class = LocationFilterSet - - class PortfolioPagination(PageNumberPagination): page_size = settings.API_PAGE_SIZE # Default page size page_size_query_param = 'page_size' # Provide custom page size through a query param max_page_size = 1000 + class PortfolioViewSet(ModelViewSet): queryset = Portfolio.objects.all() serializer_class = PortfolioSerializer @@ -181,7 +188,6 @@ class PortfolioViewSet(ModelViewSet): pagination_class = PortfolioPagination - class PortfolioPhotoViewSet(ModelViewSet): queryset = PortfolioPhoto.objects.all() serializer_class = PortfolioPhotoSerializer diff --git a/archilance/settings/base.py b/archilance/settings/base.py index 86b3daf..42c8493 100644 --- a/archilance/settings/base.py +++ b/archilance/settings/base.py @@ -266,7 +266,7 @@ if DEBUG: EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' PAGE_SIZE = 10 # Non-api page size (regular views) -API_PAGE_SIZE = 100 # Django REST framework +API_PAGE_SIZE = 1000 # Django REST framework REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ diff --git a/assets/css/jquery.jgrowl.min.css b/assets/css/jquery.jgrowl.min.css new file mode 100644 index 0000000..ea39484 --- /dev/null +++ b/assets/css/jquery.jgrowl.min.css @@ -0,0 +1 @@ +.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}} \ No newline at end of file diff --git a/assets/css/main.css b/assets/css/main.css index 20dc86f..1302101 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -5983,12 +5983,15 @@ a.linkS2[data-target="#withdraw-money"]{ border-radius: 50px; border: 1px solid #42B476; } + input[type="radio"]{ - opacity: 0; + opacity: 1; } + .wr-inset-pluss{ margin-top: 24px; } + .inset-gp .upload2{ float: left; margin: -15px 15px 0 12px; diff --git a/assets/js/chat.js b/assets/js/chat.js index 2f360c7..5ee15de 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -39,8 +39,7 @@ var SocketHandler = function () { }); setTimeout(function () { $("#orderBlock" + resOrderId).trigger('click'); - }, 2000); - console.log('approve stages'); + }, 200); } if (inbox) { @@ -77,8 +76,9 @@ function csrfSafeMethod(method) { var socket = new SocketHandler(); var csrftoken = getCookie('csrftoken'); -$(function () { + +$(function () { function dialog(message, yesCallback, notCallback) { $("#dialog_delete .modal-title").html(message); var dialog = $("#dialog_delete").modal('show'); @@ -93,48 +93,67 @@ $(function () { } var currentHash = URI(location.href).hash(); + + $('a[data-toggle="tab"]').on('show.bs.tab', function (e) { + var activeTab = $(this).attr('href').substring(1); + var liveHash = URI(location.href).hash(); + + switch(activeTab){ + case 'tab1': + setTimeout(function () { + if(liveHash.indexOf("#user") == 0) { + var userHashId = liveHash.replace("#user", ""); + $("#userBlock" + userHashId).trigger('click'); + } else { + $(".user-block").first().trigger('click'); + } + }, 100); + break; + + case 'tab2': + setTimeout(function () { + if(liveHash.indexOf("#order") == 0) { + var ordHashId = liveHash.replace("#order", ""); + $("#orderBlock" + ordHashId).trigger('click'); + } else { + $(".order-block").first().trigger('click'); + } + }, 100); + break; + + case 'tab3': + setTimeout(function () { + if(liveHash.indexOf("#teamorder") == 0) { + var teamHashId = liveHash.replace("#teamorder", ""); + $("#teamOrderBlock" + teamHashId).trigger('click'); + } else if(liveHash.indexOf("#myteam") == 0){ + var teamHashId = liveHash.replace("#myteam", ""); + $("#teamMyBlock" + teamHashId).trigger('click'); + } else { + var firstTeamBlock = $(".team-block").first(); + var firstTeamOrder = $(".team-order-block").first(); + if (firstTeamOrder.length == 1){ + firstTeamOrder.trigger('click'); + } else if(firstTeamBlock.length == 1){ + firstTeamBlock.trigger('click'); + } + } + }, 100); + + } + + }); + if (currentHash.indexOf("#order") == 0) { - var ordHashId = currentHash.replace("#order", ""); - setTimeout(function () { - $("#orderBlock" + ordHashId).trigger('click'); - $("a[href='#tab2']").trigger('click'); - }, 100); + $("a[href='#tab2']").trigger('click'); } else if(currentHash.indexOf("#user") == 0){ - var userHashId = currentHash.replace("#user", ""); - setTimeout(function () { - $("#userBlock" + userHashId).trigger('click'); - //$("a[href='#tab1']").trigger('click'); - }, 100); - } else if (currentHash.indexOf("#teamorder") == 0) { - var teamHashId = currentHash.replace("#teamorder", ""); - $("#teamOrderBlock" + teamHashId).trigger('click'); - $("a[href='#tab3']").trigger('click'); - } else if (currentHash.indexOf("#myteam") == 0){ - var teamHashId = currentHash.replace("#myteam", ""); - $("#teamMyBlock" + teamHashId).trigger('click'); - $("a[href='#tab3']").trigger('click'); - + $("a[href='#tab1']").trigger('click'); + } else if (currentHash.indexOf("#teamorder") == 0 || currentHash.indexOf("#myteam") == 0) { + $("a[href='#tab3']").trigger('click'); } else { - setTimeout(function () { - $(".user-block").first().trigger('click'); - location.hash = ''; - }, 10); - setTimeout(function () { - $(".order-block").first().trigger('click'); - location.hash = ''; - }, 500); - - setTimeout(function () { - var firstTeamBlock = $(".team-block").first(); - var firstTeamOrder = $(".team-order-block").first(); - if (firstTeamOrder.length == 1){ - firstTeamOrder.trigger('click'); - } else if(firstTeamBlock.length == 1){ - firstTeamBlock.trigger('click'); - } - location.hash = ''; - }, 1000); + $("a[href='#tab1']").trigger('click'); } + // Информация о заказе $(".full-order-info").click('on', function (e) { e.preventDefault(); @@ -220,12 +239,26 @@ $(function () { dataType: 'json', success: function (json) { if (json.status == 'ok') { + socket.send_stages_approve({ + "format_type": "approve_stages", + "data": { + "sender_id": json.sender, + "recipent_id": json.recipent, + "order_id": json.order, + "msg": "Заказчик зарезервировал сумму для этапов " + json.stages, + } + }); $("#reserve-stage-modal").modal('hide'); $("#orderBlock" + orderId).trigger('click'); + + }else if(json.status == 'error'){ + alert(json.message_error); } }, error: function (e, jqxhr) { + console.log(e); + console.log(jqxhr); } }) }); @@ -275,54 +308,54 @@ $(function () { docList.innerHTML = ''; $.ajax({ - url: '/api/documents', + url: '/api/message', type: 'GET', data: { csrfmiddlewaretoken: csrftoken, 'operand': 'in', 'sender_id': userId, - 'recipent_id': contactId, - 'is_delete': false, - 'is_send': true, + 'recipent_id': contactId }, dataType: 'json', - success: function (json) { - console.log(json); - $.each(json.results, function (i, v) { - docList.innerHTML += '
  • ' + v.file + '
  • '; + var senderName = 'Вы'; + var className = 'youChat'; + if (v.sender.id == contactId) { + senderName = v.sender.username; + className = ''; + } + inbox.innerHTML += '
    ' + + '

    ' + senderName + '

    ' + v.created + '
    ' + + '

    ' + v.text + '

    '; }); - }, - error: function (e) { - console.log(e); + var height = inbox.scrollHeight; + inbox.scrollTop = height; } }); $.ajax({ - url: '/api/message', + url: '/api/documents', type: 'GET', data: { csrfmiddlewaretoken: csrftoken, 'operand': 'in', 'sender_id': userId, - 'recipent_id': contactId + 'recipent_id': contactId, + 'is_delete': false, + 'is_send': true, }, dataType: 'json', + success: function (json) { + console.log(json); + $.each(json.results, function (i, v) { - var senderName = 'Вы'; - var className = 'youChat'; - if (v.sender.id == contactId) { - senderName = v.sender.username; - className = ''; - } - inbox.innerHTML += '
    ' + - '

    ' + senderName + '

    ' + v.created + '
    ' + - '

    ' + v.text + '

    '; + docList.innerHTML += '
  • ' + v.file + '
  • '; }); - var height = inbox.scrollHeight; - inbox.scrollTop = height; + }, + error: function (e) { + console.log(e); } }); @@ -346,7 +379,6 @@ $(function () { } }); - }); $('.deleteMess').on('click', function (e) { @@ -634,7 +666,6 @@ $(function () { }).prop('disabled', !$.support.fileInput) .parent().addClass($.support.fileInput ? undefined : 'disabled'); //Загрузка документов - $("#upload-document-contact").bind('fileuploadsubmit', function (e, data) { data.formData = { sender: $("#contact-chat-form #senderContactId").val(), @@ -643,7 +674,6 @@ $(function () { }); - $('#upload-document-contact').fileupload({ url: '/chat/create/', crossDomain: false, @@ -677,6 +707,11 @@ $(function () { }).prop('disabled', !$.support.fileInput) .parent().addClass($.support.fileInput ? undefined : 'disabled'); + $('#review-add').on('show.bs.modal', function (e) { + var related = $(e.relatedTarget); + var relatedType = related.attr('data-review-type'); + $('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true); + }); }); diff --git a/assets/js/chat_contractor.js b/assets/js/chat_contractor.js index 207f06f..00fb855 100644 --- a/assets/js/chat_contractor.js +++ b/assets/js/chat_contractor.js @@ -64,7 +64,6 @@ $(function () { dataType: 'json', success: function (json) { console.log(json); - }, error: function (e) { console.log('error'); @@ -74,7 +73,6 @@ $(function () { }); var orderId = $(this).attr('data-order-id'); - socket.send_stages_approve({ "format_type": "approve_stages", "data": { @@ -132,15 +130,23 @@ $(function () { }); $(".team-order-block").on('click', function () { + $('.team-order-block, .team-block').each(function () { $(this).removeClass('orAct'); }); $(this).addClass('orAct'); + var teamIds = ''; + $.each($(this).find('.team-chat-user'), function(i,v){ + teamIds += $(this).attr('data-id') + ";"; + }); + $("#team-chat-form #teamIds").val(teamIds); + var teamId = $(this).attr('data-team-id'); - location.hash = '#teamorder' + teamId; var orderId = $(this).attr('data-order-id'); + location.hash = '#teamorder' + orderId; $("#team-chat-form #teamId").val(teamId); + $("#team-chat-form #recipentTeamId").val(""); $("#team-chat-form #orderTeamId").val(orderId); $("#add-form-team-note #teamNote").val(teamId); @@ -219,6 +225,12 @@ $(function () { }); $(this).addClass('orAct'); + var teamIds = ''; + $.each($(this).find('.team-chat-user'), function(i,v){ + teamIds += $(this).attr('data-id') + ";"; + }); + $("#team-chat-form #teamIds").val(teamIds); + var inbox = document.getElementById('message-chat-team-space'); inbox.innerHTML = ''; @@ -229,6 +241,7 @@ $(function () { location.hash = '#myteam' + teamId; $("#team-chat-form #teamId").val(teamId); $("#add-form-team-note #teamNote").val(teamId); + $("#team-chat-form #recipentTeamId").val(""); $("#team-chat-form #orderTeamId").val(""); $("#add-form-team-note #orderNote").val(""); @@ -445,11 +458,12 @@ $(function () { '

    ' + statusName + '

    '; }); + if (statusNotAgreed) { htmlInbox += '
    ' + - 'согласовать' + - 'отказаться' + '
    '; } @@ -473,7 +487,7 @@ $(function () { '

    Срок сдачи ' + stage.term + ' ' + stage.cost + '

    '; if (!stage.close_contractor) { - stageWork += '
    Завершить этап
    '; } else { stageWork += '

    Этап ожидает завершения статуса от заказчика

    '; @@ -540,6 +554,7 @@ $(function () { var teamId = $("#team-chat-form #teamId").val(); var orderId = $("#team-chat-form #orderTeamId").val(); var documentSendIds = $("#documentSendIds").val(); + var teamIds = $("#team-chat-form #teamIds").val(); if (chatMessage) { var sendLinks = $("#document-send a"); @@ -561,6 +576,7 @@ $(function () { "recipent_id": recipentId, "chat_message": chatMessage, "team_id": teamId, + "team_ids": teamIds, "order_id": orderId, "document_send_links": sendLinkIds, "document_data": { diff --git a/chat/admin.py b/chat/admin.py index 0c43269..969a2d8 100644 --- a/chat/admin.py +++ b/chat/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Message, Notes, Documents +from .models import Message, Notes, Documents, NewMessage class MessageAdmin(admin.ModelAdmin): @@ -17,3 +17,4 @@ class DocumentsAdmin(admin.ModelAdmin): admin.site.register(Message, MessageAdmin) admin.site.register(Notes, NotesAdmin) admin.site.register(Documents, DocumentsAdmin) +admin.site.register(NewMessage) diff --git a/chat/chat.py b/chat/chat.py index 8eb0ca9..5d1eda1 100644 --- a/chat/chat.py +++ b/chat/chat.py @@ -43,6 +43,9 @@ class ChatHandler(websocket.WebSocketHandler): @gen.coroutine def approve_stages(self, data): + print(data) + data['data']['chat_message'] = data['data']['msg'] + self.add_message(data) sender_id = data['data']['sender_id'] recipent_id = data['data']['recipent_id'] order_id = data['data'].get('order_id') @@ -54,11 +57,11 @@ class ChatHandler(websocket.WebSocketHandler): @gen.coroutine def add_message(self, message_data): - sender_id = message_data['data']['sender_id'] recipent_id = message_data['data'].get('recipent_id', None) order_id = message_data['data'].get('order_id', None) team_id = message_data['data'].get('team_id', None) + team_ids_raw = message_data['data'].get('team_ids', None) message = message_data['data'].get('chat_message', None) docs_send_links = message_data['data'].get('document_send_links', None) if 'document_data' in message_data['data']: @@ -78,7 +81,7 @@ class ChatHandler(websocket.WebSocketHandler): team_value = "NULL" if team_id is None or not team_id else team_id - if not team_id and not recipent_id: + if not recipent_id: recipent_id = sender_id order_value = "NULL" if order_id is None or not order_id else order_id @@ -92,6 +95,19 @@ class ChatHandler(websocket.WebSocketHandler): cursor = cursor_list.get('cursor') result = cursor.fetchone() message_id = result[0] + if team_ids_raw: + team_ids = [t for t in team_ids_raw.rstrip(';').split(';')] + values_str = ''; + for t in team_ids: + values_str +='(DEFAULT,{0},{1}),'.format(message_id,t) + values_str = values_str.rstrip(',') + insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES{0}".\ + format(values_str) + else: + insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES(DEFAULT,{0},{1})".\ + format(message_id, recipent_id) + yield self.db.execute(insert_new_messages) + if docs_send_links: is_send = 'true' docs_send_ids = docs_send_links.rstrip(';').replace(';', ',') diff --git a/chat/filters.py b/chat/filters.py index 5caab95..6e878f7 100644 --- a/chat/filters.py +++ b/chat/filters.py @@ -27,6 +27,8 @@ class MessageFilterSet(FilterSet): model = Message + + class DocumentFilterSet(FilterSet): id = AllLookupsFilter() # order = RelatedFilter('projects.filters.OrderFilterSet') diff --git a/chat/migrations/0014_newmessage.py b/chat/migrations/0014_newmessage.py new file mode 100644 index 0000000..d0adea0 --- /dev/null +++ b/chat/migrations/0014_newmessage.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-12 16:12 +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): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('chat', '0013_auto_20160907_1556'), + ] + + operations = [ + migrations.CreateModel( + name='NewMessage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_messages', to='chat.Message')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_messages', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'Новые сообщения', + 'verbose_name': 'Новые сообщения', + }, + ), + ] diff --git a/chat/models.py b/chat/models.py index 88b30a0..06c445d 100644 --- a/chat/models.py +++ b/chat/models.py @@ -24,6 +24,18 @@ class Message(models.Model): verbose_name_plural = 'Сообщения' +class NewMessage(models.Model): + user = models.ForeignKey(User, related_name='new_messages') + message = models.ForeignKey(Message, related_name='new_messages') + + def __str__(self): + return str(self.id) + + class Meta: + verbose_name = 'Новые сообщения' + verbose_name_plural = 'Новые сообщения' + + class Notes(models.Model): text = models.TextField() created = models.DateTimeField(default=timezone.now) diff --git a/chat/templates/chat_contractor.html b/chat/templates/chat_contractor.html index 9d052e2..83b7bc1 100644 --- a/chat/templates/chat_contractor.html +++ b/chat/templates/chat_contractor.html @@ -15,7 +15,7 @@