remotes/origin/PR-39
Mukhtar 10 years ago
commit 83e07bfd96
  1. 20
      archilance/util.py
  2. 73
      assets/css/main.css
  3. 2
      assets/js/chat.js
  4. 9
      assets/js/main.js
  5. 8
      chat/templates/chat_contractor.html
  6. 9
      chat/templates/chat_customer.html
  7. 32
      projects/forms.py
  8. 24
      projects/migrations/0014_auto_20160823_1912.py
  9. 29
      projects/migrations/0015_auto_20160824_1538.py
  10. 19
      projects/models.py
  11. 22
      projects/templates/customer_project_create.html
  12. 32
      projects/templates/customer_project_edit.html
  13. 151
      projects/templates/portfolio_detail.html
  14. 726
      projects/templates/project_detail.html
  15. 22
      projects/templates/project_filter.html
  16. 26
      projects/templatetags/project_tags.py
  17. 12
      projects/urls.py
  18. 255
      projects/views.py
  19. 8
      ratings/templates/templatetags/ratings_widget.html
  20. 37
      ratings/templates/templatetags/specializations_widget.html
  21. 18
      ratings/templatetags/specializtions_tags.py
  22. 8
      templates/partials/header.html
  23. 6
      update.sh
  24. 50
      users/templates/contractor_filter.html
  25. 63
      users/templates/contractor_office.html
  26. 15
      users/templates/contractor_profile.html
  27. 4
      users/templates/portfolio_create_form.html
  28. 45
      users/templates/worksell_create_form.html
  29. 1
      users/views.py
  30. 39
      wallets/views.py
  31. 15
      work_sell/templates/worksell_detail.html
  32. 5
      work_sell/templates/worksells_list.html

@ -1,9 +1,11 @@
from django.core import validators
from django.shortcuts import _get_queryset
from django.utils import timezone
from pprint import pprint, pformat
import natsort
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
import string
def take(coll, n):
@ -89,3 +91,21 @@ def decap(s):
raise TypeError('String expected')
return s[0].lower() + s[1:] if len(s) > 0 else s
def random_ident(length=8):
return ''.join(
[random.choice(string.ascii_lowercase)] +
_.times(lambda x_: random.choice(string.ascii_letters + string.digits), length-1)
)
def validate_phone(text):
text = text.replace(' ', '').replace('-', '')
validate = validators.RegexValidator(
regex=r'^((\+7|8)(\(\d{3}\)|(\d{3}))\d{7})$',
message='Неверный номер телефона. Формат: +71112223344',
)
validate(text)

@ -1041,7 +1041,7 @@ footer:after {
background-position: 0 -26px;
}
.rightPro ul li:last-child:before {
.rightPro ul li:nth-child(3):before {
height: 22px;
background: url('../img/cenaList.png') no-repeat left;
background-size: cover;
@ -5639,13 +5639,13 @@ input[type="radio"]:checked + span {
right: 3px;
top: 16px;
}
#portfolio-add-form .textAreaBlock2 p{
#portfolio-add-form .textAreaBlock2 p, #worksell-add-form .textAreaBlock2 p{
font-size: 15px;
font-family: 'Arial-MT-Regular', sans-serif;
font-weight: bold;
color: #2c2c2c;
}
#portfolio-add-form #text-new{
#portfolio-add-form #text-new, #worksell-add-form #text-new{
margin-top: 0;
}
.textAreaBlock2 input{
@ -5657,10 +5657,10 @@ input[type="radio"]:checked + span {
color: #9c9c9c;
font-family: 'Arial-MT-Regular', sans-serif;
}
#portfolio-add-form .polsF1 .btn{
#portfolio-add-form .polsF1 .btn, #worksell-add-form .polsF1 .btn{
border:1px solid #c2c2c2;
}
#portfolio-add-form .bootstrap-select{
#portfolio-add-form .bootstrap-select, #worksell-add-form .bootstrap-select{
width: 100% !important;
}
.textAreaBlock2 .add_file_to_port{
@ -5750,7 +5750,7 @@ input[type="radio"]:checked + span {
margin: 4px 0 4px 0;
}
.birth_edit_dat .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn){
width: 190px;
width: 190px !important;
}
.btn-submit-link{
color: #333 !important;
@ -5758,4 +5758,65 @@ input[type="radio"]:checked + span {
border-color: #ccc !important;
margin-bottom: 0 !important
}
.infoProfile .dropdown-menu{
height: 316px;
}
.form-regestration{
padding: 20px 0 40px 0;
}
#resume-text-edit .modal-title, #fullfill-balance .modal-title, #withdraw-money .modal-title{
font-family: Arial, Verdana, Helvetica, sans-serif;
font-size: 16px;
text-transform: uppercase;
color: #2c2c2c;
font-weight: bold;
}
#withdraw-money .modal-body, #fullfill-balance .modal-body{
width: 100%;
display: table;
}
#withdraw-money .modal-body p, #fullfill-balance .modal-body p{
color: #2c2c2c;
font-family: Arial, Verdana, Helvetica, sans-serif;
font-weight: bold;
}
#withdraw-money .modal-footer .btn, #fullfill-balance .modal-footer .btn{
padding: 10px 34px;
border-radius: 40px;
font-family: 'pfdintextcomppro-regular', sans-serif;
font-size: 16px;
text-transform: uppercase;
letter-spacing: 2px;
}
:focus{
outline: none !important;
}
#withdraw-money .modal-footer .btn.btn-primary, #fullfill-balance .modal-footer .btn.btn-primary{
background-color: #000;
}
.modal-footer{
border-top: none;
}
#resume-text-edit .btn-submit-link{
float: right;
margin-top: 15px;
}
#resume-text-edit .searchF1{
padding: 0;
}
.list-summ-block{
min-height: 195px;
}
.select2-dropdown-open > a.select2-choice{
border: 1px solid #ff0029 !important;
}
.select2-display-none {
border: 1px solid #ff0029 !important;
}
.bootstrap-select.btn-group .dropdown-menu li a span.text {
font-size: 14px;
display: block;
text-transform: initial;
letter-spacing: normal;
}
/*end_new*/

@ -1,6 +1,6 @@
var SocketHandler = function () {
domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var url = 'ws://' + domain + '/chat/' + userId + '/';
var sock = new WebSocket(url);
var intervalId;
sock.onopen = function () {

@ -3,9 +3,14 @@ $(document).ready(function(){
$('.messd:first').addClass('mesAct');
$('.orderBlock:first').addClass('orAct');
if(!$.cookie('slideResVisible')) {
if ($.cookie('slideResVisible')) {
$('.slideRes').slideDown(300);
$('.resButtonF1').css('transform','rotate(0deg)');
$('#extraFields').val('on')
} else {
$('.slideRes').slideUp(300);
$('.resButtonF1').css('transform','rotate(180deg)');
$('#extraFields').val('')
}
$('.resButtonF1').click(function(e){
@ -15,10 +20,12 @@ $(document).ready(function(){
$('.slideRes').slideUp(300);
$(this).css('transform','rotate(180deg)');
$.cookie('slideResVisible', '', {expires: new Date(new Date().getTime() + 300000)}) // 5 minutes
$('#extraFields').val('')
} else {
$('.slideRes').slideDown(300);
$(this).css('transform','rotate(0deg)');
$.cookie('slideResVisible', 'on', {expires: new Date(new Date().getTime() + 300000)})
$('#extraFields').val('on')
}
});

@ -47,7 +47,13 @@
{% endthumbnail %}
</div>
<p class="nameMess">
<div><a href="#" style="color:black;">{{ contact.username }}</a></div>
{% if contact.is_contractor %}
{% url "users:contractor-profile" pk=contact.pk as contact_url %}
{% else %}
{% url "users:customer-profile-open-projects" pk=contact.pk as contact_url %}
{% endif %}
<div><a href="{{ contact_url }}" style="color:black;">{{ contact.username }}</a></div>
</p>
<a href="#" class="conMess">Контакты</a>

@ -41,7 +41,12 @@
{% endif %}
</div>
<p class="nameMess">
<div><a href="#">{{ contact.username }}</a></div>
{% if contact.is_contractor %}
{% url "users:contractor-profile" pk=contact.pk as contact_url %}
{% else %}
{% url "users:customer-profile-open-projects" pk=contact.pk as contact_url %}
{% endif %}
<div><a href="{{ contact_url }}">{{ contact.username }}</a></div>
</p>
<a href="#" data-target="#userModal" data-toggle="modal"
@ -148,7 +153,7 @@
Обсуджение задания и условий выполнения работы. Подтверждение заказа исполнителем.
</p>
</div>
<div id="order-stages" class="stepssBlock"></div>
<div class="stepssBlock box-sizing disTab" id="reserveSpace" style="display:none;">

@ -6,6 +6,7 @@ from pprint import pprint, pformat
import itertools
from .models import Project, ProjectFile, Portfolio, Answer, AnswerMessage, Realty, PortfolioPhoto, Stage
from archilance import util
from common.models import Location
from specializations.models import Specialization
from users.models import User, Team
@ -117,12 +118,27 @@ class CustomerProjectEditForm(forms.ModelForm):
super().__init__(*args, **kwargs)
self.fields['realty'].empty_label = 'Создать новый'
self.fields['realty'].queryset = self.request.user.realties.all()
self.fields['budget'].required = False
self.fields['currency'].required = False
self.fields['term_type'].required = False
self.fields['specialization'].queryset = Specialization.objects.root_nodes()[0].get_descendants()
# self.fields['specialization'].queryset = Specialization.objects # Migrate with this enabled
if self.instance.pk:
self.fields['files'].queryset = self.instance.files
def clean(self):
cleaned_data = super().clean()
if not cleaned_data.get('budget_by_agreement'):
if not cleaned_data.get('budget'): raise forms.ValidationError({'budget': 'Это поле обязательно'})
if not cleaned_data.get('currency'): raise forms.ValidationError({'currency': 'Это поле обязательно'})
if not cleaned_data.get('term_type'): raise forms.ValidationError({'term_type': 'Это поле обязательно'})
return cleaned_data
class RealtyForm(forms.ModelForm):
@ -145,8 +161,24 @@ class RealtyForm(forms.ModelForm):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['name'].required = False
self.fields['location'].queryset = Location.objects.root_nodes()[0].get_descendants()
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
def clean_name(self):
name = self.cleaned_data.get('name')
if not name:
if self.data.get('extra_fields'):
raise forms.ValidationError('Это поле обязательно')
else:
if not self.instance.pk:
name = self.instance.name
else:
name = 'Новый объект %s' % util.random_ident(length=20)
return name
class PortfolioForm(forms.ModelForm):

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

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-24 12:38
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('projects', '0014_auto_20160823_1912'),
]
operations = [
migrations.AlterModelOptions(
name='portfolio',
options={'ordering': ['-created'], 'verbose_name': 'Портфолио', 'verbose_name_plural': 'Портфолио'},
),
migrations.RemoveField(
model_name='project',
name='rejected_answers_count',
),
migrations.AddField(
model_name='portfolio',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

@ -78,7 +78,7 @@ class Project(models.Model, HitCountMixin):
('trashed', 'В корзине'),
('deleted', 'Удален'),
)
budget = models.DecimalField(max_digits=10, decimal_places=0)
budget_by_agreement = models.BooleanField(default=False)
created = models.DateTimeField(default=timezone.now)
@ -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()
@ -278,11 +278,25 @@ class Portfolio(models.Model):
term_type = models.CharField(max_length=20, choices=TERMS, default='hour', null=True, blank=True)
user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True)
worksell = models.BooleanField(default=False)
created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.name
def get_prev(self):
try:
return self.get_previous_by_created()
except self.DoesNotExist:
return None
def get_next(self):
try:
return self.get_next_by_created()
except self.DoesNotExist:
return None
class Meta:
ordering = ['-created']
verbose_name = 'Портфолио'
verbose_name_plural = 'Портфолио'
@ -291,6 +305,7 @@ class Portfolio(models.Model):
return photo and photo.img
class PortfolioPhoto(models.Model):
img = models.ImageField(upload_to='projects/portfolio')
portfolio = models.ForeignKey(Portfolio, related_name='photos')

@ -14,6 +14,8 @@
<form action="{% url 'projects:customer-project-create' %}" method="POST" enctype="multipart/form-data" novalidate>
{% csrf_token %}
<input type="hidden" id="extraFields" name="extra_fields" value="">
<div class="chatBlock new-rass new-rass2 disTab">
<div class="col-lg-9">
<p class="new-pp new-pp3">Формирование заказа</p>
@ -52,7 +54,7 @@
<p class="type-work">Тип работы <span style="color: red">{{ form.work_type.errors.as_text }}</span></p>
<div class="mail-block type-work-inset">
{% for id, name in form.work_type.field.choices %}
{% for id, text in form.work_type.field.choices %}
<div class="inset-mb">
<label>
<input
@ -64,7 +66,7 @@
<span></span>
</label>
<p>{{ name }}</p>
<p>{{ text }}</p>
</div>
{% endfor %}
</div>
@ -198,6 +200,7 @@
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта<br><span style="color: red">{{ form.realty.errors.as_text }}</span></div>
@ -271,21 +274,12 @@
<p>Требуется допуск (СРО)</p>
</div>
</div>
{# {% if back %}<a href='{{ back }}'>Назад</a>{% endif %}#}
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
</div>
</div>
</form>

@ -13,7 +13,9 @@
<form action="{% url 'projects:customer-project-edit' pk=pk %}" method="POST" enctype="multipart/form-data" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'projects:detail' pk=pk %}">
<input type="hidden" id="extraFields" name="extra_fields" value="">
<div class="chatBlock new-rass new-rass2 disTab">
<div class="col-lg-9">
@ -92,22 +94,22 @@
</div>
<div class="polsF1 disTab -spec-select-container">
<div class="col-lg-3">
<input type='hidden' class="-spec-select -spec-select-level-1" style="width: 100%">
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3">
<input type='hidden' class="-spec-select -spec-select-level-2" style="width: 100%">
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3">
<input type='hidden' class="-spec-select -spec-select-level-3" style="width: 100%">
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3">
<input type='hidden' class="-spec-select -spec-select-level-4" style="width: 100%">
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<input type="hidden" class="-chosen-spec-id" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}">
<input type="hidden" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}" class="-chosen-spec-id">
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Бюджет <span style="color: red">{{ form.budget.errors.as_text }}</span></div>
@ -258,19 +260,19 @@
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение:</div>
<div class="col-lg-12">Местоположение <span style="color: red">{{ realty_form.location.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<input type='hidden' class="-location-select -location-select-country" style="width: 100%">
<input type="hidden" class="-location-select -location-select-country" style="width: 100%">
</div>
<div class="col-lg-3">
<input type='hidden' class="-location-select -location-select-region" style="width: 100%">
<input type="hidden" class="-location-select -location-select-region" style="width: 100%">
</div>
<div class="col-lg-3">
<input type='hidden' class="-location-select -location-select-city" style="width: 100%">
<input type="hidden" class="-location-select -location-select-city" style="width: 100%">
</div>
<input type="hidden" id="chosenLocationId" name="{{ realty_form.location.html_name }}" value="{{ realty_form.location.value }}">
@ -288,17 +290,11 @@
<p>Требуется допуск СРО</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Сохранить">
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Сохранить">
</div>
</div>
</div>
</form>

@ -0,0 +1,151 @@
{% extends 'partials/base.html' %}
{% load staticfiles %}
{% load thumbnail %}
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">{{ object }}</p>
{% if object.get_prev %}
<div class="all all1">
<p><a href="{% url 'projects:contractor-portfolio-detail' object.get_prev.pk %}"> предыдущий</a></p>
</div>
{% endif %}
{% if object.get_next %}
<div class="all all2">
<p><a href="{% url 'projects:contractor-portfolio-detail' object.get_next.pk %}">следующий</a></p>
</div>
{% endif %}
</div>
{# <div class="btnReadyBlock disTab">#}
{# <div class="triangle1"></div>#}
{# <div class="col-lg-3">#}
{# <p class="cenaReady">{{ object.budget }} <i class="fa fa-rub"></i></p>#}
{# </div>#}
{# {% if request.user.is_authenticated %}#}
{# <div class="col-lg-3">#}
{# <a href="{% url 'users:contractor-profile' object.contractor.pk %}" class="linkReady LR1">#}
{# купить#}
{# </a>#}
{# </div>#}
{##}
{# <div class="col-lg-3">#}
{# <a href="{% url 'chat:chat-user' %}?user_id={{ object.contractor.pk }}" class="linkReady LR2">#}
{# Написать сообщение#}
{# </a>#}
{# </div>#}
{# {% endif %}#}
{# <div class="col-lg-3">#}
{# <a href="{% url 'users:contractor-profile' object.contractor.pk %}" class="linkReady LR3">#}
{# посмотреть контакты#}
{# </a>#}
{# </div>#}
{# </div>#}
<div class="desReadyBlock">
{% if object.text %}
<div class="col-lg-10 col-lg-offset-1">
<p class="titleReady">Описание:</p>
</div>
<div class="col-lg-10 col-lg-offset-1">
<p class="textReady">
{{ object.text }}
</p>
</div>
{% endif %}
<div class="col-lg-12 sliderReady">
<div class="arroww prev3"></div>
<div class="arroww next3"></div>
<div class="swiper-container gallery-top">
<div class="swiper-wrapper">
<div class="swiper-slide" style="background-image:url('{% static object.get_cover.url %}')"></div>
{% for photo in object.photos.all %}
<div class="swiper-slide" style="background-image:url('{% static photo.img.url %}')"></div>
{% endfor %}
</div>
</div>
<div class="swiper-container gallery-thumbs">
<div class="swiper-wrapper">
<div class="swiper-slide is-selected"
style="background-image:url('{% static object.get_cover.url %}')"></div>
{% for photo in object.photos.all %}
<div class="swiper-slide" style="background-image:url('{% static photo.img.url %}')"></div>
{% endfor %}
</div>
</div>
</div>
{# <div class="col-lg-12">#}
{# <div class="triangle2"></div>#}
{# <div class="arr arr1"></div>#}
{# <div class="arr arr2"></div>#}
{# </div>#}
</div>
{# <div class="col-lg-12 sibl">#}
{# <p>Похожие работы</p>#}
{# </div>#}
{# <div class="gallMini disTab">#}
{# {% for worksell in worksell_related %}#}
{# <div class="col-lg-3">#}
{##}
{# <div class="insetCol box-sizing disTab">#}
{# {% thumbnail worksell.get_cover "265x265" crop="center" as im %}#}
{# <div class="imgGal" style="background: url('{{ im.url }}') no-repeat center;">#}
{# <div class="imgFigure"></div>#}
{# </div>#}
{# {% endthumbnail %}#}
{# </div>#}
{# </div>#}
{# {% endfor %}#}
{# </div>#}
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="{% static 'js/swiper.min.js' %}"></script>
<script>
var galleryTop = new Swiper('.gallery-top', {
nextButton: '.next3',
prevButton: '.prev3',
spaceBetween: 10,
});
var galleryTop = new Swiper('.gallery-top', {
nextButton: '.next3',
prevButton: '.prev3',
spaceBetween: 15,
onSlideChangeEnd: function (swiper) {
var activeIndex = swiper.activeIndex;
$(galleryThumbs.slides).removeClass('is-selected');
$(galleryThumbs.slides).eq(activeIndex).addClass('is-selected');
galleryThumbs.slideTo(activeIndex, 300, false);
}
});
var galleryThumbs = new Swiper('.gallery-thumbs', {
spaceBetween: 15,
freeMode: true,
centeredSlides: false,
slidesPerView: 6,
touchRatio: 0.2,
nextButton: '.arr2',
prevButton: '.arr1',
onClick: function (swiper, event) {
var clicked = swiper.clickedIndex
swiper.activeIndex = clicked;
swiper.updateClasses()
$(swiper.slides).removeClass('is-selected');
$(swiper.clickedSlide).addClass('is-selected');
galleryTop.slideTo(clicked, 300, false);
}
});
</script>
{% endblock %}

@ -3,27 +3,28 @@
{% load humanize %}
{% load thumbnail %}
{% load specializtions_tags %}
{% load project_tags %}
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">{{ project.name }}</p>
</div>
<div class="doneBlock new-done disTab">
<div class="triangle1"></div>
{% if request.user.is_contractor %}
<div class="col-lg-12">
<p class="titleScore">{{ project.name }}</p>
</div>
<div class="doneBlock new-done disTab">
<div class="triangle1"></div>
{% if request.user.is_contractor %}
<div class="col-lg-12 new-p">
<p>{{ project.budget|intcomma }} <i class="fa fa-rub"></i></p>
</div>
{% endif %}
<div class="col-lg-4">
<a href="#" class="aLinkExe">
<div class="col-lg-4">
<a href="#" class="aLinkExe">
<div class="imgExecutor">
{% if project.customer.avatar %}
<img src="{{ project.customer.avatar.url }}" alt="execitor-image">
@ -31,13 +32,26 @@
<img src="{% static 'img/profile.jpg' %}" alt="execitor-image">
{% endif %}
</div>
</a>
<p class="nameExecutor">
<a href="{% url 'users:customer-profile-open-projects' project.customer.pk %}">{{ project.customer.get_full_name }} [{{ project.customer.username }}]</a>
</p>
<p class="navv2">На сайте {{ project.created|naturaltime }}</p>
</div>
</a>
<p class="nameExecutor">
<a href="{% url 'users:customer-profile-open-projects' project.customer.pk %}">{{ project.customer.get_full_name }} [{{ project.customer.username }}]</a>
</p>
<p class="navv2">На сайте {{ project.created|naturaltime }}</p>
{% if not request.user.is_contractor %}
<ul class="rettList restList3">
<li>
<a href="javascript:void(0)">Отзывы:
<span> + 0</span>
<small> 0</small>
<mark> - 0</mark>
</a>
</li>
</ul>
{% endif %}
</div>
{% if request.user.is_contractor %}
<div class="col-lg-3 retts">
<ul class="rettList restList2">
<li>Рейтинг: <span> 0</span></li>
@ -51,43 +65,48 @@
</li>
</ul>
</div>
<div class="col-lg-3 retts new-list">
<ul class="desListPro">
<li>{{ project.created }}</li>
<li>{{ project.get_work_type_display }}</li>
</ul>
{% endif %}
<div class="col-lg-3 retts new-list">
<ul class="desListPro">
<li>
{{ project.created }}
</li>
<li>
{{ project.get_work_type_display }}
</li>
</ul>
{% if project.cro %}
<div class="sroUser sroExecutor sroPro">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% endif %}
</div>
</div>
<div class="infoProjectBlock info-new disTab">
<div class="triangle2"></div>
{% if project.realty %}
</div>
</div>
<div class="infoProjectBlock info-new disTab">
<div class="triangle2"></div>
{% if project.realty %}
<div class="col-lg-10 col-lg-offset-1">
<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 %}
<div class="col-lg-10 col-lg-offset-1">
<p class="textProIn">
{% endif %}
<div class="col-lg-10 col-lg-offset-1">
<p class="textProIn">
{{ project.text }}
</p>
</div>
<div class="col-lg-10 col-lg-offset-1">
{% if request.user.is_contractor and not answer %}
</div>
<div class="col-lg-10 col-lg-offset-1">
{% if request.user.is_contractor and not answer %}
<a href="#" onclick="$('.-project-answer-form').toggle('slow'); return false" class="new-answer">
Ответить на проект
</a>
@ -109,7 +128,7 @@
</div>
</div>
{% endif %}
{% elif request.user.is_customer %}
{% elif request.user.is_customer and request.user == project.customer %}
<a href="{% url 'projects:customer-project-edit' pk=project.pk %}?back={{ request.path }}" class="linkProIn linkProIn1">
Редактировать
</a>
@ -123,8 +142,28 @@
</a>
</form>
{% endif %}
</div>
</div>
</div>
</div>
@ -137,12 +176,6 @@
{% if request.user.is_contractor %}
{% if answer %}
<div class="candidateBlock disTab">
<div class="candidate can-new">
@ -212,7 +245,7 @@
<p>Опубликован: {{ answer.created|date:'M d, Y' }}</p>
</div>
<div class="col-lg-3 retts">
<form action="{% url 'projects:contractor-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>
@ -262,7 +295,7 @@
</span>
<p class="textComm44">
{{ message.text }}
{{ message.text|linebreaksbr }}
</p>
</div>
</div>
@ -421,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">
@ -458,168 +506,438 @@
</div>
{% endif %}
<div class="col-lg-12 exNew">
<p>Новые исполнители</p>
</div>
{% for answer in project.answers.all %}
<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">
<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|get_new_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>
</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 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>
{% elif answer.author|class_name == 'Team' %}
<!-- TODO -->
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
<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>
{% 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 'projects:add-candidate' answer_id=answer.pk project_id=project.pk %}" class="candLink candLink1">
Кандидат
</a>
<form action="{% url 'projects:customer-offer-order' answer_id=answer.pk project_id=project.pk %}" method="POST" novalidate>
{% csrf_token %}
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink2">
предложить проект
</a>
</form>
<form action="{% url 'projects:customer-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 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 %}
{% if not project.order.contractor and not project.order.team %}
<form action="{% url 'projects:customer-offer-order' answer_id=answer.pk project_id=project.pk %}" method="POST" novalidate>
{% csrf_token %}
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink2">
предложить проект
</a>
</form>
{% endif %}
<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>
{% endfor %}
</div>
</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>
{% 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 -->
<span class="dateComm44">
{{ message.created }}
</span>
<p class="textComm44">
{{ message.text }}
</p>
</div>
<div class="sroUser sroExecutor">
<div class="iconSRO"></div>
<p>Есть допуск СРО</p>
</div>
{% endif %}
</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>
<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">
{% if not project.order.contractor and not project.order.team %}
<form action="{% url 'projects:customer-offer-order' answer_id=answer.pk project_id=project.pk %}" method="POST" novalidate>
{% csrf_token %}
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink2">
предложить проект
</a>
</form>
{% endif %}
<form action="{% url 'projects:reject-project-answer' 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>
<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>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% include 'partials/footer.html' %}
</div>
</div>

@ -5,11 +5,11 @@
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">Биржа проектов</p>
</div>
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">Биржа проектов</p>
</div>
<form action="{% url 'projects:project-filter' %}" method="GET" novalidate>
<div class="col-lg-12">
@ -50,7 +50,7 @@
</div>
<div class="searchF1">
<div class="searchF1 resSearchF1">
<div class="col-lg-6">
<input
type="text"
@ -167,7 +167,7 @@
<input type="checkbox" name="{{ form.reverse_order.html_name }}" {% if reverse_order %}checked{% endif %} style="display: none">
</div>
</div>
</form>
</form>
@ -230,11 +230,11 @@
<div class="col-lg-12 pagin">
{% include 'partials/pagination.html' %}
{% include 'partials/pagination.html' %}
</div>
{% include 'partials/footer.html' %}
</div>
</div>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}

@ -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,24 @@ 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,
PortfolioDetail,
)
app_name = 'projects'
@ -38,12 +38,12 @@ 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+)/?$', PortfolioDetail.as_view(), name='contractor-portfolio-detail'),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'),
urls.url(r'^candidate/add/(?P<answer_id>(\d+))/(?P<project_id>(\d+))/$', add_candidate, name='add-candidate'),
urls.url(r'^candidate/delete/(?P<pk>(\d+))/$', CandidateDeleteView.as_view(), name='delete-candidate'),
urls.url(r'^candidate/comparison/sort/$', sort_candidates, name='comparison-sort'),

@ -2,6 +2,7 @@ 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
from django.core.files.base import ContentFile
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse, reverse_lazy
@ -42,78 +43,78 @@ from .forms import (
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))
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.all()
project_answers = project.answers.filter(rejected=False)
contractor = request.user
answer = _.first(_.filter(project_answers, lambda a: a.author == contractor))
if not answer:
try: team = contractor.team
except Team.DoesNotExist: team = None
if team:
answer = _.first(_.filter(project_answers, lambda a: a.author == team))
context.update({'answer': answer})
if not answer:
try: contractor.team; context.update({'can_answer_as_team': True})
except Team.DoesNotExist: pass
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)
@ -123,7 +124,7 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
context.update({'form': form})
return render(request, self.template_name, context)
else:
@ -132,41 +133,41 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
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:
return HttpResponseForbidden('403 Forbidden')
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):
try: team = request.user.team
except Team.DoesNotExist: team = None
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:
@ -174,49 +175,30 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
redirect_to = request.POST.get('next')
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):
answer = get_object_or_404(request.user.contractor_answers, pk=kwargs.get('pk'))
answer.delete()
messages.info(request, 'Вы успешно отказались от проекта')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
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.rejected = True
answer.save()
messages.info(request, 'Успешный отказ от проекта')
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()
messages.info(request, 'Вы успешно отказали пользователю в проекте')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@ -225,107 +207,107 @@ 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
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, (
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'<p>Произошла ошибка (realty_form)</p>'
'<pre>{realty_form}</pre>'
).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,
@ -334,7 +316,7 @@ class ProjectFilterView(BaseMixin, View):
'page_obj': projects,
'display_msg': display_msg,
})
return render(request, self.template_name, context)
@ -342,33 +324,33 @@ 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
@ -378,12 +360,12 @@ class CustomerProjectCreateView(BaseMixin, View):
secure = False
if 'secure_deal' in project.deal_type:
secure = True
Order.objects.create(project=project, secure=secure)
for file in request.FILES.getlist('new_files'):
ProjectFile.objects.create(file=file, project=project)
if realty:
realty_form.save()
else:
@ -391,10 +373,10 @@ class CustomerProjectCreateView(BaseMixin, View):
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)
@ -404,13 +386,13 @@ class CustomerProjectCreateView(BaseMixin, View):
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'<p>Произошла ошибка (realty_form)</p>'
'<pre>{realty_form}</pre>'
).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)
@ -420,52 +402,52 @@ 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:
return HttpResponseForbidden('403 Forbidden')
raise PermissionDenied
def get(self, request, *args, **kwargs):
project = get_object_or_404(request.user.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.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:
@ -473,10 +455,10 @@ class CustomerProjectEditView(BaseMixin, View):
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)
@ -486,13 +468,13 @@ class CustomerProjectEditView(BaseMixin, View):
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
if realty_form and realty_form.errors:
messages.info(request, (
'<p>Произошла ошибка (realty_form)</p>'
'<pre>{realty_form}</pre>'
).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)
@ -528,41 +510,41 @@ class ContractorPortfolioTrashView(View):
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:
return HttpResponseForbidden('403 Forbidden')
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, 'Произошла ошибка: <pre>{msg}</pre>'.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:
return HttpResponseForbidden('403 Forbidden')
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'
@ -571,23 +553,23 @@ class CustomerProjectRestoreView(View):
messages.info(req, 'Проект восстановлен из корзины')
else:
messages.info(req, 'Произошла ошибка: <pre>{msg}</pre>'.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:
return HttpResponseForbidden('403 Forbidden')
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'
@ -595,7 +577,7 @@ class CustomerProjectDeleteView(View):
messages.info(req, 'Проект удалён навсегда')
else:
messages.info(req, 'Произошла ошибка: <pre>{msg}</pre>'.format(msg=pformat(form.errors)))
redirect_to = req.POST.get('next')
return redirect(redirect_to)
@ -672,7 +654,7 @@ class OfferOrderView(View):
return HttpResponseRedirect(redirect_url)
def contractor_portfolio_create(request):
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()))
@ -729,4 +711,11 @@ class PortfolioDelete(DeleteView):
model = Portfolio
success_url = reverse_lazy('users:contractor-profile')
class PortfolioDetail(DetailView):
model = Portfolio
template_name = 'portfolio_detail.html'
# import code; code.interact(local=dict(globals(), **locals()))

@ -1,12 +1,12 @@
<ul class="rettList {{ class_name }}">
<li>Рейтинг: <span> {{ ratings }}</span></li>
<li>Безопасные сделки: <span> 0</span></li>
<li>Безопасные сделки: <span> {{ deals }}</span></li> {# TODO #}
<li>
<a href="javascript:void(0)">
Отзывы:
<span> + 0</span>
<small> 0</small>
<mark> - 0</mark>
<span> + {{ reviews_p }}</span>
<small> {{ reviews_n }}</small>
<mark> - {{ reviews_m }}</mark>
</a>
</li>
</ul>

@ -1,38 +1,3 @@
{#<!-- Новая вёрстка ---------------------------------------------------->#}
{##}
{# <div class="dashedCol4 dashedCol44">#}
{# <p class="specUser">#}
{# Специализации:#}
{# </p>#}
{# <div class="insetSpec">#}
{# <span>Интерьеры</span>#}
{# <span>2-й</span>#}
{# </div>#}
{# <div class="insetSpec">#}
{# <span>Визуализация/3D</span>#}
{# <span>45-й</span>#}
{# </div>#}
{# <div class="insetSpec">#}
{# <span>Экстерьеры</span>#}
{# <span>10-й</span>#}
{# </div>#}
{# <div class="showSpec">#}
{# <div class="insetSpec">#}
{# <span>Архитектура</span>#}
{# <span>3-й</span>#}
{# </div>#}
{# <div class="insetSpec">#}
{# <span>3D Моделирование</span>#}
{# <span>100-й</span>#}
{# </div>#}
{# </div>#}
{# <button class="showPress">#}
{# #}
{# </button>#}
{# </div>#}
<div class="dashedCol4">
<p class="specUser">Специализации:</p>
{% for spec in specializations %}
@ -40,5 +5,7 @@
<span>{{ spec.specialization.name }}</span>
<span>{{ spec.position }}-й</span>
</div>
{% empty %}
Рейтинги скоро будут рассчитаны
{% endfor %}
</div>

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

@ -107,12 +107,12 @@
<a href="{% url 'wallets:score-detail' pk=request.user.pk %}">Счет<span></span></a>
</li>
<li class="icon_mm5">
<a href="#">Настройки<span></span></a>
<a href="{% url 'users:user-profile-edit' pk=request.user.pk %}">Настройки<span></span></a>
</li>
<li class="icon_mm6">
<a href="#">FAQ<span></span></a>
</li>
{# <li class="icon_mm6">#}
{# <a href="#">FAQ<span></span></a>#}
{# </li>#}
<li class="icon_mm6">
<a href="{% url 'auth_logout' %}">Выйти<span></span></a>

@ -4,8 +4,10 @@ source ../env/bin/activate &&
pip install -r requirements/base.txt &&
git reset --hard &&
git pull &&
chmod +x update.sh &&
./manage.py migrate --noinput &&
./manage.py collectstatic --noinput &&
supervisorctl restart arch &&
./manage.py recalculation_spec
supervisorctl restart arch_chat &&
./manage.py recalculation_spec &&
chown -R www-data:www-data ./ && chmod -R 775 ./ &&
chmod +x update.sh

@ -24,7 +24,7 @@
<div class="titleF1 disTab">
<div class="col-lg-3">Специализации</div>
</div>
<div class="polsF1 disTab">
<div class="polsF1 disTab -spec-select-container">
<div class="col-lg-3">
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
@ -38,34 +38,11 @@
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<input type="hidden" id="chosenSpecId" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}">
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-3">Местоположение</div>
<input type="hidden" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}" class="-chosen-spec-id">
</div>
<div class="searchF1 polsF1 polsFF polsF3">
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-country" style="width: 100%">
</div>
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-region" style="width: 100%">
</div>
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-city" style="width: 100%">
</div>
<input type="hidden" id="chosenLocationId" name="{{ form.location.html_name }}" value="{{ form.location.value }}">
<div class="col-lg-3">
<a href="#" class="findReal" onclick="$(this).closest('form').submit(); return false">
найти исполнителя
@ -103,6 +80,27 @@
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение</div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-country" style="width: 100%">
</div>
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-region" style="width: 100%">
</div>
<div class="col-lg-3">
<input type="hidden" class="-location-select -location-select-city" style="width: 100%">
</div>
<input type="hidden" id="chosenLocationId" name="{{ form.location.html_name }}" value="{{ form.location.value }}">
</div>
<div class="titleF1 disTab">
<div class="col-lg-3">Классификация здания</div>
<div class="col-lg-3">Тип работы</div>
@ -178,7 +176,7 @@
<img src="img/profile.jpg" alt="execitor-image">
</div></a>
<p class="nameExecutor">
<a href="#">{{ team.name }}</a>
<a href="">{{ team.name }}</a> {# TODO #}
</p>
<p class="navv2">На сайте 8 лет и 3 месяца</p>
<div class="statusUser">Свободен</div>

@ -91,7 +91,8 @@
<span>Программист</span>
</div>
{% empty %}
В группе пока нет участников
{% endfor %}
</div>
</div>
@ -163,7 +164,7 @@
</div>
</div>
<div class="insetCol2 box-sizing disTab">
<p>{{ p.name }}</p>
<a href="{% url 'projects:contractor-portfolio-detail' p.pk %}">{{ p }}</a>
<div class="buttonsImg" disTab>
<a href="{% url 'projects:contractor-portfolio-edit' p.pk %}">
<div class="insetBI insetBI1">
@ -176,15 +177,19 @@
</div>
</div>
</div>
{% empty %}
<div class="col-lg-12">
<p style="text-align: center;margin-top: 25px;">Работ в портфолио пока нет</p>
</div>
{% endfor %}
</div>
<div class="col-lg-9 col-lg-offset-3">
<div class="linkElse">
<a href="javascript:void(0)" class="showElse">показать еще</a>
</div>
</div>
{# TODO:#}
{# <div class="col-lg-9 col-lg-offset-3">#}
{# <div class="linkElse">#}
{# <a href="javascript:void(0)" class="showElse">показать еще</a>#}
{# </div>#}
{# </div>#}
</div>
<div id="tab12" class="tab-pane fade">
@ -204,42 +209,48 @@
</div>
</div>
<div class="insetCol2 box-sizing disTab">
<p>{{ ws }}</p>
<a href="{% url 'work_sell:detail' ws.pk %}">{{ ws }}</a>
</div>
</div>
{% empty %}
<div class="col-lg-12">
<p style="text-align: center;margin-top: 25px;">Готовых работ пока нет</p>
</div>
{% endfor %}
</div>
<div class="col-lg-9 col-lg-offset-3">
<div class="linkElse">
<a href="javascript:void(0)" class="showElse">показать еще</a>
</div>
</div>
{# TODO#}
{# <div class="col-lg-9 col-lg-offset-3">#}
{# <div class="linkElse">#}
{# <a href="javascript:void(0)" class="showElse">показать еще</a>#}
{# </div>#}
{# </div>#}
</div>
<div id="tab13" class="tab-pane fade">
{% for review in reviews %}
<div class="new-comm-44">
<div class="col-lg-12">
<p class="nameComm">
<a href="#">Иванов Петр Иванович</a>
<a href="">{{ review.get_sender }}</a>
</p>
<span class="dateComm44">
Безопасная сделка
</span>
{% if review.project.deal_type == 'secure_deal' %}
<span class="dateComm44">
Безопасная сделка
</span>
{% endif %}
<div class="stars box-sizing">
<span class="glyphicon glyphicon-star starAct" aria-hidden="true"></span>
<span class="glyphicon glyphicon-star starAct" aria-hidden="true"></span>
<span class="glyphicon glyphicon-star starAct" aria-hidden="true"></span>
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
<a href="#">положительный отзыв</a>
</div>
<p class="textComm44">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum
{{ review.text|safe }}
</p>
</div>
</div>
{% empty %}
<div class="new-comm-44">
<p style="text-align: center;">Отзывов пока нет</p>
</div>
{% endfor %}
</div>
</div>
</div>

@ -176,8 +176,7 @@
{% endthumbnail %}
</div>
<div class="insetCol2 box-sizing disTab">
<p>{{ p.name }}</p>
<a href="{% url 'projects:contractor-portfolio-detail' p.pk %}">{{ p }}</a>
{% if request.user.pk == contractor.pk %}
<div class="buttonsImg" disTab>
<a href="{% url 'projects:contractor-portfolio-edit' p.pk %}">
@ -196,7 +195,12 @@
{% endif %}
</div>
</div>
{% empty %}
<div class="col-lg-4">
<p style="text-align: center;">Работ в портфолио пока нет</p>
</div>
{% endfor %}
</div>
<div class="col-lg-9 col-lg-offset-3">
@ -247,8 +251,7 @@
</div>
</div>
<div class="insetCol2 box-sizing disTab">
<p>{{ ws }}</p>
<a href="{% url 'work_sell:detail' ws.pk %}">{{ ws }}</a>
{% if request.user.pk == contractor.pk %}
<div class="buttonsImg" disTab>
<div class="insetBI insetBI1">
@ -455,6 +458,10 @@
</p>
</div>
</div>
{% empty %}
<div class="new-comm-44">
<p style="text-align: center;">Отзывов пока нет</p>
</div>
{% endfor %}
</div>
</div>

@ -95,9 +95,9 @@
<div class="extAreaBlock2 polsF1 text-nn box-sizing disTab">
<p></p>
<div class="row">
<div class="col-lg-6 make-new">
<div class="make-new">
<label>{{ portfolio_form.duplicate }}<span></span></label>
<label style="margin-left: 0;">{{ portfolio_form.duplicate }}<span></span></label>
<p>Дублировать проект в готовые работы</p>
</div>

@ -43,56 +43,65 @@
value="{{ worksell_form.specialization.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<div class="textAreaBlock2 polsF1 text-nn box-sizing disTab">
<p>Бюджет{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<div class="col-lg-6">
<input type="text" class="box-sizing" name="{{ worksell_form.budget.html_name }}" value="{{ worksell_form.budget.value }}">
</div>
<div class="col-lg-4">
<div class="col-lg-3">
{{ worksell_form.currency}}
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<div class="textAreaBlock2 polsF1 text-nn box-sizing disTab">
<p>Срок выполнения{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<div class="col-lg-6">
<input type="text" class="box-sizing" name="{{ worksell_form.term.html_name }}" value="{{ worksell_form.term.value }}">
</div>
<div class="col-lg-4">
<div class="col-lg-3">
{{ worksell_form.term_type }}
</div>
</div>
</div>
<div class="polsF1 polsF2 disTab">
<p>Вид строительства</p>
<div class="textAreaBlock2 polsF1 text-nn box-sizing disTab">
<p>Вид строительства</p>
<div class="row">
<div class="col-lg-6">
{{ worksell_form.construction_type}}
</div>
</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="textAreaBlock2 polsF1 text-nn box-sizing disTab">
<p>Классификация здания</p>
<div class="row">
<div class="col-lg-6">
{{ worksell_form.building_classification}}
</div>
</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="polsF1 disTab">
<input type="hidden" name="{{ worksell_form.contractor.html_name }}" value="{{ request.user.pk }}" />
</div>
<div class="polsF1 polsF2 disTab">
<div class="polsF1 disTab">
<input type="hidden" name="images-ids" id="upload-files-worksell-pk" />
</div>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="glyphicon glyphicon-plus"></i>
<span>Выберите файлы</span>
<!-- The file input field used as target for the file upload widget -->
<input id="fileupload-worksell" type="file" name="file" multiple>
</span>
<div class="textAreaBlock2 polsF1 text-nn box-sizing disTab" style="margin-bottom: 12px;">
<span class="btn btn-success fileinput-button add_file_to_port">
<span>Выберите файлы</span>
<!-- The file input field used as target for the file upload widget -->
<input id="fileupload-worksell" type="file" name="file" multiple>
</span>
</div>
<br>
<br>
<!-- The global progress bar -->
@ -103,7 +112,7 @@
<div id="files-worksell" class="files"></div>
<br>
<div class="searchF1 polsF1 polsFF links-filter">
<div class="polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>

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

@ -1,27 +1,25 @@
from django.conf import urls, settings
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Sum
from django.http import HttpResponse, JsonResponse, HttpResponseForbidden
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import DetailView, CreateView
from django.views.generic.base import View
from pprint import pprint, pformat
import logging
from users.models import User
from .forms import WithDrawForm, TmpCheckOrderForm, TmpPaymentAvisoForm
from .models import InvoiceHistory, WithDraw, Transaction
from users.mixins import CheckForUserMixin
from users.models import User
class ScoreDetailView(DetailView):
model = User
template_name = 'score-detail.html'
context_object_name = 'user_score'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user_score_balance = InvoiceHistory.objects.filter(user=self.get_object()).aggregate(Sum('sum'))
@ -30,7 +28,7 @@ class ScoreDetailView(DetailView):
return context
class ScoreView(View):
class ScoreView(LoginRequiredMixin, View):
template_name = 'score-detail.html'
def get(self, request, *args, **kwargs):
@ -39,7 +37,7 @@ class ScoreView(View):
user_score = get_object_or_404(User.objects, pk=kwargs.get('pk'))
current_sum_info = InvoiceHistory.objects.filter(user=user_score).aggregate(Sum('sum'))
user_score_balance = current_sum_info['sum__sum'] or 0
return render(request, self.template_name, {
'transaction': transaction[0],
'YANDEX_MONEY': settings.YANDEX_MONEY,
@ -96,23 +94,22 @@ class WithDrawCreate(CreateView):
return super().form_invalid(form)
# Yandex Money ------------------------------------------------
class TmpCheckOrderView(View):
form_class = TmpCheckOrderForm
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
# trans = form.cleaned_data.get('transaction')
if form.is_valid():
res = """<?xml version="1.0" encoding="utf-8"?>
<checkOrderResponse
@ -125,7 +122,7 @@ class TmpCheckOrderView(View):
invoice_id=form.cleaned_data.get('invoiceId'),
shop_id=form.cleaned_data.get('shopId'),
)
return HttpResponse(res, content_type='application/xml')
else:
res = """<?xml version="1.0" encoding="utf-8"?>
@ -141,21 +138,21 @@ class TmpCheckOrderView(View):
invoice_id=form.cleaned_data.get('invoiceId'),
shop_id=form.cleaned_data.get('shopId'),
)
return HttpResponse(res, content_type='application/xml')
# return HttpResponse('<pre>{msg}</pre>'.format(msg=pformat(form.errors))) # Debug
class TmpPaymentAvisoView(View):
form_class = TmpPaymentAvisoForm
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
transaction = form.cleaned_data.get('transaction_id')
transaction.complete = True
@ -173,7 +170,7 @@ class TmpPaymentAvisoView(View):
invoice_id=form.cleaned_data.get('invoiceId'),
shop_id=form.cleaned_data.get('shopId'),
)
return HttpResponse(res, content_type='application/xml')
else:
res = """<?xml version="1.0" encoding="utf-8"?>
@ -183,6 +180,6 @@ class TmpPaymentAvisoView(View):
message="Payment aviso, validation error"
techMessage="Payment aviso, validation error"/>
""".format(date=timezone.now().isoformat())
return HttpResponse(res, content_type='application/xml')
# return HttpResponse('<pre>{msg}</pre>'.format(msg=pformat(form.errors))) # Debug

@ -48,20 +48,21 @@
<div class="desReadyBlock">
{% if object.text %}
<div class="col-lg-10 col-lg-offset-1">
<p class="titleReady">Описание:</p>
</div>
<div class="col-lg-10 col-lg-offset-1">
<p class="textReady">
{{ object.text }}
</p>
</p>
</div>
{% endif %}
<div class="col-lg-12 sliderReady">
<div class="arroww prev3"></div>
<div class="arroww next3"></div>
<div class="swiper-container gallery-top">
<div class="swiper-wrapper">
шь
<div class="swiper-slide" style="background-image:url('{% static object.get_cover.url %}')"></div>
{% for photo in object.photos.all %}
<div class="swiper-slide" style="background-image:url('{% static photo.img.url %}')"></div>
@ -77,11 +78,11 @@
</div>
</div>
</div>
<div class="col-lg-12">
<div class="triangle2"></div>
<div class="arr arr1"></div>
<div class="arr arr2"></div>
</div>
{# <div class="col-lg-12">#}
{# <div class="triangle2"></div>#}
{# <div class="arr arr1"></div>#}
{# <div class="arr arr2"></div>#}
{# </div>#}
</div>
<div class="col-lg-12 sibl">
<p>Похожие работы</p>

@ -140,7 +140,7 @@
{% for work in work_sells %}
<div class="col-lg-3">
<div class="insetCol box-sizing disTab">
<a href="{% url 'work_sell:detail' work.pk %}">
<a href="{% url 'work_sell:detail' work.pk %}">
{% thumbnail work.get_cover "265x265" crop="center" as im %}
<div class="imgGal" style="background: url('{{ im.url }}') no-repeat center;">
<div class="imgFigure"></div>
@ -154,8 +154,7 @@
</div>
</div>
<div class="insetCol2 box-sizing disTab">
<p>{{ work }}</p>
<a href="{% url 'work_sell:detail' work.pk %}">{{ work }}</a>
{% if request.user.pk == work.contractor.pk %}
<div class="buttonsImg" disTab>
<a href="{% url 'work_sell:edit' work.pk %}">

Loading…
Cancel
Save