Team invitations

remotes/origin/PR-39
ArturBaybulatov 9 years ago
parent 8673fb7aef
commit dfb0ba2db3
  1. 16
      archilance/views.py
  2. 77
      assets/index.js
  3. 2
      assets/js/chat.js
  4. 7
      common/templatetags/common_tags.py
  5. 2
      projects/admin.py
  6. 8
      projects/urls.py
  7. 34
      projects/views.py
  8. 4
      templates/partials/base.html
  9. 10
      templates/test.html
  10. 16
      users/admin.py
  11. 7
      users/filters.py
  12. 29
      users/migrations/0017_teaminvitation.py
  13. 19
      users/migrations/0018_auto_20160913_0215.py
  14. 13
      users/models.py
  15. 5
      users/serializers.py
  16. 23
      users/templates/contractor_office.html
  17. 35
      users/templates/contractor_profile.html
  18. 15
      users/templates/team_profile.html
  19. 6
      users/urls.py
  20. 58
      users/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)

@ -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 != '') {

@ -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 () {

@ -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)

@ -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,
)

@ -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<answer_id>(\d+))/(?P<project_id>(\d+))/$', CustomerOfferOrderView.as_view(), name='customer-offer-order'),
urls.url(r'^(?P<project_id>\d+)/contractor-offer-order/(?P<contractor_id>\d+)/$', ContractorOfferOrder.as_view(), name='contractor-offer-order'),
urls.url(r'^(?P<project_id>\d+)/team-offer-order/(?P<team_id>\d+)/$', TeamOfferOrder.as_view(), name='team-offer-order'),
# urls.url(r'^(?P<project_id>\d+)/contractor-offer-order/(?P<contractor_id>\d+)/$', ContractorOfferOrder.as_view(), name='contractor-offer-order'),
# urls.url(r'^(?P<project_id>\d+)/team-offer-order/(?P<team_id>\d+)/$', TeamOfferOrder.as_view(), name='team-offer-order'),
]

@ -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()))

@ -24,7 +24,7 @@
<link rel='stylesheet' href='{% static "css/swiper.min.css" %}'>
<link rel='stylesheet' href='{% static "lib/jquery.fileupload/css/jquery.fileupload.css" %}'>
<link rel='stylesheet' href='{% static "js/magnific-popup.css" %}'>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.css"> <!-- TODO: Download locally -->
{# <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.css"> <!-- TODO: Download locally -->#}
{% 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 () {

@ -9,17 +9,13 @@
<link rel='stylesheet' href='{% static "lib/jquery-ui/jquery-ui.css" %}'>
<link rel='stylesheet' href='{% static "css/bootstrap.css" %}'>
<script>
window.djangoData = JSON.parse({{ data_json|repr|safe }})
</script>
</head>
<body>
<div class='container-fluid'>
<div class='row'>
<div class='col-xs-12' style='margin-top: 15px'>
<pre>{{ data_json }}</pre>
<pre>Blah</pre>
</div>
</div>
</div>
@ -30,5 +26,9 @@
<script src='{% static "js/bootstrap.min.js" %}'></script>
<script src='{% static "my-libs.js" %}'></script>
<script>
console.log(JSON.parse({{ foo|json }}))
</script>
</body>
</html>

@ -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)

@ -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

@ -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': 'Приглашения в команды',
},
),
]

@ -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')]),
),
]

@ -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')

@ -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):

@ -19,10 +19,10 @@
<div class="buttonGP disTab">
<div class="btn-group valProject2 val-pro3" role="group">
{% if contractor.team %}
<button type="button" class="btn btn-default">
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}" class="btn btn-default">
{{ contractor.team.name }}
<span><mark>0</mark></span>
</button>
</a>
{% else %}
<button type="button" class="btn btn-default add-group" data-toggle="modal" data-target="#myModal">
+ Добавить группу
@ -75,10 +75,10 @@
<div class="avatarInset">
{% if contractor.avatar %}
{% thumbnail contractor.avatar "265x264" crop="center" as im %}
<img src="{{ im.url }}" alt="profile-image">
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{{ im.url }}" alt="profile-image"></a>
{% endthumbnail %}
{% else %}
<img src="{% static 'img/profile.jpg' %}" alt="profile-image">
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{% static 'img/profile.jpg' %}" alt="profile-image"></a>
{% endif %}
</div>
</div>
@ -118,7 +118,7 @@
<div class="col-lg-9 divCol9">
<div class="col-lg-4">
<p class="nameUser">{{ contractor.team.name }}</p>
<p class="nameUser"><a href="{% url 'users:team-profile' pk=contractor.team.pk %}">{{ contractor.team.name }}</a></p>
<p class="cityUser">{{ contractor.get_location }}</p>
<p class="navv">На сайте {{ contractor.team.created }}</p>
@ -131,7 +131,6 @@
</p>
<div class="statusUser st-new">Свободен</div>
<a href="javascript:void(0)" class="new-prop new-prop2 new-prop3">написать сообщение</a>
</div>
<div class="col-lg-4">
@ -260,6 +259,9 @@
{% block js_block %}
<script>
(function() {
// Pagination ---------------------------------------------------
var $portfoliosContainer = $('.-portfolios-container').first()
var portfolioItemTempl = _.template($portfoliosContainer.find('.-portfolio-item-templ').first().html())
var $morePortfBtn = $('.-more-portfolios-btn').first()
@ -341,12 +343,13 @@
// Add new team member --------------------------------------------
{% if request.user.has_team %}
{% if contractor.has_team %}
var $contractorSelect = $('.-contractor-select').first()
var $addTeamMemberModal = $('#addTeamMemberModal')
{# var addTeamMemberUrl = '/users/add-team-member/%id%/'#}
initContractorSelect($contractorSelect)
var contractorExcludeIds = JSON.parse({{ team_invitation_exclude_contractor_ids|json }})
initContractorSelect($contractorSelect, contractorExcludeIds)
$addTeamMemberModal.find('.-action-button').first().on('click', function($evt) {
$addTeamMemberModal.modal('hide')
@ -355,9 +358,9 @@
format_type: 'add_message_contact',
data: {
sender_id: '{{ request.user.pk }}',
sender_id: '{{ contractor.pk }}',
recipent_id: String($contractorSelect.select2('val')),
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:team-profile' pk=request.user.team.pk %}',
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:team-profile' pk=contractor.team.pk %}',
},
})

@ -33,7 +33,7 @@
</li>
{% endif %}
{% if contractor.pk != request.user.pk and request.user.is_contractor and request.user.has_team %}
{% if display_team_invitation_button %}
<li class="icon_um2">
<a href="#" onclick="return false" class='-team-invitation'>
пригласить в группу
@ -592,6 +592,7 @@
(function() {
// Pagination ---------------------------------------------------
var $portfoliosContainer = $('.-portfolios-container').first()
var portfolioItemTempl = _.template($portfoliosContainer.find('.-portfolio-item-templ').first().html())
var $morePortfBtn = $('.-more-portfolios-btn').first()
@ -703,19 +704,29 @@
{% if contractor.pk != request.user.pk and request.user.is_contractor and request.user.has_team %}
var createTeamInvitationUrl = format('/users/create-team-invitation/%s/', {{ contractor.pk }})
$('.-team-invitation').first().on('click', function($evt) {
socketMain.add_message({
format_type: 'add_message_contact',
data: {
sender_id: '{{ request.user.pk }}',
recipent_id: '{{ contractor.pk }}',
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:team-profile' pk=request.user.team.pk %}',
},
var $inviteBtn = $(this)
$.post(createTeamInvitationUrl).then(function(res) {
if (res.status === 'success') {
socketMain.add_message({
format_type: 'add_message_contact',
data: {
sender_id: '{{ request.user.pk }}',
recipent_id: '{{ contractor.pk }}',
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:accept-team-invitation' owner_id=request.user.pk %}',
},
})
$.jGrowl('Приглашение успешно отправлено')
$inviteBtn.fadeOut()
} else {
$.jGrowl('Произошла ошибка при отправке приглашения')
}
})
$.jGrowl('Приглашение успешно отправлено')
$(this).fadeOut()
})
{% endif %}

@ -23,9 +23,11 @@
</div>
<div class="menuUser disTab">
<a href="{% url 'users:contractor-filter' %}" class="add-man">
добавить участника
</a>
{% if request.user == team.owner %}
<a href="{% url 'users:contractor-filter' %}" class="add-man">
добавить участника
</a>
{% endif %}
<div class="block-users">
<p>Состав группы</p>
@ -69,7 +71,12 @@
Выполненных проектов: <span>{{ completed_project_count }}</span>
</p>
<div class="statusUser st-new">Свободен</div>
{% if team.owner.contractor_status == 'free' %}
<div class="statusUser">Свободен</div>
{% else %}
<div class="statusUser busy">Занят</div>
{% endif %}
<a href="{% url 'chat:chat-user' %}?user_id={{ team.owner.pk }}" class="new-prop new-prop2 new-prop3">написать сообщение</a>
</div>

@ -3,7 +3,7 @@ from django.conf.urls import include
from django.contrib.auth.views import login, logout
from .views import (
AddTeamMember,
AcceptTeamInvitation,
contractor_resumefile_create,
ContractorChatProjectsView,
ContractorFilterView,
@ -11,6 +11,7 @@ from .views import (
ContractorOfficeView,
ContractorProfileView,
ContractorResumeUpdateView,
CreateTeamInvitation,
CustomerProfileCurrentProjectsView,
CustomerProfileOpenProjectsView,
CustomerProfileReviewsView,
@ -44,5 +45,6 @@ urlpatterns = [
urls.url(r'^teams/(?P<pk>\d+)/$', TeamProfileView.as_view(), name='team-profile'),
urls.url(r'^add-team-member/(?P<pk>\d+)/$', AddTeamMember.as_view(), name='add-team-member'),
urls.url(r'^create-team-invitation/(?P<contractor_id>\d+)/$', CreateTeamInvitation.as_view(), name='create-team-invitation'),
urls.url(r'^accept-team-invitation/(?P<owner_id>\d+)/$', AcceptTeamInvitation.as_view(), name='accept-team-invitation'),
]

@ -17,18 +17,19 @@ import json
import natsort
import pydash as _; _.map = _.map_; _.filter = _.filter_
from .mixins import CheckForUserMixin
from .models import User, Team, UserFinancialInfo, ContractorResume, ContractorResumeFiles
from .forms import TeamForm, ContractorResumeFilesForm, ContractorResumeForm
from .mixins import CheckForUserMixin
from .models import User, Team, UserFinancialInfo, ContractorResume, ContractorResumeFiles, TeamInvitation
from archilance import util
from archilance.mixins import BaseMixin
from chat.models import Message
from common.mixins import CustomerRequiredMixin, ContractorRequiredMixin, NoCsrfMixin
from projects.forms import PortfolioForm
from projects.models import Project, Portfolio, Order
from reviews.models import Review
from specializations.models import Specialization
from work_sell.forms import WorkSellForm
from work_sell.models import WorkSell, Picture
from chat.models import Message
from .forms import (
ContractorFilterForm,
@ -386,9 +387,20 @@ class ContractorProfileView(BaseMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
request_user = self.request.user
contractor = self.object
context['worksell_form'] = self.worksell_form_class
context['portfolio_form'] = self.portfolio_form_class
display_team_invitation_button = \
request_user.is_contractor() and \
contractor != request_user and \
request_user.has_team() and \
contractor not in request_user.team.contractors.all()
context['display_team_invitation_button'] = display_team_invitation_button
resume = self.object.contractor_resume
if resume:
@ -432,7 +444,6 @@ class ContractorOfficeView(BaseMixin, DetailView):
# for c in members:
# compl_proj.extend(tuple(o.project for o in c.orders.filter(status='completed')))
grouped_projects = _get_projects_grouped(contractor)
private_open_projects = grouped_projects['private_open_projects']
@ -447,9 +458,11 @@ class ContractorOfficeView(BaseMixin, DetailView):
len(private_open_projects) + len(private_archived_projects) + \
len(team_open_projects) + len(team_archived_projects)
compl_proj = tuple(o.project for o in team.orders.filter(status='completed'))
team_invitation_exclude_contractor_ids = tuple(itertools.chain((contractor.pk,), tuple(c.pk for c in contractor.team.contractors.all())))
context['team_invitation_exclude_contractor_ids'] = team_invitation_exclude_contractor_ids
context['completed_project_count'] = len(compl_proj)
context['reviews'] = Review.objects.filter(target_contractor=contractor)
@ -764,6 +777,7 @@ def contractor_resumefile_create(request):
else:
raise Http404
class CustomerProfileReviewsView(BaseMixin, View):
template_name = 'customer_profile_reviews.html'
@ -780,15 +794,35 @@ class CustomerProfileReviewsView(BaseMixin, View):
return render(request, self.template_name, context)
class AddTeamMember(NoCsrfMixin, LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
# import code; code.interact(local=dict(globals(), **locals()))
class CreateTeamInvitation(NoCsrfMixin, ContractorRequiredMixin, View):
def post(self, request, *args, contractor_id, **kwargs):
contractor1 = request.user
contractor2 = get_object_or_404(User.contractor_objects, pk=contractor_id)
try:
TeamInvitation.objects.create(contractor1=contractor1, contractor2=contractor2)
return JsonResponse({'status': 'success'})
except:
return JsonResponse({'status': 'error'})
class AcceptTeamInvitation(NoCsrfMixin, ContractorRequiredMixin, View):
# TODO: Users can accept invitations by clicking a link, hence the "get":
def get(self, request, *args, owner_id, **kwargs):
contractor1 = get_object_or_404(User.contractor_objects, pk=owner_id)
contractor2 = request.user
# if request.user.is_contractor()...
# kwargs.get('pk')
#
team_invitation = util.get_or_none(TeamInvitation, contractor1=contractor1, contractor2=contractor2)
team = util.get_related_or_none(contractor1, 'team')
return JsonResponse({'status': 'success'})
if team_invitation and team:
team.contractors.add(contractor2)
team_invitation.delete()
return JsonResponse({'status': 'success'})
else:
return JsonResponse({'status': 'error'})
# import code; code.interact(local=dict(globals(), **locals()))

Loading…
Cancel
Save