From dfb0ba2db3ceb65c93d17adfecf7bbecf4523c9c Mon Sep 17 00:00:00 2001 From: ArturBaybulatov Date: Tue, 13 Sep 2016 03:02:37 +0300 Subject: [PATCH] Team invitations --- archilance/views.py | 16 +---- assets/index.js | 77 ++++++++++++++++----- assets/js/chat.js | 2 +- common/templatetags/common_tags.py | 7 +- projects/admin.py | 2 +- projects/urls.py | 8 +-- projects/views.py | 34 ++++----- templates/partials/base.html | 4 +- templates/test.html | 10 +-- users/admin.py | 16 +++-- users/filters.py | 7 +- users/migrations/0017_teaminvitation.py | 29 ++++++++ users/migrations/0018_auto_20160913_0215.py | 19 +++++ users/models.py | 13 ++++ users/serializers.py | 5 +- users/templates/contractor_office.html | 23 +++--- users/templates/contractor_profile.html | 35 ++++++---- users/templates/team_profile.html | 15 ++-- users/urls.py | 6 +- users/views.py | 58 ++++++++++++---- 20 files changed, 277 insertions(+), 109 deletions(-) create mode 100644 users/migrations/0017_teaminvitation.py create mode 100644 users/migrations/0018_auto_20160913_0215.py diff --git a/archilance/views.py b/archilance/views.py index a51e9aa..334f185 100644 --- a/archilance/views.py +++ b/archilance/views.py @@ -9,13 +9,14 @@ from django.views.generic import TemplateView, View from pprint import pprint, pformat import json import logging +import pydash as _; _.map = _.map_; _.filter = _.filter_ from .mixins import BaseMixin from archilance import util from chat.models import Documents from common.models import MainPage, PrintDocuments from projects.models import Order -from users.models import ContractorResumeFiles +from users.models import User, ContractorResumeFiles from work_sell.models import Picture @@ -54,20 +55,9 @@ class TestView(BaseMixin, View): def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) - data = {} - from projects.models import Project - from projects.serializers import ProjectSerializer - from users.models import User - from users.serializers import UserSerializer + context['foo'] = _.flatten_deep(User.contractor_objects.order_by('?')[:10].values_list('pk')) - contractor = User.contractor_objects.get(pk=11) - project = Project.objects.get(pk=153) - - data['contractor'] = UserSerializer(contractor).data - data['project'] = ProjectSerializer(project).data - - context['data_json'] = json.dumps(data) return render(request, self.template_name, context) # return redirect('projects:detail', pk=153) diff --git a/assets/index.js b/assets/index.js index 4a2cdc1..ead0214 100644 --- a/assets/index.js +++ b/assets/index.js @@ -1,3 +1,10 @@ +// Constants ----------------------------------------------- + + +var API_PAGE_SIZE = 100 + + + // Plugins init -------------------------------------------- $('.datepicker').datepicker() @@ -30,7 +37,7 @@ var specSelectOptions = { data: function(term, page) { return { - 'name__icontains': term, + name__icontains: term, page: page, } }, @@ -45,7 +52,7 @@ var specSelectOptions = { } }, data.results), - more: (page * 10) < data.count, + more: (page * API_PAGE_SIZE) < data.count, } }, }, @@ -203,23 +210,40 @@ var contractorSelectOptions = { language: 'ru', placeholder: 'Выберите пользователя', // Required by `allowClear` allowClear: true, -} - -function initContractorSelect($select) { - return $.ajax({url: '/api/users/?is_contractor=true', method: 'GET', dataType: 'json'}) - .then(function(res) { - var contractors = res.results - - $select.select2(_.merge(contractorSelectOptions, { - data: _.map(function(contractor) { + + ajax: { + url: '/api/users/', + dataType: 'json', + quietMillis: 250, + cache: true, + + data: function(term, page) { + return { + username__icontains: term, + page: page, + is_contractor: 'true', + } + }, + + results: function(data, page) { + return { + results: _.map(function(item) { return { - id: contractor.id, - text: contractor.username, - origItem: contractor, + id: item.id, + text: format('%s (%s)', item.username, item.get_full_name), + origItem: item, } - }, contractors), - })) - }) + }, data.results), + + more: (page * API_PAGE_SIZE) < data.count, + } + }, + }, +} + +function initContractorSelect($select, excludeIds) { + contractorSelectOptions.ajax.url = format('%s?id__in!=%s', contractorSelectOptions.ajax.url, excludeIds.join(',')) + return $select.select2(contractorSelectOptions) } @@ -751,6 +775,25 @@ function humanFileSize(bytes, si) { } +function format() { + var str = _.head(arguments) + var args = _.tail(arguments) + + var arg + + while (true) { + arg = args.shift() + + if (arg != null && str.search('%s') !== -1) + str = str.replace('%s', arg) + else + break + } + + return str +} + + function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie != '') { diff --git a/assets/js/chat.js b/assets/js/chat.js index 2f360c7..63f1a30 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 () { diff --git a/common/templatetags/common_tags.py b/common/templatetags/common_tags.py index cb22262..5347785 100644 --- a/common/templatetags/common_tags.py +++ b/common/templatetags/common_tags.py @@ -1,6 +1,7 @@ from django import template from django.utils.safestring import mark_safe from pprint import pprint, pformat +import json import os import pydash as _; _.map = _.map_; _.filter = _.filter_ @@ -66,6 +67,11 @@ def to_repr(val): return repr(val) +@register.filter('json') +def to_json(val): + return mark_safe(repr(json.dumps(val))) + + @register.filter def class_name(val): return type(val).__name__ @@ -99,7 +105,6 @@ def decap(val): return val - @register.simple_tag def tooltip(**kwargs): tooltip = util.get_or_none(Tooltip, **kwargs) diff --git a/projects/admin.py b/projects/admin.py index c7e3dd0..d45017a 100644 --- a/projects/admin.py +++ b/projects/admin.py @@ -3,6 +3,7 @@ from django.contrib import admin from .models import ( Answer, + Arbitration, BuildingClassfication, Candidate, ConstructionType, @@ -13,7 +14,6 @@ from .models import ( ProjectFile, Realty, Stage, - Arbitration, ) diff --git a/projects/urls.py b/projects/urls.py index a4813c8..bb4bef6 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -2,12 +2,13 @@ from django.conf import urls from django.views.generic import TemplateView from .views import ( + # ContractorOfferOrder, + # TeamOfferOrder, add_candidate, ArbitrationCreateView, CandidateDeleteView, contractor_portfolio_create, ContractorAnswerArchiveView, - ContractorOfferOrder, ContractorPortfolioTrashView, ContractorPortfolioUpdateView, CustomerOfferOrderView, @@ -24,7 +25,6 @@ from .views import ( RejectProjectAnswerView, RestoreProjectAnswerView, sort_candidates, - TeamOfferOrder, ) app_name = 'projects' @@ -58,6 +58,6 @@ urlpatterns = [ urls.url(r'^customer-offer-order/(?P(\d+))/(?P(\d+))/$', CustomerOfferOrderView.as_view(), name='customer-offer-order'), - urls.url(r'^(?P\d+)/contractor-offer-order/(?P\d+)/$', ContractorOfferOrder.as_view(), name='contractor-offer-order'), - urls.url(r'^(?P\d+)/team-offer-order/(?P\d+)/$', TeamOfferOrder.as_view(), name='team-offer-order'), + # urls.url(r'^(?P\d+)/contractor-offer-order/(?P\d+)/$', ContractorOfferOrder.as_view(), name='contractor-offer-order'), + # urls.url(r'^(?P\d+)/team-offer-order/(?P\d+)/$', TeamOfferOrder.as_view(), name='team-offer-order'), ] diff --git a/projects/views.py b/projects/views.py index 9fd0989..2fcae3c 100644 --- a/projects/views.py +++ b/projects/views.py @@ -263,7 +263,7 @@ class ProjectFilterView(BaseMixin, View): realty_form = self.realty_form(request.GET, request=request, prefix='realty_form') context = self.get_context_data(**_.merge({}, request.GET, kwargs)) - projects = Project.objects.all() + projects = Project.objects.filter(state='active') if form.is_valid() and realty_form.is_valid(): ord = None @@ -802,22 +802,22 @@ class ArbitrationCreateView(CreateView): return super().form_invalid(form) -class ContractorOfferOrder(NoCsrfMixin, CustomerRequiredMixin, View): - def post(self, request, *args, project_id, contractor_id, **kwargs): - project = get_object_or_404(Project, pk=project_id) - contractor = get_object_or_404(User.contractor_objects, pk=contractor_id) - - # project.order.contractor - - return JsonResponse({'status': 'success'}) - - -class TeamOfferOrder(NoCsrfMixin, CustomerRequiredMixin, View): - def post(self, request, *args, project_id, team_id, **kwargs): - project = get_object_or_404(Project, pk=project_id) - team = get_object_or_404(Team, pk=team_id) - - return JsonResponse({'status': 'success'}) +# class ContractorOfferOrder(NoCsrfMixin, CustomerRequiredMixin, View): +# def post(self, request, *args, project_id, contractor_id, **kwargs): +# project = get_object_or_404(Project, pk=project_id) +# contractor = get_object_or_404(User.contractor_objects, pk=contractor_id) +# +# # project.order.contractor +# +# return JsonResponse({'status': 'success'}) +# +# +# class TeamOfferOrder(NoCsrfMixin, CustomerRequiredMixin, View): +# def post(self, request, *args, project_id, team_id, **kwargs): +# project = get_object_or_404(Project, pk=project_id) +# team = get_object_or_404(Team, pk=team_id) +# +# return JsonResponse({'status': 'success'}) # import code; code.interact(local=dict(globals(), **locals())) diff --git a/templates/partials/base.html b/templates/partials/base.html index 8ed6bd6..b270854 100644 --- a/templates/partials/base.html +++ b/templates/partials/base.html @@ -24,7 +24,7 @@ - +{# #} {% block head_css %}{% endblock %} @@ -87,7 +87,7 @@ if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) { 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 () { diff --git a/templates/test.html b/templates/test.html index 6859022..60431ea 100644 --- a/templates/test.html +++ b/templates/test.html @@ -9,17 +9,13 @@ - -
-
{{ data_json }}
+
Blah
@@ -30,5 +26,9 @@ + + diff --git a/users/admin.py b/users/admin.py index 375ab81..f74d89f 100644 --- a/users/admin.py +++ b/users/admin.py @@ -1,6 +1,13 @@ from django.contrib import admin -from .models import User, Team, UserFinancialInfo, ContractorResume, ContractorResumeFiles +from .models import ( + ContractorResume, + ContractorResumeFiles, + Team, + TeamInvitation, + User, + UserFinancialInfo, +) class UserAdmin(admin.ModelAdmin): @@ -17,8 +24,9 @@ class TeamAdmin(admin.ModelAdmin): ordering = ('-rating',) -admin.site.register(User, UserAdmin) -admin.site.register(UserFinancialInfo) -admin.site.register(Team, TeamAdmin) admin.site.register(ContractorResume) admin.site.register(ContractorResumeFiles) +admin.site.register(Team, TeamAdmin) +admin.site.register(TeamInvitation) +admin.site.register(User, UserAdmin) +admin.site.register(UserFinancialInfo) diff --git a/users/filters.py b/users/filters.py index 7969b44..bc29452 100755 --- a/users/filters.py +++ b/users/filters.py @@ -13,6 +13,7 @@ class UserFilterSet(FilterSet): is_contractor = MethodFilter() is_customer = MethodFilter() last_name = AllLookupsFilter() + username = AllLookupsFilter() projects = RelatedFilter('projects.filters.ProjectFilterSet') @@ -21,13 +22,15 @@ class UserFilterSet(FilterSet): def filter_is_contractor(self, name, qs, value): if value == 'true': - return _.filter(qs, lambda user: user.is_contractor()) + # return _.filter(qs, lambda user: user.is_contractor()) + return qs.filter(groups__name='Исполнители') return qs def filter_is_customer(self, name, qs, value): if value == 'true': - return _.filter(qs, lambda user: user.is_customer()) + # return _.filter(qs, lambda user: user.is_customer()) + return qs.filter(groups__name='Заказчики') return qs diff --git a/users/migrations/0017_teaminvitation.py b/users/migrations/0017_teaminvitation.py new file mode 100644 index 0000000..69d1ddb --- /dev/null +++ b/users/migrations/0017_teaminvitation.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-12 23:13 +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 = [ + ('users', '0016_remove_team_avatar'), + ] + + operations = [ + migrations.CreateModel( + name='TeamInvitation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('contractor1', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites', to=settings.AUTH_USER_MODEL)), + ('contractor2', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invited_by', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Приглашение в команду', + 'verbose_name_plural': 'Приглашения в команды', + }, + ), + ] diff --git a/users/migrations/0018_auto_20160913_0215.py b/users/migrations/0018_auto_20160913_0215.py new file mode 100644 index 0000000..16d4881 --- /dev/null +++ b/users/migrations/0018_auto_20160913_0215.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-12 23:15 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0017_teaminvitation'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='teaminvitation', + unique_together=set([('contractor1', 'contractor2')]), + ), + ] diff --git a/users/models.py b/users/models.py index 2892ae1..3e95239 100644 --- a/users/models.py +++ b/users/models.py @@ -245,3 +245,16 @@ class Team(models.Model): class Meta: verbose_name = 'Команда' verbose_name_plural = 'Команды' + + +class TeamInvitation(models.Model): + contractor1 = models.ForeignKey(User, related_name='invites') + contractor2 = models.ForeignKey(User, related_name='invited_by') + + def __str__(self): + return '%s-%s' % (self.contractor1.username, self.contractor2.username) + + class Meta: + verbose_name = 'Приглашение в команду' + verbose_name_plural = 'Приглашения в команды' + unique_together = ('contractor1', 'contractor2') diff --git a/users/serializers.py b/users/serializers.py index 513b496..6d78c1a 100755 --- a/users/serializers.py +++ b/users/serializers.py @@ -50,8 +50,10 @@ class UserSerializer(ModelSerializer): 'date_of_birth', 'email', 'financial_info', + 'fio', 'first_name', 'gender', + 'get_full_name', 'has_team', 'id', 'is_active', @@ -61,11 +63,10 @@ class UserSerializer(ModelSerializer): 'last_time_visit', 'location', 'patronym', + 'phone', 'skype', 'username', 'website', - 'phone', - 'fio', ) def get__type(self, obj): diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html index d9b3c65..8ccc6c4 100644 --- a/users/templates/contractor_office.html +++ b/users/templates/contractor_office.html @@ -19,10 +19,10 @@
{% if contractor.team %} - + {% else %}
@@ -118,7 +118,7 @@
-

{{ contractor.team.name }}

+

{{ contractor.team.name }}

{{ contractor.get_location }}

@@ -131,7 +131,6 @@

Свободен
- написать сообщение
@@ -260,6 +259,9 @@ {% block js_block %}