diff --git a/assets/js/chat.js b/assets/js/chat.js index 93c8c3b..1375c1f 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -1,6 +1,6 @@ var SocketHandler = function () { domain = domain.replace(':' + port, ''); - var url = 'ws://' + domain + '/chat/' + userId + '/'; + var url = 'ws://' + domain + ':8888/chat/' + userId + '/'; var sock = new WebSocket(url); var intervalId; sock.onopen = function () { @@ -23,9 +23,17 @@ var SocketHandler = function () { alert('approve stages'); } if (inbox) { - inbox.innerHTML += '
' + - '

ВЫ

Сейчас
' + - '

' + message.msg + '

'; + var textMessage = message.msg; + var classMessage = 'youChat'; + var senderName = 'Вы'; + var timeMessage = message.msg_time; + if (message.sender_id != userId){ + senderName = message.sender_name; + classMessage = ''; + } + inbox.innerHTML += '
' + + '

'+ senderName +'

' + timeMessage + '
' + + '

' + textMessage + '

'; } }; diff --git a/chat/chat.py b/chat/chat.py index c4143ab..84897e0 100644 --- a/chat/chat.py +++ b/chat/chat.py @@ -1,4 +1,4 @@ -import os.path +import html from tornado import gen, web, websocket, escape, options from tornado.ioloop import IOLoop from tornado.httpserver import HTTPServer @@ -33,7 +33,6 @@ class ChatHandler(websocket.WebSocketHandler): if 'dummy' in parsed: return - print(parsed['format_type']) if 'approve_stages' in parsed['format_type']: self.approve_stages(parsed) else: @@ -60,6 +59,8 @@ class ChatHandler(websocket.WebSocketHandler): order_id = message_data['data'].get('order_id', None) team_id = message_data['data'].get('team_id', None) message = message_data['data'].get('chat_message', None) + message = html.escape(message) + message = message.replace('\n', '
') answer_type = message_data['format_type'] private_type = 'true' if not order_id and not team_id else 'false' @@ -74,14 +75,26 @@ class ChatHandler(websocket.WebSocketHandler): insert_sql = "INSERT INTO chat_message (id,text,created, sender_id,recipent_id," \ " private_type,team_id, order_id,is_delete,is_new) " \ - "VALUES (DEFAULT,'{0}',NOW(),{1},{2},{3},{4},{5},{6},{7})".\ + "VALUES (DEFAULT,'{0}',NOW(),{1},{2},{3},{4},{5},{6},{7}) RETURNING id".\ format(message, sender_id, recipent_id, private_type, team_value,order_value, is_delete, is_new) - yield self.db.execute(insert_sql) - waiters = tuple(w for c, w in self.waiters if c == recipent_id or c == sender_id) + cursor_list = yield dict(cursor=self.db.execute(insert_sql)) + cursor = cursor_list.get('cursor') + result = cursor.fetchone() + message_id = result[0] + select_last_sql = "SELECT chat_message.id, chat_message.text, chat_message.created, chat_message.sender_id," \ + "users_user.id, users_user.username FROM chat_message" \ + " INNER JOIN users_user ON (chat_message.sender_id = users_user.id)" \ + " WHERE chat_message.id = {0}".format(message_id) + + cursor_msg = yield self.db.execute(select_last_sql) + msg_data = cursor_msg.fetchone() + sender_name = msg_data[5] + msg_time = msg_data[2].strftime("%Y-%m-%d %H:%M:%S") + waiters = tuple(w for c, w in self.waiters if c == recipent_id or c == sender_id) for waiter in waiters: - waiter.write_message({'msg': message, 'answer_type': answer_type}) + waiter.write_message({'msg': message, 'msg_time': msg_time, 'sender_id': sender_id, 'sender_name': sender_name, 'answer_type': answer_type}) def check_origin(self, origin): return True diff --git a/chat/serializers.py b/chat/serializers.py index cd43c8d..e4d8467 100644 --- a/chat/serializers.py +++ b/chat/serializers.py @@ -1,3 +1,4 @@ +from rest_framework import serializers from rest_framework.serializers import ModelSerializer from .models import Message, Notes, Documents @@ -20,6 +21,7 @@ class DocumentsSerializer(ModelSerializer): class MessageSerializer(ModelSerializer): sender = UserSerializer() recipent = UserSerializer() + created = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S") class Meta: model = Message diff --git a/chat/templates/chat_customer.html b/chat/templates/chat_customer.html index 12c4df7..a3db522 100644 --- a/chat/templates/chat_customer.html +++ b/chat/templates/chat_customer.html @@ -72,7 +72,7 @@ - отправить + отправить @@ -108,7 +108,7 @@

Испонитель: {{ order.order.contractor.get_full_name }}

- + Полное описание заказа @@ -134,7 +134,7 @@ Не более 10 файлов с общим объемом 500мб - отправить + отправить @@ -448,18 +448,7 @@ } ii--; }); - - } -{# #} -{# var updateFormStages = $(".update-stages-form");#} -{# var limitCount = countStage + 1;#} -{# for (var i = 2; i < limitCount; i++) {#} -{# var stageCopy = $("#stage1").clone().attr("id", "stage" + i).addClass("stages_form");#} -{# stageCopy.find('.stage-span-id').html(i);#} -{# $("#stage1").after(stageCopy);#} -{##} -{# }#} }); $('.order-block').on('click', function () { @@ -545,7 +534,8 @@ }); - $('#contact-chat-add-message').on('click', function () { + $('#contact-chat-add-message').on('click', function (e) { + e.preventDefault(); var chatMessage = $("#chat").val(); var recipentId = $("#recipentId").val(); var senderId = $("#senderId").val(); @@ -566,7 +556,8 @@ alert('add review'); }); - $('#order-chat-add-message').on('click', function () { + $('#order-chat-add-message').on('click', function (e) { + e.preventDefault(); var chatMessage = $("#chat-order-add #chat").val(); var recipentId = $("#chat-order-add #recipentId").val(); var senderId = $("#chat-order-add #senderId").val(); diff --git a/projects/templates/project_detail.html b/projects/templates/project_detail.html index aec5b6d..1752865 100644 --- a/projects/templates/project_detail.html +++ b/projects/templates/project_detail.html @@ -580,9 +580,15 @@ {% endif %} - - предложить проект - + {% if not project.order.contractor and not project.order.team %} +
+ {% csrf_token %} + + + предложить проект + +
+ {% endif %}
{% csrf_token %} @@ -728,9 +734,15 @@
- - предложить проект - + {% if not project.order.contractor and not project.order.team %} + + {% csrf_token %} + + + предложить проект + + + {% endif %}
{% csrf_token %} diff --git a/projects/urls.py b/projects/urls.py index ac25df9..e858b2a 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -47,7 +47,7 @@ urlpatterns = [ urls.url(r'^candidate/comparison/sort/$', sort_candidates, name='comparison-sort'), urls.url(r'^candidate/comparison/(?P\d+)/$', ProjectComparisonView.as_view(), name='comparison'), - urls.url(r'^offerorder/(?P(\d+))/(?P(\d+))/$', OfferOrderView.as_view(), name='offer-order'), + urls.url(r'^offerorder/(?P(\d+))/(?P(\d+))/$', OfferOrderView.as_view(), name='customer-offer-order'), # urls.url(r'^portfolio/create/$', PortfolioCreateView.as_view(), name='portfolio-create'), ] diff --git a/projects/views.py b/projects/views.py index 1753cd4..409e5e5 100644 --- a/projects/views.py +++ b/projects/views.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.core.exceptions import PermissionDenied from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin from django.core.exceptions import PermissionDenied @@ -9,7 +10,7 @@ from django.db.models import Q, F from django.http import HttpResponseForbidden, HttpResponseRedirect, HttpResponse, Http404 from django.shortcuts import render, get_object_or_404, redirect from django.views.generic import ListView, DetailView, CreateView, DeleteView, View, UpdateView, TemplateView, FormView -from django.views.generic.base import ContextMixin + from hitcount.models import HitCount from hitcount.views import HitCountMixin from pprint import pprint, pformat @@ -328,7 +329,7 @@ class CustomerProjectCreateView(BaseMixin, View): if request.user.is_authenticated() and request.user.is_customer(): return super().dispatch(request, *args, **kwargs) else: - return HttpResponseForbidden() + raise PermissionDenied def get(self, request, *args, **kwargs): form = self.form_class(request=request) @@ -604,6 +605,7 @@ class CandidateDeleteView(DeleteView): def get(self, *args, **kwargs): return self.post(*args, **kwargs) + def sort_candidates(request): if request.is_ajax(): items = request.POST.getlist('items[]') @@ -623,8 +625,29 @@ def sort_candidates(request): class OfferOrderView(View): template_name = 'chattest.html' - def get(self, request, *args, **kwargs): - return render(request, self.template_name) + def post(self, request, *args, **kwargs): + project_id = kwargs.get('project_id') + answer_id = kwargs.get('answer_id') + try: + project = Project.objects.get(pk=project_id) + except Project.DoesNotExist: + project = None + try: + answer = Answer.objects.get(pk=answer_id) + except Answer.DoesNotExist: + answer = None + + order = project.order + if not order.contractor and not order.team: + if isinstance(answer.author, User): + order.contractor = answer.author + order.save() + elif isinstance(answer.author, Team): + order.team = answer.author + order.save() + + redirect_url = reverse('chat:chat-user') + '?order_id=' + str(order.pk) + return HttpResponseRedirect(redirect_url) def contractor_portfolio_create(request): diff --git a/ratings/templates/templatetags/ratings_widget.html b/ratings/templates/templatetags/ratings_widget.html index 9a9305d..e756f47 100644 --- a/ratings/templates/templatetags/ratings_widget.html +++ b/ratings/templates/templatetags/ratings_widget.html @@ -1,12 +1,12 @@ diff --git a/ratings/templates/templatetags/specializations_widget.html b/ratings/templates/templatetags/specializations_widget.html index 65d20f0..1c0261a 100644 --- a/ratings/templates/templatetags/specializations_widget.html +++ b/ratings/templates/templatetags/specializations_widget.html @@ -1,38 +1,3 @@ -{##} -{##} -{#
#} -{#

#} -{# Специализации:#} -{#

#} -{#
#} -{# Интерьеры#} -{# 2-й#} -{#
#} -{#
#} -{# Визуализация/3D#} -{# 45-й#} -{#
#} -{#
#} -{# Экстерьеры#} -{# 10-й#} -{#
#} -{#
#} -{#
#} -{# Архитектура#} -{# 3-й#} -{#
#} -{#
#} -{# 3D Моделирование#} -{# 100-й#} -{#
#} -{#
#} -{# #} -{#
#} - - -

Специализации:

{% for spec in specializations %} @@ -40,5 +5,7 @@ {{ spec.specialization.name }} {{ spec.position }}-й
+ {% empty %} + Рейтинги скоро будут рассчитаны {% endfor %}
diff --git a/ratings/templatetags/specializtions_tags.py b/ratings/templatetags/specializtions_tags.py index 6ef72bb..2659523 100644 --- a/ratings/templatetags/specializtions_tags.py +++ b/ratings/templatetags/specializtions_tags.py @@ -1,11 +1,13 @@ from django import template -from archilance import util -from users.models import User, Team +from projects.models import Order from ratings.models import SpecializationRating +from reviews.models import Review +from users.models import User, Team register = template.Library() + @register.inclusion_tag('templatetags/specializations_widget.html', takes_context=True) def specialization_widget(context, user_id, class_name=None): user_id = int(user_id) @@ -25,18 +27,26 @@ def specialization_team_widget(context, team_id): } - @register.inclusion_tag("templatetags/ratings_widget.html", takes_context=True) def ratings_widget(context, user_id, class_name=None): ratings = User.objects.get(pk=user_id).rating return { 'ratings': ratings, - 'class_name': class_name + 'class_name': class_name, + 'deals': Order.objects.filter(secure=True, contractor_id=user_id, status=1).count(), + 'reviews_n': Review.objects.filter(target_contractor_id=user_id, type='neutral').count(), + 'reviews_m': Review.objects.filter(target_contractor_id=user_id, type='negative').count(), + 'reviews_p': Review.objects.filter(target_contractor_id=user_id, type='positive').count(), } + @register.inclusion_tag("templatetags/ratings_widget.html", takes_context=True) def ratings_team_widget(context, team_id): ratings = Team.objects.get(pk=team_id).rating return { 'ratings': ratings, + 'deals': Order.objects.filter(secure=True, team_id=team_id, status=1).count(), + 'reviews_n': Review.objects.filter(target_team_id=team_id, type='neutral').count(), + 'reviews_m': Review.objects.filter(target_team_id=team_id, type='negative').count(), + 'reviews_p': Review.objects.filter(target_team_id=team_id, type='positive').count(), } diff --git a/users/mixins.py b/users/mixins.py index a1bb811..b1e16bd 100644 --- a/users/mixins.py +++ b/users/mixins.py @@ -1,5 +1,4 @@ -from django.http import HttpResponseForbidden -from django.contrib import messages +from django.core.exceptions import PermissionDenied class CheckForUserMixin(object): @@ -8,9 +7,9 @@ class CheckForUserMixin(object): pk = kwargs.get('pk') if pk: if request.user.pk != int(pk): - return HttpResponseForbidden('403 Forbidden') + raise PermissionDenied else: - return HttpResponseForbidden('403 Forbidden') + raise PermissionDenied return super().dispatch(request, *args, **kwargs) diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html index de876f1..18e87b8 100644 --- a/users/templates/contractor_office.html +++ b/users/templates/contractor_office.html @@ -91,7 +91,8 @@ Программист - + {% empty %} + В группе пока нет участников {% endfor %} @@ -176,15 +177,19 @@ + {% empty %} +
+

Работ в портфолио пока нет

+
{% endfor %} - -
-
- показать еще -
-
+{# TODO:#} +{#
#} +{#
#} +{# показать еще#} +{#
#} +{#
#}
@@ -208,38 +213,45 @@
+ {% empty %} +
+

Готовых работ пока нет

+
{% endfor %} -
-
- показать еще -
-
+{# TODO#} +{#
#} +{#
#} +{# показать еще#} +{#
#} +{#
#}
+ {% for review in reviews %}

- Иванов Петр Иванович + {{ review.get_sender }}

- - Безопасная сделка - + {% if review.project.deal_type == 'secure_deal' %} + + Безопасная сделка + + {% endif %}

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum - + {{ review.text|safe }}

+ {% empty %} +
+

Отзывов пока нет

+
+ {% endfor %}
diff --git a/users/templates/contractor_profile.html b/users/templates/contractor_profile.html index 0d69861..55c0941 100644 --- a/users/templates/contractor_profile.html +++ b/users/templates/contractor_profile.html @@ -196,7 +196,12 @@ {% endif %} + {% empty %} +
+

Работ в портфолио пока нет

+
{% endfor %} +
@@ -455,6 +460,10 @@

+ {% empty %} +
+

Отзывов пока нет

+
{% endfor %} diff --git a/users/views.py b/users/views.py index d87e666..e51ba37 100644 --- a/users/views.py +++ b/users/views.py @@ -375,6 +375,7 @@ class ContractorOfficeDetailView(DetailView): work_sells = WorkSell.objects.filter(contractor__in=user_ids) context['portfolios'] = portfolios context['work_sells'] = work_sells + context['reviews'] = Review.objects.filter(target_contractor__in=user_ids) return context diff --git a/wallets/forms.py b/wallets/forms.py index bdfd997..d8afe1a 100644 --- a/wallets/forms.py +++ b/wallets/forms.py @@ -43,6 +43,7 @@ class TmpPaymentAvisoForm(forms.Form): md5 = forms.CharField() shopId = forms.IntegerField() invoiceId = forms.IntegerField() + orderSumAmount = forms.DecimalField() def clean_action(self): action = self.cleaned_data.get('action') diff --git a/wallets/signals.py b/wallets/signals.py index 50d2f08..785383c 100644 --- a/wallets/signals.py +++ b/wallets/signals.py @@ -22,7 +22,7 @@ def send_for_accountant(sender, instance, created, **kwargs): @receiver(post_save, sender=Transaction) def add_invoice_history(sender, instance, created, **kwargs): - if 'add' in instance.type: + if 'add' in instance.type and instance.complete: inv_history = InvoiceHistory() inv_history.comment = 'Пополнение счета' inv_history.sum = instance.sum diff --git a/wallets/templates/score-detail.html b/wallets/templates/score-detail.html index 9a9f2c9..275e727 100644 --- a/wallets/templates/score-detail.html +++ b/wallets/templates/score-detail.html @@ -23,7 +23,7 @@ {% endif %}
- вывести средства
@@ -75,15 +75,14 @@