remotes/origin/PR-39
ArturBaybulatov 10 years ago
parent 1f8713db24
commit 55f13883ba
  1. 24
      projects/migrations/0014_auto_20160823_1912.py
  2. 2
      projects/models.py
  3. 349
      projects/templates/project_detail.html
  4. 26
      projects/templatetags/project_tags.py
  5. 8
      projects/urls.py
  6. 40
      projects/views.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-23 16:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0013_auto_20160819_1735'),
]
operations = [
migrations.AlterModelOptions(
name='project',
options={'ordering': ('-created',), 'verbose_name': 'Проект', 'verbose_name_plural': 'Проекты'},
),
migrations.AddField(
model_name='answer',
name='rejected',
field=models.BooleanField(default=False),
),
]

@ -89,7 +89,6 @@ class Project(models.Model, HitCountMixin):
name = models.CharField(max_length=255)
price_and_term_required = models.BooleanField(default=False)
realty = models.ForeignKey(Realty, blank=True, null=True, related_name='projects')
rejected_answers_count = models.PositiveIntegerField(default=0)
specialization = TreeForeignKey(Specialization, related_name='projects')
state = models.CharField(default='active', max_length=20, choices=STATES)
term = models.IntegerField(default=0)
@ -134,6 +133,7 @@ class Answer(models.Model):
term = models.IntegerField(blank=True, null=True)
term_type = models.CharField(max_length=10, choices=TERMS, blank=True, null=True)
is_archive = models.BooleanField(default=False)
rejected = models.BooleanField(default=False)
content_type = models.ForeignKey(ContentType, limit_choices_to=Q(app_label='users', model='user') | Q(app_label='users', model='team'))
object_id = models.IntegerField()

@ -3,6 +3,7 @@
{% load humanize %}
{% load thumbnail %}
{% load specializtions_tags %}
{% load project_tags %}
{% block content %}
{% include 'partials/header.html' %}
@ -66,15 +67,6 @@
</div>
{% endif %}
<div class="col-lg-2 new-dashed">
{% specialization_widget contractor.pk %}
{# <div class="dashedCol4 dashedCol44 dashedColColor">#}
{# <p class="specUser">#}
{# Специализации:#}
{# </p>#}
{# </div>#}
</div>
<div class="col-lg-3 retts new-list">
<ul class="desListPro">
<li>
@ -102,7 +94,7 @@
<ul class="listProjectIn">
<li><span>Местоположение:</span> {{ project.realty.location.name }}</li>
<li><span>Классификация здания:</span> {{ project.realty.building_classification.name }}</li>
<li><span>Вид строительства:</span> {{ project.realty.type_construction.name }}</li>
<li><span>Вид строительства:</span> {{ project.realty.construction_type.name }}</li>
</ul>
</div>
{% endif %}
@ -163,13 +155,27 @@
{% if request.user.is_contractor %}
{% if request.user.is_contractor %}
{% if answer %}
<div class="candidateBlock disTab">
<div class="candidate can-new">
@ -289,7 +295,7 @@
</span>
<p class="textComm44">
{{ message.text }}
{{ message.text|linebreaksbr }}
</p>
</div>
</div>
@ -448,34 +454,49 @@
{% elif request.user.is_customer and project in request.user.projects.all %}
{% elif request.user.is_customer and project in request.user.projects.all %}
<div class="exBigBlock disTab">
<div class="col-lg-12">
<p class="titleEx">Исполнители</p>
</div>
<div class="col-lg-12 exButton">
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default">
Новые <span>{{ project.answers.count|sub:project.candidates.count }}</span>
</button>
<button type="button" class="btn btn-default">
Кандидаты <span>{{ project.candidates.count }}</span>
</button>
<button type="button" class="btn btn-default">
Отказал <span>{{ project.rejected_answers_count }}</span>
</button>
<div class="btn-group" role="group">
<a href="#new-answers" data-toggle="tab" class="btn btn-default">
Новые <span>{{ project|get_new_answers|length }}</span>
</a>
<a href="#candidate-answers" data-toggle="tab" class="btn btn-default">
Кандидаты <span>{{ project|get_candidate_answers|length }}</span>
</a>
<a href="#rejected-answers" data-toggle="tab" class="btn btn-default">
Отказал <span>{{ project|get_rejected_answers|length }}</span>
</a>
</div>
</div>
</div>
{% if project.answers.exists %}
{% if project.candidates.count > 1 %}
<div class="col-lg-12 compareBlock">
@ -485,11 +506,13 @@
</div>
{% endif %}
<div class="tab-content">
<div id="new-answers" class="tab-pane fade in active">
<div class="col-lg-12 exNew">
<p>Новые исполнители</p>
</div>
{% for answer in project.answers.all %}
{% for answer in project|get_new_answers %}
<div class="candidateBlock disTab">
<div class="candidate">
<div class="col-lg-4">
@ -551,15 +574,17 @@
</div>
<div class="col-lg-3 retts">
{% if answer.author not in project|get_candidates %}
<a href="{% url 'projects:add-candidate' answer_id=answer.pk project_id=project.pk %}" class="candLink candLink1">
Кандидат
</a>
{% endif %}
<a href="{% url 'chat:chat-user' %}" class="candLink candLink2">
предложить проект
</a>
<form action="{% url 'projects:customer-reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
<form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink3">отказ</a>
@ -601,18 +626,20 @@
</span>
<p class="textComm44">
{{ message.text }}
{{ message.text|linebreaksbr }}
</p>
</div>
</div>
{% endfor %}
<div class="comm44 disTab">
{# <div class="i col-lg-10 col-lg-offset-1">#}
{# <a href="#" onclick="return false" class="answerComm">#}
{# Ответить#}
{# </a>#}
{# </div>#}
<!--
<div class="i col-lg-10 col-lg-offset-1">
<a href="#" onclick="return false" class="answerComm">
Ответить
</a>
</div>
-->
<div class="col-lg-10 col-lg-offset-1">
<form action="{% url 'projects:create-answer-message' pk=answer.pk %}" method="POST" novalidate>
@ -628,20 +655,276 @@
</div>
</div>
{% endfor %}
</div>
<div id="candidate-answers" class="tab-pane fade">
<div class="col-lg-12 exNew">
<p>Кандидаты</p>
</div>
{% for answer in project|get_candidate_answers %}
<div class="candidateBlock disTab">
<div class="candidate">
<div class="col-lg-4">
<a href="#" class="aLinkExe">
<div class="imgExecutor">
{% if answer.author.avatar %}
<img src="{{ answer.author.avatar.url }}" alt="execitor-image">
{% else %}
<img src="{% static 'img/profile.jpg' %}" alt="execitor-image">
{% endif %}
</div>
</a>
<p class="nameExecutor">
{% if answer.author|class_name == 'User' %}
<a href="#">{{ answer.author.get_full_name }} [{{ answer.author.username }}]</a>
{% elif answer.author|class_name == 'Team' %}
<a href="#">{{ answer.author.name }}</a>
{% endif %}
</p>
<p class="navv2">На сайте {{ answer.author.created }}</p>
{% if answer.author|class_name == 'User' %}
{% if answer.author.contractor_status == 'free' %}
<div class="statusUser">Свободен</div><!-- ............. -->
{% endif %}
{% endif %}
</div>
<div class="col-lg-3 retts">
{% ratings_widget answer.author.pk 'restList2' %}
{% if answer.author|class_name == 'User' and answer.author.cro %}
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% elif answer.author|class_name == 'Team' %}
<!-- TODO -->
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% endif %}
</div>
<div class="col-lg-2 listCens">
<p>
Цена:
<span>{{ answer.budget|intcomma }}</span>
<i class="fa fa-rub"></i>
</p>
<p>
Срок: <span>{{ answer.term }} {{ answer.get_currency_display }} {{ answer.get_term_type_display|decap }}</span>
</p>
<p>Опубликован: {{ answer.created|date:'M d, Y' }}</p>
</div>
<div class="col-lg-3 retts">
<a href="{% url 'chat:chat-user' %}" class="candLink candLink2">
предложить проект
</a>
<form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink3">отказ</a>
</form>
</div>
<div class="gallMini disTab">
{% for portf in answer.portfolios.all %}
<div class="col-lg-3">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background: url('{{ portf.photos.first.img.url }}') no-repeat center">
<div class="imgFigure"></div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="commBlock44 disTab">
{% for message in answer.messages.all %}
<div class="comm44 disTab">
<div class="col-lg-10 col-lg-offset-1">
{% if message.is_sender_customer %}
<p class="nameComm nameCommAct">
{{ project.customer.get_full_name }}
</p>
{% else %}
<p class="nameComm">
{% if answer.author|class_name == 'User' %}
{{ answer.author.get_full_name }}
{% elif answer.author|class_name == 'Team' %}
{{ answer.author.name }}
{% endif %}
</p>
{% endif %}
<span class="dateComm44">
{{ message.created }}
</span>
<p class="textComm44">
{{ message.text|linebreaksbr }}
</p>
</div>
</div>
{% endfor %}
<div class="comm44 disTab">
<!--
<div class="i col-lg-10 col-lg-offset-1">
<a href="#" onclick="return false" class="answerComm">
Ответить
</a>
</div>
-->
<div class="col-lg-10 col-lg-offset-1">
<form action="{% url 'projects:create-answer-message' pk=answer.pk %}" method="POST" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'projects:detail' pk=project.pk %}">
<div><textarea name="text"></textarea></div>
<div><button type="submit">Отправить</button></div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div id="rejected-answers" class="tab-pane fade">
<div class="col-lg-12 exNew">
<p>Отказал</p>
</div>
{% for answer in project|get_rejected_answers %}
<div class="candidateBlock disTab">
<div class="candidate">
<div class="col-lg-4">
<a href="#" class="aLinkExe">
<div class="imgExecutor">
{% if answer.author.avatar %}
<img src="{{ answer.author.avatar.url }}" alt="execitor-image">
{% else %}
<img src="{% static 'img/profile.jpg' %}" alt="execitor-image">
{% endif %}
</div>
</a>
<p class="nameExecutor">
{% if answer.author|class_name == 'User' %}
<a href="#">{{ answer.author.get_full_name }} [{{ answer.author.username }}]</a>
{% elif answer.author|class_name == 'Team' %}
<a href="#">{{ answer.author.name }}</a>
{% endif %}
</p>
<p class="navv2">На сайте {{ answer.author.created }}</p>
{% if answer.author|class_name == 'User' %}
{% if answer.author.contractor_status == 'free' %}
<div class="statusUser">Свободен</div><!-- ............. -->
{% endif %}
{% endif %}
</div>
<div class="col-lg-3 retts">
{% ratings_widget answer.author.pk 'restList2' %}
{% if answer.author|class_name == 'User' and answer.author.cro %}
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% elif answer.author|class_name == 'Team' %}
<!-- TODO -->
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% endif %}
</div>
<div class="col-lg-2 listCens">
<p>
Цена:
<span>{{ answer.budget|intcomma }}</span>
<i class="fa fa-rub"></i>
</p>
<p>
Срок: <span>{{ answer.term }} {{ answer.get_currency_display }} {{ answer.get_term_type_display|decap }}</span>
</p>
<p>Опубликован: {{ answer.created|date:'M d, Y' }}</p>
</div>
<div class="gallMini disTab">
{% for portf in answer.portfolios.all %}
<div class="col-lg-3">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background: url('{{ portf.photos.first.img.url }}') no-repeat center">
<div class="imgFigure"></div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="commBlock44 disTab">
{% for message in answer.messages.all %}
<div class="comm44 disTab">
<div class="col-lg-10 col-lg-offset-1">
{% if message.is_sender_customer %}
<p class="nameComm nameCommAct">
{{ project.customer.get_full_name }}
</p>
{% else %}
<p class="nameComm">
{% if answer.author|class_name == 'User' %}
{{ answer.author.get_full_name }}
{% elif answer.author|class_name == 'Team' %}
{{ answer.author.name }}
{% endif %}
</p>
{% endif %}
<span class="dateComm44">
{{ message.created }}
</span>
<p class="textComm44">
{{ message.text|linebreaksbr }}
</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endif %}
{% include 'partials/footer.html' %}
</div>

@ -0,0 +1,26 @@
from django import template
from pprint import pprint, pformat
from archilance import util
register = template.Library()
@register.filter
def get_candidates(project):
return tuple(c.answer.author for c in project.candidates.all())
@register.filter
def get_new_answers(project):
return set(project.answers.filter(rejected=False)) - set(c.answer for c in project.candidates.filter(answer__rejected=False))
@register.filter
def get_candidate_answers(project):
return tuple(c.answer for c in project.candidates.filter(answer__rejected=False))
@register.filter
def get_rejected_answers(project):
return project.answers.filter(rejected=True)
# import code; code.interact(local=dict(globals(), **locals()))

@ -3,24 +3,23 @@ from django.views.generic import TemplateView
from .views import (
add_candidate,
CandidateDeleteView,
contractor_portfolio_create,
ContractorAnswerArchiveView,
ContractorPortfolioTrashView,
ContractorPortfolioUpdateView,
ContractorRejectProjectAnswerView,
CustomerProjectCreateView,
CustomerProjectDeleteView,
CustomerProjectEditView,
CustomerProjectRestoreView,
CustomerProjectTrashView,
CustomerRejectProjectAnswerView,
OfferOrderView,
ProjectAnswerCreateMessageView,
ProjectComparisonView,
ProjectDetailWithAnswerView,
ProjectFilterView,
RejectProjectAnswerView,
sort_candidates,
CandidateDeleteView,
)
app_name = 'projects'
@ -38,8 +37,7 @@ urlpatterns = [
urls.url(r'^(?P<pk>\d+)/delete/$', CustomerProjectDeleteView.as_view(), name='customer-project-delete'),
urls.url(r'^create-answer-message/(?P<pk>\d+)/$', ProjectAnswerCreateMessageView.as_view(), name='create-answer-message'),
urls.url(r'^contractor-reject-project-answer/(?P<pk>\d+)/$', ContractorRejectProjectAnswerView.as_view(), name='contractor-reject-project-answer'),
urls.url(r'^customer-reject-project-answer/(?P<pk>\d+)/$', CustomerRejectProjectAnswerView.as_view(), name='customer-reject-project-answer'),
urls.url(r'^reject-project-answer/(?P<pk>\d+)/$', RejectProjectAnswerView.as_view(), name='reject-project-answer'),
urls.url(r'^portfolio/create/$', contractor_portfolio_create, name='contractor-portfolio-create'),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'),

@ -1,6 +1,7 @@
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
@ -178,43 +179,24 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
return redirect(redirect_to)
class ContractorRejectProjectAnswerView(BaseMixin, View):
class RejectProjectAnswerView(BaseMixin, View):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_contractor():
if request.user.is_authenticated():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
raise PermissionDenied
def post(self, request, *args, **kwargs):
if request.user.is_contractor():
answer = get_object_or_404(request.user.contractor_answers, pk=kwargs.get('pk'))
elif request.user.is_customer():
project = get_object_or_404(request.user.projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
answer.delete()
messages.info(request, 'Вы успешно отказались от проекта')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
class CustomerRejectProjectAnswerView(BaseMixin, View):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
def post(self, request, *args, **kwargs):
answer_id = kwargs.get('pk')
project = get_object_or_404(request.user.projects, answers__pk=answer_id)
answer = get_object_or_404(project.answers, pk=answer_id)
answer.delete()
project.rejected_answers_count = F('rejected_answers_count') + 1
project.save()
answer.rejected = True
answer.save()
messages.info(request, 'Вы успешно отказали пользователю в проекте')
messages.info(request, 'Успешный отказ от проекта')
redirect_to = request.POST.get('next')
return redirect(redirect_to)

Loading…
Cancel
Save