from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin from django.core.exceptions import PermissionDenied from django.core.files.base import ContentFile from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse, reverse_lazy from django.db.models import Q, F from django.http import HttpResponseForbidden, JsonResponse, 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 hitcount.models import HitCount from hitcount.views import HitCountMixin from pprint import pprint, pformat import json import natsort import pydash as _; _.map = _.map_; _.filter = _.filter_ import re from .mixins import LastAccessMixin from archilance import util from archilance.mixins import BaseMixin from common.mixins import CustomerRequiredMixin, ContractorRequiredMixin, NoCsrfMixin from users.models import User, Team from work_sell.models import Picture, WorkSell, WorkSellPhoto from .models import ( Answer, AnswerFile, AnswerMessage, Arbitration, Candidate, Order, Portfolio, PortfolioPhoto, Project, ProjectFile, Realty, TERM_TYPE_MORPHS, ) from .forms import ( ContractorPortfolioTrashForm, CustomerProjectDeleteForm, CustomerProjectEditForm, CustomerProjectRestoreForm, CustomerProjectTrashForm, PortfolioForm, ProjectAnswerForm, ProjectAnswerMessageForm, ProjectFilterForm, ProjectFilterRealtyForm, RealtyForm, ) class ProjectDetailWithAnswerView(BaseMixin, View): form_class = ProjectAnswerForm template_name = 'project_detail.html' def get(self, request, *args, **kwargs): context = self.get_context_data(**_.merge({}, request.GET, kwargs)) context.update({'TERM_TYPE_MORPHS': TERM_TYPE_MORPHS}) project = get_object_or_404(Project, pk=kwargs.get('pk')) context.update({'project': project}) hit_count = HitCount.objects.get_for_object(project) HitCountMixin.hit_count(request, hit_count) if request.user.is_authenticated() and request.user.is_contractor(): project_answers = project.answers.filter(rejected=False) contractor = request.user answer = _.first(_.filter(project_answers, lambda a: a.author == contractor)) if not answer: team = util.get_related_or_none(contractor, 'team') if team: answer = _.first(_.filter(project_answers, lambda a: a.author == team)) context.update({'answer': answer}) if not answer: team = util.get_related_or_none(contractor, 'team') if team: context.update({'can_answer_as_team': True}) if request.GET.get('answer_as_team') == 'on': context.update({'answer_as_team': True}) form = self.form_class(request=request, answer_as_team=True, project=project) else: form = self.form_class(request=request, project=project) context.update({'form': form}) return render(request, self.template_name, context) def post(self, request, *args, **kwargs): if request.user.is_authenticated() and request.user.is_contractor(): context = self.get_context_data(**kwargs) answer_as_team = None project = get_object_or_404(Project, pk=kwargs.get('pk')) # TODO: Does this work? if request.POST.get('answer_as_team') == 'on': answer_as_team = True if answer_as_team: form = self.form_class(request.POST, request=request, answer_as_team=True, project=project) else: form = self.form_class(request.POST, request=request, project=project) project = get_object_or_404(Project, pk=kwargs.get('pk')) context.update({'project': project}) if form.is_valid(): answer = form.save(commit=False) answer.project = project answer.author = request.user.team if answer_as_team else request.user answer.save() form.save_m2m() for file in request.FILES.getlist('new_files'): if len(file.name) <= 255: AnswerFile.objects.create(file=file, name=file.name, answer=answer) message = AnswerMessage(text=form.cleaned_data.get('text'), is_sender_customer=False) message.answer = answer message.contractor_or_team = answer.author message.save() messages.info(request, 'Отклик успешно размещён') redirect_to = request.POST.get('next') return redirect(redirect_to) else: if form.errors: messages.info(request, ( '
Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
context.update({'form': form})
return render(request, self.template_name, context)
else:
raise PermissionDenied
class ProjectAnswerCreateMessageView(BaseMixin, View):
form_class = ProjectAnswerMessageForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request)
if form.is_valid():
message = form.save(commit=False)
if request.user.is_contractor():
answer = get_object_or_404(Answer, pk=kwargs.get('pk'))
message.answer = answer
message.is_sender_customer = False
if isinstance(answer.author, User) and answer.author == request.user:
message.contractor_or_team = request.user
elif isinstance(answer.author, Team):
team = util.get_related_or_none(request.user, 'team')
if team and answer.author == team:
message.contractor_or_team = team
elif request.user.is_customer():
answer = get_object_or_404(Answer, pk=kwargs.get('pk')) # TODO: Perform additional checks
message.answer = answer
message.is_sender_customer = True
message.save()
form.save_m2m()
messages.info(request, 'Сообщение успешно размещено')
else:
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class RejectProjectAnswerView(NoCsrfMixin, LoginRequiredMixin, BaseMixin, View):
def post(self, request, *args, **kwargs):
if request.user.is_contractor():
answer = None
contractor_answer = util.get_or_none(request.user.contractor_answers, pk=kwargs.get('pk'))
if contractor_answer:
answer = contractor_answer
elif request.user.team:
answer = util.get_or_none(request.user.team.answers, pk=kwargs.get('pk'))
if not answer:
raise Http404
elif request.user.is_customer():
project = get_object_or_404(request.user.customer_projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
answer.rejected = True
answer.save()
candidate = Candidate.objects.filter(answer=answer)
if candidate:
candidate.delete()
messages.info(request, 'Успешный отказ от проекта')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class RestoreProjectAnswerView(NoCsrfMixin, LoginRequiredMixin, BaseMixin, View):
def post(self, request, *args, **kwargs):
if request.user.is_contractor():
answer = None
contractor_answer = util.get_or_none(request.user.contractor_answers, pk=kwargs.get('pk'))
if contractor_answer:
answer = contractor_answer
elif request.user.team:
answer = util.get_or_none(request.user.team.answers, pk=kwargs.get('pk'))
if not answer:
raise Http404
elif request.user.is_customer():
project = get_object_or_404(request.user.customer_projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
answer.rejected = False
answer.save()
messages.info(request, 'Проект успешно восстановлен')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class ProjectFilterView(BaseMixin, View):
template_name = 'project_filter.html'
form_class = ProjectFilterForm
realty_form = ProjectFilterRealtyForm
def get(self, request, *args, **kwargs):
form = self.form_class(request.GET, request=request)
realty_form = self.realty_form(request.GET, request=request, prefix='realty_form')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
projects = Project.objects.filter(state='active')
if form.is_valid() and realty_form.is_valid():
ord = None
keywords = form.cleaned_data.get('keywords')
cro = form.cleaned_data.get('cro')
work_type = form.cleaned_data.get('work_type')
specialization = form.cleaned_data.get('specialization')
building_classification = realty_form.cleaned_data.get('building_classification')
construction_type = realty_form.cleaned_data.get('construction_type')
location = realty_form.cleaned_data.get('location')
if keywords:
keywords = tuple(filter(None, re.split(r'\s|,|;', keywords)))
for k in keywords:
projects = projects.filter(Q(name__icontains=k) | Q(text__icontains=k))
if cro:
projects = projects.filter(cro=cro)
if work_type:
projects = projects.filter(work_type=work_type)
if specialization:
projects = projects.filter(
specialization__lft__gte=specialization.lft,
specialization__rght__lte=specialization.rght,
)
if building_classification:
projects = projects.filter(realty__building_classification=building_classification)
if construction_type:
projects = projects.filter(realty__construction_type=construction_type)
if location:
projects = projects.filter(
realty__location__lft__gte=location.lft,
realty__location__rght__lte=location.rght,
)
order_by = form.cleaned_data.get('order_by')
last_order_by = form.cleaned_data.get('last_order_by')
reverse_order = form.cleaned_data.get('reverse_order')
if order_by:
reverse_order = not reverse_order if order_by == last_order_by else False
ord = order_by
last_order_by = ord
elif last_order_by:
ord = last_order_by
manual_sort = None
if ord and ord == 'views':
projects = natsort.natsorted(projects.all(), key=lambda p: p.hit_count.hits, reverse=reverse_order)
manual_sort = True
elif ord:
projects = projects.order_by('-%s' % ord if reverse_order else ord)
context.update({
'last_order_by': last_order_by,
'reverse_order': reverse_order,
})
project_count = len(projects) if manual_sort else projects.count()
display_msg = 'Найдено %s проектов' % project_count if project_count > 0 else 'Ничего не найдено'
else:
display_msg = 'Пожалуйста, введите корректные данные'
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
paginator = Paginator(projects if manual_sort else projects.all(), settings.PAGE_SIZE)
page = request.GET.get('page')
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
context.update({
'form': form,
'realty_form': realty_form,
'projects': projects,
'is_paginated': True,
'page_obj': projects,
'display_msg': display_msg,
})
return render(request, self.template_name, context)
class CustomerProjectCreateView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_create.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def get(self, request, *args, **kwargs):
form = self.form_class(request=request)
realty_form = self.realty_form(request=request, prefix='realty_form')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request) # Passing `request.FILES` seems unnecessary here. Files are added manually below
form.is_valid()
realty = form.cleaned_data.get('realty')
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
if form.is_valid() and realty_form.is_valid():
project = form.save(commit=False)
project.customer = request.user
project.save()
form.save_m2m()
Order.objects.create(project=project, secure=project.deal_type == 'secure_deal')
for file in request.FILES.getlist('new_files'):
ProjectFile.objects.create(file=file, project=project)
if realty:
realty_form.save()
else:
realty = realty_form.save(commit=False)
realty.user = request.user
realty.save()
realty_form.save_m2m()
project.realty = realty # Connect a realty with a project
project.save()
messages.info(request, 'Проект успешно создан')
redirect_to = reverse('projects:detail', kwargs={'pk': project.pk})
return redirect(redirect_to)
else:
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
class CustomerProjectEditView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_edit.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def get(self, request, *args, **kwargs):
project = get_object_or_404(request.user.customer_projects, pk=kwargs.get('pk'))
form = self.form_class(instance=project, request=request)
realty = project.realty
if realty:
realty_form = self.realty_form(instance=project.realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request=request, prefix='realty_form')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
project = get_object_or_404(request.user.customer_projects, pk=kwargs.get('pk'))
form = self.form_class(request.POST, request.FILES, request=request, instance=project)
form.is_valid()
realty = form.cleaned_data.get('realty')
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
if form.is_valid() and realty_form.is_valid():
project = form.save(commit=False)
project.customer = request.user
project.files = form.cleaned_data.get('files') # TODO: Should we somehow get rid of this explicit assignment?
project.save()
form.save_m2m()
for file in request.FILES.getlist('new_files'):
proj_file = ProjectFile.objects.create(file=file, project=project)
proj_file.save()
if realty:
realty_form.save()
else:
realty = realty_form.save(commit=False)
realty.user = request.user
realty.save()
realty_form.save_m2m()
project.realty = realty # Connect a realty with a project
project.save()
messages.info(request, 'Проект успешно отредактирован')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
else:
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
class ContractorAnswerArchiveView(View):
def post(self, request, *args, **kwargs):
project_pk = request.POST.get('project_pk')
user_pk = request.POST.get('user_pk')
answer = Answer.objects.filter(project_id=project_pk,object_id=user_pk, content_type__model='user').first()
answer.is_archive = True
answer.save()
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class ContractorPortfolioTrashView(View):
form_class = ContractorPortfolioTrashForm
def post(self, request, *args, **kwargs):
form = self.form_class(_.merge({}, request.POST, kwargs), request=request)
if form.is_valid():
portfolio = form.cleaned_data.get('pk')
portfolio.delete()
messages.info(request, 'Портфолио удалено')
else:
messages.info(request, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class CustomerProjectTrashView(View):
form_class = CustomerProjectTrashForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'trashed'
project.save()
messages.info(req, 'Проект перемещён в корзину')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class CustomerProjectRestoreView(View):
form_class = CustomerProjectRestoreForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'active'
project.save()
messages.info(req, 'Проект восстановлен из корзины')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class CustomerProjectDeleteView(View):
form_class = CustomerProjectDeleteForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'deleted'
project.save()
messages.info(req, 'Проект удалён навсегда')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class ProjectComparisonView(DetailView):
model = Project
template_name = 'comparison.html'
def get_context_data(self, **kwargs):
c = super().get_context_data(**kwargs)
c['TERM_TYPE_MORPHS'] = TERM_TYPE_MORPHS
return c
def add_candidate(request, answer_id, project_id):
answer = Answer.objects.get(pk=answer_id)
project = Project.objects.get(pk=project_id)
count_answers = Candidate.objects.filter(project=project).count()
count_answers += 1
candidate = Candidate.objects.filter(answer=answer).first()
if not candidate:
Candidate.objects.create(answer=answer, project=project, position=count_answers)
redirect_to = '%s%s' % (reverse('projects:detail', kwargs={'pk': project_id}), '#answers')
return redirect(redirect_to)
class CandidateDeleteView(DeleteView):
model = Candidate
def get_success_url(self):
return reverse('projects:comparison', kwargs={'pk': self.object.project_id})
def get(self, *args, **kwargs):
return self.post(*args, **kwargs)
def sort_candidates(request):
if request.is_ajax():
items = request.POST.getlist('items[]')
i = 1
for item in items:
candidate = Candidate.objects.get(pk=item)
candidate.position = i
candidate.save()
i += 1
data = {
'success': 'ok',
}
return HttpResponse(json.dumps(data), content_type='application/json')
class CustomerOfferOrderView(View):
template_name = 'chattest.html'
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' + str(order.pk)
return HttpResponseRedirect(redirect_url)
def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using generic
if request.is_ajax():
form = PortfolioForm(data=request.POST)
# import code; code.interact(local=dict(globals(), **locals()))
if form.is_valid():
duplicate = form.cleaned_data.get('duplicate')
instance = form.save(commit=False)
instance.user = request.user
instance.save()
if duplicate:
work_sell = WorkSell()
work_sell.name = instance.name
work_sell.budget = instance.budget
work_sell.building_classification = instance.building_classification
work_sell.construction_type = instance.construction_type
work_sell.currency = instance.currency
work_sell.description = instance.description
work_sell.term = instance.term
work_sell.term_type = instance.term_type
work_sell.contractor = instance.user
work_sell.save()
images_ids = request.POST.get('images_ids').split(';')[:-1]
for pk in images_ids:
picture = Picture.objects.get(pk=pk)
temp_file = ContentFile(picture.file.read())
temp_file.name = picture.file.name
p_photo = PortfolioPhoto()
p_photo.img = temp_file
p_photo.portfolio = instance
p_photo.save()
if duplicate:
w_photo = WorkSellPhoto()
w_photo.img = temp_file
w_photo.worksell = work_sell
w_photo.save()
data = {'status': 'ok'}
else:
data = {'status': 'no', 'form_errors': form.errors}
return HttpResponse(json.dumps(data), content_type='application/json')
else:
raise Http404
class ContractorPortfolioUpdateView(UpdateView):
model = Portfolio
form_class = PortfolioForm
template_name = 'contractor_portfolio_edit.html'
def get_success_url(self):
return reverse('users:contractor-profile',kwargs={'pk':self.object.user_id})
class PortfolioDelete(DeleteView):
model = Portfolio
success_url = reverse_lazy('users:contractor-profile')
class PortfolioDetail(DetailView):
model = Portfolio
template_name = 'portfolio_detail.html'
def dispatch(self, request, *args, **kwargs):
if not self.get_object().user:
return HttpResponseForbidden('404 Not Found')
return super().dispatch(request, *args, **kwargs)
class ArbitrationCreateView(CreateView):
model = Arbitration
fields = (
'order',
'user',
'text',
)
def form_valid(self, form):
if self.request.is_ajax():
self.object = form.save()
data = {
'status': 'ok',
}
return JsonResponse(data)
return super().form_valid(form)
def form_invalid(self, form):
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
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'})
# import code; code.interact(local=dict(globals(), **locals()))